Compare commits
282 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1b12173d5 | |||
| f3bf13da59 | |||
| f236b75f55 | |||
| 2434231f9b | |||
| b8b39e69a1 | |||
| f8158dfd8e | |||
| f1ab7df5ce | |||
| 986de49052 | |||
| d09ca5548a | |||
| 77e22c40b6 | |||
| de74647c43 | |||
| 498601c21a | |||
| e99f06ddf5 | |||
| f06af59168 | |||
| d50e3a7fc0 | |||
| 3e4e7ff1ab | |||
| 21ff3c8655 | |||
| 166e61bab4 | |||
| 93cd8bd8da | |||
| f5c1641af2 | |||
| 62ffed0624 | |||
| 8d0a4a7417 | |||
| b1b274ea12 | |||
| fc38865965 | |||
| 911adaaecb | |||
| 7626ed8d1d | |||
| 6135fd7e1b | |||
| 14bea36356 | |||
| 4abc163a59 | |||
| 5bb9cf1e7c | |||
| a0cdf408a1 | |||
| 8467506915 | |||
| 704255774d | |||
| 10bb7b1cca | |||
| 7f82af3d3b | |||
| d7c729c035 | |||
| 51fa904f9b | |||
| 6191e55745 | |||
| c6e93c266a | |||
| bcf7e5b5ab | |||
| 466f122cc4 | |||
| 539484ea25 | |||
| db1cee25bf | |||
| 96502b18d0 | |||
| c491ece8c5 | |||
| c76464bf8a | |||
| 08b75b1a79 | |||
| 90da869277 | |||
| 80c4affaa8 | |||
| aa087193fe | |||
| af7e51f69b | |||
| 1b62d8ddc2 | |||
| 0839579733 | |||
| 7f9bf08f76 | |||
| 79ec1c6922 | |||
| c68246df83 | |||
| abc0ad7120 | |||
| 4356ca1cff | |||
| d6215d009c | |||
| 0b501e95a5 | |||
| f157ff37e0 | |||
| 11d425faa9 | |||
| 5fc68a1926 | |||
| ec4577c187 | |||
| 8052de8ad5 | |||
| 2fda959cd6 | |||
| 05fa8a5ae2 | |||
| a341b41e9a | |||
| a2e0a4d78a | |||
| 4fb1d6077c | |||
| 53ab927544 | |||
| 897cc2daf2 | |||
| 73ff31daf5 | |||
| c387a9e17e | |||
| 1f92931076 | |||
| d3105a1532 | |||
| 7f50f31837 | |||
| 4c3e90ea4b | |||
| 8d80fc4aa9 | |||
| 78a04ffcf1 | |||
| bcb2c91823 | |||
| 9058f209dd | |||
| 1f172d583e | |||
| 439b4757df | |||
| 9f8809bd51 | |||
| 4e326650cc | |||
| e27be27692 | |||
| 88eb2a1897 | |||
| 8c84ef73c8 | |||
| 859e49f16c | |||
| 14ba8b0a23 | |||
| d7c70f75f3 | |||
| 2760e3ea08 | |||
| f2f041fe55 | |||
| 9ec9700ed4 | |||
| c6b047fc02 | |||
| 6727264e39 | |||
| d1a5fe234d | |||
| e84c7cd276 | |||
| 087813fab9 | |||
| 3a4e7398c6 | |||
| 1630fd7e71 | |||
| 4aa99beaff | |||
| eaa0ba28d8 | |||
| 3f3e1120b2 | |||
| 89d7a2c740 | |||
| 56c31f0204 | |||
| 338cfb5be2 | |||
| 43f6fe664b | |||
| 209c9fb7f7 | |||
| a562f67e8d | |||
| ef0fe5a425 | |||
| b50902d23e | |||
| 55490579ec | |||
| 957d08701f | |||
| 61e1aed35d | |||
| 2034f0ef5f | |||
| fcb92c7f0b | |||
| 28ea1ca1df | |||
| ebc1a458a9 | |||
| df92ef5296 | |||
| 253e0a8378 | |||
| a9fbe4eddd | |||
| cfccc638c6 | |||
| 9f1460cc26 | |||
| f0a4541420 | |||
| d0ce482680 | |||
| 40e2734a4e | |||
| 8552796343 | |||
| 6d72ffb876 | |||
| 65faaf0183 | |||
| 7f7c2e8d6f | |||
| 3be491baa7 | |||
| 48b974ae58 | |||
| c394898fcd | |||
| f22249f3e9 | |||
| 120cec52e9 | |||
| 535cc6b3d9 | |||
| 18a060e2bf | |||
| 7b8165cee6 | |||
| 6331eb64e6 | |||
| 3c38b551f0 | |||
| 8e761347f2 | |||
| e2d44e0e6a | |||
| 607da92c71 | |||
| ae0427604e | |||
| 2aa981c581 | |||
| 5d8ec82502 | |||
| fc9bb52c41 | |||
| 3224e4138f | |||
| 7ea28c4fd3 | |||
| acb5567f6a | |||
| cc877c7c11 | |||
| 87bdddf7cf | |||
| d939b64d64 | |||
| 3f64f71947 | |||
| aaae7db24d | |||
| 48c3934b6e | |||
| 258b4af0b1 | |||
| edfbd03f13 | |||
| 9cb0f62da7 | |||
| 9591960e31 | |||
| 132d8ebe8f | |||
| 4020073aca | |||
| 5025692220 | |||
| d04a4c4103 | |||
| 7e6dea68f4 | |||
| a046833414 | |||
| cfa59ca8bf | |||
| d470bd58f2 | |||
| ea82d2b1df | |||
| 93b44088a9 | |||
| 625c091fdc | |||
| e0c0447fb6 | |||
| b67a8e8537 | |||
| c9f1b02954 | |||
| 6b3bfca1f6 | |||
| 15e568594d | |||
| 089f41ee89 | |||
| 636c68e532 | |||
| 6d8a04dfb2 | |||
| 01d5aad8aa | |||
| 002733cd99 | |||
| d7a086dfb1 | |||
| d60c7dcd9b | |||
| 58baf8aa82 | |||
| 0a8504d9f2 | |||
| 934a83c0e2 | |||
| ea65ad042f | |||
| 3544cd2943 | |||
| e593707ce7 | |||
| 6dd0fc7410 | |||
| faf7bf29e2 | |||
| c5b5f6aefe | |||
| 8e48eec01d | |||
| e3c5cc882f | |||
| dfb1bebc6b | |||
| a40367f813 | |||
| 1a1b62698c | |||
| b36dc0b897 | |||
| a9eacf21f4 | |||
| 23d84b25be | |||
| 6c7cc62371 | |||
| 588326cc57 | |||
| ce654bf7a2 | |||
| a3bcb76105 | |||
| 4045ebc24e | |||
| 2be3710462 | |||
| fe3185c6dc | |||
| f10b813e1e | |||
| c67d4199a0 | |||
| d0a6c59c62 | |||
| 60f622542f | |||
| e7fcd64b3a | |||
| 3772e7307e | |||
| e853db164d | |||
| 509283adab | |||
| db56b7f2dc | |||
| acd663d79f | |||
| 3636268a71 | |||
| 6402f42d01 | |||
| 71ac19cbb9 | |||
| d99389a398 | |||
| 024c386af4 | |||
| d982c9c24b | |||
| 480973318d | |||
| a497937106 | |||
| 0d33150f30 | |||
| 94667b1454 | |||
| 2cbcd8ad70 | |||
| 9f40a55907 | |||
| 51e01ec142 | |||
| 63fc2b9855 | |||
| edd8a02423 | |||
| a0a81b514c | |||
| 92820804f8 | |||
| e7ea7ed1c4 | |||
| 7471c90f49 | |||
| f545a9c469 | |||
| 30073c226d | |||
| 1b1e91b6c0 | |||
| 71d7ccf27d | |||
| 3ca24ec87f | |||
| b2940d9fa8 | |||
| a7b7f0eac2 | |||
| 4859916edd | |||
| ad3e19e965 | |||
| 03dead1083 | |||
| 341fba6ca9 | |||
| 58c4890122 | |||
| af42e5ef21 | |||
| 5abff0384c | |||
| cc915560b8 | |||
| 9835a4c0d1 | |||
| 90782ae710 | |||
| 75c790dd6e | |||
| 8604858a61 | |||
| d1b6162d3a | |||
| a445644b47 | |||
| ebb4434506 | |||
| 36a53662a0 | |||
| 569d95dc9c | |||
| bc4c4e03f0 | |||
| 170c5ea098 | |||
| 3fd5f166e2 | |||
| a2285a78a1 | |||
| 1ae7f72de3 | |||
| 19782a2dbe | |||
| b755e658f5 | |||
| cc0279b964 | |||
| 099da549b5 | |||
| 92a76032eb | |||
| cd17eebf96 | |||
| 1726312d34 | |||
| 444d1479b0 | |||
| 2949ea4dfa | |||
| 8ab292b2f9 | |||
| 9d8bfd9826 | |||
| 8a6685a6d0 | |||
| 78990d3c08 | |||
| 227662489b | |||
| 5ce410f233 |
@@ -0,0 +1,7 @@
|
|||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
max_line_length = 80
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"reportUnusedDisableDirectives": true,
|
|
||||||
"ignorePatterns": ["**/build", "**/dist"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"plugins": ["@typescript-eslint", "import"],
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:@typescript-eslint/stylistic",
|
|
||||||
"plugin:import/recommended",
|
|
||||||
"plugin:import/typescript",
|
|
||||||
"prettier",
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es2020": true,
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"tsconfigRootDir": ".",
|
|
||||||
"project": true,
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaVersion": 2020,
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"import/parsers": {
|
|
||||||
"@typescript-eslint/parser": [".ts", ".tsx"],
|
|
||||||
},
|
|
||||||
"import/resolver": {
|
|
||||||
"typescript": true,
|
|
||||||
},
|
|
||||||
"react": {
|
|
||||||
"version": "detect",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/array-type": "error",
|
|
||||||
"@typescript-eslint/ban-types": "error",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "error",
|
|
||||||
"@typescript-eslint/consistent-type-definitions": "error",
|
|
||||||
"@typescript-eslint/consistent-type-imports": [
|
|
||||||
"error",
|
|
||||||
{ "prefer": "type-imports" },
|
|
||||||
],
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "error",
|
|
||||||
"@typescript-eslint/method-signature-style": ["error", "property"],
|
|
||||||
"@typescript-eslint/naming-convention": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"selector": "typeParameter",
|
|
||||||
"format": ["PascalCase"],
|
|
||||||
"leadingUnderscore": "forbid",
|
|
||||||
"trailingUnderscore": "forbid",
|
|
||||||
"custom": {
|
|
||||||
"regex": "^(T|T[A-Z][A-Za-z]+)$",
|
|
||||||
"match": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-empty-function": "error",
|
|
||||||
"@typescript-eslint/no-empty-interface": "error",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-condition": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": "error",
|
|
||||||
"@typescript-eslint/no-inferrable-types": [
|
|
||||||
"error",
|
|
||||||
{ "ignoreParameters": true },
|
|
||||||
],
|
|
||||||
"import/default": "error",
|
|
||||||
"import/export": "error",
|
|
||||||
"import/namespace": "error",
|
|
||||||
"import/newline-after-import": "error",
|
|
||||||
"import/no-cycle": "error",
|
|
||||||
"import/no-duplicates": "off",
|
|
||||||
"import/no-named-as-default-member": "error",
|
|
||||||
"import/no-unused-modules": "error",
|
|
||||||
"import/order": [
|
|
||||||
"off",
|
|
||||||
{
|
|
||||||
"groups": [
|
|
||||||
"builtin",
|
|
||||||
"external",
|
|
||||||
"internal",
|
|
||||||
"parent",
|
|
||||||
"sibling",
|
|
||||||
"index",
|
|
||||||
"object",
|
|
||||||
"type",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-case-declarations": "error",
|
|
||||||
"no-empty": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-redeclare": "error",
|
|
||||||
"no-shadow": "error",
|
|
||||||
"no-undef": "off",
|
|
||||||
"sort-imports": "off",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1 +1,10 @@
|
|||||||
examples/** linguist-documentation
|
examples/** linguist-documentation
|
||||||
|
*.css linguist-detectable=false
|
||||||
|
*.scss linguist-detectable=false
|
||||||
|
*.js linguist-detectable=false
|
||||||
|
*.jsx linguist-detectable=true
|
||||||
|
*.tsx linguist-detectable=true
|
||||||
|
*.ts linguist-detectable=true
|
||||||
|
*.rs linguist-detectable=true
|
||||||
|
*.html linguist-detectable=false
|
||||||
|
*.sh linguist-detectable=false
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Bug report
|
||||||
|
description: File a bug report
|
||||||
|
title: '[Bug]: '
|
||||||
|
labels: ['bug']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behaviour
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: How to reproduce
|
||||||
|
description: Steps to reproduce the behavior (code, example repository)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: screenshots
|
||||||
|
attributes:
|
||||||
|
label: Screenshots
|
||||||
|
description: If applicable, add screenshots to help explain your problem.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: system-info
|
||||||
|
attributes:
|
||||||
|
label: System Info
|
||||||
|
description: Output of `npx envinfo --system --npmPackages 'tuono' --binaries --browsers`
|
||||||
|
render: shell
|
||||||
|
placeholder: System, Binaries, Browsers
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: system-info-rust
|
||||||
|
attributes:
|
||||||
|
label: System info (Rust)
|
||||||
|
description: Output of `rustc --version && cargo --version && tuono --version`
|
||||||
|
render: shell
|
||||||
|
placeholder: 'rustc, cargo and tuono binary version'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the bug here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest an idea for this project
|
||||||
|
title: '[Feature request]: '
|
||||||
|
labels: ['enhancement']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Is your feature request related to a problem? Please describe.
|
||||||
|
description: A clear and concise description of what the problem is.
|
||||||
|
placeholder: I'm always frustrated when [...]
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Describe the solution you'd like
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Describe alternatives you've considered
|
||||||
|
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context or screenshots about the feature request here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: Install NodeJS Dependencies
|
||||||
|
description: This is a composite GitHub Action that sets up pnpm, node and installs the project's dependencies.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
node-version:
|
||||||
|
description: 'Explicit node version. Otherwise fallback reading `.nvmrc`. Use in conjunction with matrix'
|
||||||
|
required: false
|
||||||
|
registry-url:
|
||||||
|
description: 'https://github.com/actions/setup-node?tab=readme-ov-file#usage'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js (via input)
|
||||||
|
if: ${{ inputs.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ inputs.node-version }}
|
||||||
|
registry-url: ${{ inputs.registry-url }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Setup Node.js (via .nvmrc)
|
||||||
|
if: ${{ !inputs.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
registry-url: ${{ inputs.registry-url }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
shell: bash
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
## Description
|
## Context & Description
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Thank you for your Pull Request. Please provide a description above and review
|
Thank you for your Pull Request.
|
||||||
the requirements below.
|
|
||||||
|
Explain the context and why you're making that change. What is the problem
|
||||||
|
you're trying to solve? If a new feature is being added, describe the intended
|
||||||
|
use case that feature fulfills.
|
||||||
|
|
||||||
Bug fixes and new features should include tests.
|
Bug fixes and new features should include tests.
|
||||||
|
|
||||||
Contributors guide: https://github.com/Valerioageno/tuono/blob/main/CONTRIBUTING.md
|
Contributors guide: https://github.com/Valerioageno/tuono/blob/main/CONTRIBUTING.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Explain the context and why you're making that change. What is the problem
|
|
||||||
you're trying to solve? If a new feature is being added, describe the intended
|
|
||||||
use case that feature fulfills.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Summarize the solution and provide any necessary context needed to understand
|
|
||||||
the code change.
|
|
||||||
-->
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
name: Documentation Website CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
paths:
|
||||||
|
# ⬇ Keep paths in sync with `typescript.yml`
|
||||||
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
- '!Cargo.toml'
|
||||||
|
# ⬆ Keep paths in sync with `typescript.yml`
|
||||||
|
- 'apps/documentation/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
# ⬇ Keep paths in sync with `typescript.yml`
|
||||||
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
- '!Cargo.toml'
|
||||||
|
# ⬆ Keep paths in sync with `typescript.yml`
|
||||||
|
- 'apps/documentation/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Check build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install NodeJS Dependencies
|
||||||
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
|
- name: Setup rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
|
||||||
|
- name: Install tuono
|
||||||
|
run: cargo install tuono@0.16.4
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
working-directory: ./apps/documentation
|
||||||
|
run: tuono build --static
|
||||||
|
|
||||||
|
code_check:
|
||||||
|
name: Check format, lint and types
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install NodeJS Dependencies
|
||||||
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: pnpm docs:format:check
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: pnpm docs:lint
|
||||||
|
|
||||||
|
- name: Types
|
||||||
|
run: pnpm docs:types
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
name: Deploy documentation website on AWS S3
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
paths:
|
||||||
|
- '.github/**'
|
||||||
|
- 'apps/documentation/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-documentation:
|
||||||
|
name: Deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install NodeJS Dependencies
|
||||||
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
|
- name: Setup rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
|
||||||
|
- name: Install tuono
|
||||||
|
run: cargo install tuono@0.16.4
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
working-directory: ./apps/documentation
|
||||||
|
run: tuono build --static
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: reggionick/s3-deploy@v4
|
||||||
|
with:
|
||||||
|
folder: apps/documentation/out/static
|
||||||
|
bucket: tuono-documentation
|
||||||
|
bucket-region: eu-west-3
|
||||||
|
dist-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
|
||||||
|
invalidation: /
|
||||||
|
delete-removed: true
|
||||||
|
no-cache: true
|
||||||
|
private: true
|
||||||
|
files-to-include: '{.*/**,**}'
|
||||||
@@ -1,78 +1,68 @@
|
|||||||
name: Publish on npm and crates
|
name: Publish on npm and crates
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-on-npm-dry-run:
|
publish_npm_dry_run:
|
||||||
|
name: NPM publish (dry run)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install NodeJS Dependencies
|
||||||
uses: actions/setup-node@v4
|
uses: ./.github/actions/install-node-dependencies
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
name: Install pnpm
|
|
||||||
with:
|
|
||||||
version: 8
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: pnpm publish -r --no-git-checks --dry-run
|
run: pnpm publish -r --no-git-checks --dry-run
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
publish-on-crates-dry-run:
|
publish_crates_dry_run:
|
||||||
|
name: Crates publish (dry run)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout code
|
||||||
- uses: actions-rs/toolchain@v1
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
toolchain: stable
|
- name: Setup rust toolchain
|
||||||
override: true
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
- uses: katyo/publish-crates@v2
|
with:
|
||||||
with:
|
toolchain: 'stable'
|
||||||
|
|
||||||
|
- uses: katyo/publish-crates@v2
|
||||||
|
with:
|
||||||
path: './crates/tuono'
|
path: './crates/tuono'
|
||||||
dry-run: true
|
dry-run: true
|
||||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
check-repo: true
|
check-repo: true
|
||||||
|
|
||||||
publish-on-npm:
|
publish_npm:
|
||||||
needs: [publish-on-crates-dry-run, publish-on-npm-dry-run]
|
name: NPM publish
|
||||||
|
needs: [publish_npm_dry_run, publish_crates_dry_run]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install NodeJS Dependencies
|
||||||
uses: actions/setup-node@v4
|
uses: ./.github/actions/install-node-dependencies
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
name: Install pnpm
|
|
||||||
with:
|
|
||||||
version: 8
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Clone root README.md on tuono package
|
- name: Clone root README.md on tuono package
|
||||||
run: cp README.md packages/tuono/README.md
|
run: cp README.md packages/tuono/README.md
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
@@ -81,17 +71,22 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
publish-on-crates:
|
publish_crates:
|
||||||
needs: [publish-on-crates-dry-run, publish-on-npm-dry-run]
|
name: Crates publish
|
||||||
|
needs: [publish_npm_dry_run, publish_crates_dry_run]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout code
|
||||||
- uses: actions-rs/toolchain@v1
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
toolchain: stable
|
- name: Setup rust toolchain
|
||||||
override: true
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
- uses: katyo/publish-crates@v2
|
with:
|
||||||
with:
|
toolchain: 'stable'
|
||||||
|
|
||||||
|
- uses: katyo/publish-crates@v2
|
||||||
|
with:
|
||||||
path: './crates/tuono'
|
path: './crates/tuono'
|
||||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
check-repo: true
|
check-repo: true
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
name: Repo root CI
|
||||||
|
|
||||||
|
# All workflows are triggered based on specific paths,
|
||||||
|
# the root repository is something that is not applicable to rust nor typescript
|
||||||
|
# so it has been moved in a standalone workflow
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
paths:
|
||||||
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
name: Check format
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install NodeJS Dependencies
|
||||||
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
|
- name: Test project
|
||||||
|
run: pnpm repo:root:format:check
|
||||||
@@ -2,39 +2,67 @@ name: Rust CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
paths:
|
paths:
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
|
- 'Cargo.toml'
|
||||||
- 'crates/**'
|
- 'crates/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
|
- 'Cargo.toml'
|
||||||
- 'crates/**'
|
- 'crates/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint_and_fmt:
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- run: cargo fmt --all -- --check
|
|
||||||
- run: cargo clippy -- -D warnings
|
|
||||||
|
|
||||||
build_and_test:
|
build_and_test:
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
name: Build and test crates on ${{ matrix.os }} with rust ${{ matrix.toolchain }}
|
||||||
name: Build and test rust crates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
|
os:
|
||||||
|
- 'ubuntu-latest'
|
||||||
|
- 'macos-latest'
|
||||||
|
- 'windows-latest'
|
||||||
toolchain:
|
toolchain:
|
||||||
- stable
|
- 'stable'
|
||||||
- beta
|
|
||||||
- nightly
|
# Test `beta` and `nightly` toolchains only on `ubuntu`
|
||||||
|
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#expanding-or-adding-matrix-configurations
|
||||||
|
include:
|
||||||
|
- toolchain: 'beta'
|
||||||
|
os: 'ubuntu-latest'
|
||||||
|
- toolchain: 'nightly'
|
||||||
|
os: 'ubuntu-latest'
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout code
|
||||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup rust toolchain
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
|
||||||
- run: cargo build --verbose
|
- run: cargo build --verbose
|
||||||
- run: cargo test --verbose
|
- run: cargo test --verbose
|
||||||
|
|
||||||
|
lint_and_fmt:
|
||||||
|
name: Check format and lint rust crates
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup rust toolchain
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
- run: cargo clippy -- -D warnings
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
name: Spell Checker
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
spell_checking:
|
||||||
|
name: LanguageTool
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
# Keep this value in order to show the error comments
|
||||||
|
# also on edited files.
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
# If the workflow is triggered by a push use check reporter
|
||||||
|
#
|
||||||
|
# If the workflow is triggered by a PR coming from the
|
||||||
|
# same repository, the author has write access so we can
|
||||||
|
# use pr-review reporter since the GITHUB_TOKEN will have review
|
||||||
|
# write permissions
|
||||||
|
#
|
||||||
|
# Otherwise use annotation reporter that do not require any
|
||||||
|
# particular permission
|
||||||
|
#
|
||||||
|
# Why this?
|
||||||
|
# https://github.com/Valerioageno/tuono/pull/137#pullrequestreview-2455455272
|
||||||
|
- name: Set reporter
|
||||||
|
id: reporter
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
FROM_MAIN_REPO: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
|
||||||
|
run: |
|
||||||
|
echo "EVENT_NAME: $EVENT_NAME FROM_MAIN_REPO: $FROM_MAIN_REPO"
|
||||||
|
if [ "$EVENT_NAME" == 'push' ]; then
|
||||||
|
echo "name=github-check" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$FROM_MAIN_REPO" == 'true' ]; then
|
||||||
|
echo "name=github-pr-review" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "name=github-pr-annotations" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Review dog languagetool
|
||||||
|
uses: reviewdog/action-languagetool@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review].
|
||||||
|
reporter: ${{ steps.reporter.outputs.name }}
|
||||||
|
# Change reporter level if you need.
|
||||||
|
level: 'warning'
|
||||||
|
patterns: '**/*.md **/*.txt **/*.mdx'
|
||||||
|
# enabled_categories: "STYLE,PLAIN_ENGLISH,WHITESPACE_RULE,EN_QUOTES,DASH_RULE,WORD_CONTAINS_UNDERSCORE,UPPERCASE_SENTENCE_START,ARROWS,COMMA_PARENTHESIS_WHITESPACE,UNLIKELY_OPENING_PUNCTUATION,SENTENCE_WHITESPACE,EN_UNPAIRED_BRACKETS,PHRASE_REPETITION,PUNCTUATION_PARAGRAPH_END"
|
||||||
|
disabled_categories: ''
|
||||||
|
disabled_rules: ''
|
||||||
@@ -2,65 +2,69 @@ name: Typescript CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
paths:
|
paths:
|
||||||
|
# ⬇ Keep paths in sync with `ci-documentation.yml`
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
- '!Cargo.toml'
|
||||||
|
# ⬆ Keep paths in sync with `ci-documentation.yml`
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
|
# ⬇ Keep paths in sync with `ci-documentation.yml`
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
|
- '*'
|
||||||
|
- '!Cargo.toml'
|
||||||
|
# ⬆ Keep paths in sync with `ci-documentation.yml`
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
build_and_test:
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
name: Check build and test
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Setup Node.js environment
|
strategy:
|
||||||
uses: actions/setup-node@v4
|
fail-fast: true
|
||||||
with:
|
matrix:
|
||||||
node-version: 20
|
os:
|
||||||
|
- 'ubuntu-latest'
|
||||||
|
- 'macos-latest'
|
||||||
|
- 'windows-latest'
|
||||||
|
|
||||||
- name: Install pnpm
|
runs-on: ${{ matrix.os }}
|
||||||
run: npm i -g pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
run: pnpm build
|
|
||||||
|
|
||||||
- name: Test project
|
steps:
|
||||||
run: pnpm test
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
fmt-lint-and-types:
|
- name: Install NodeJS Dependencies
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Test project
|
||||||
|
run: pnpm test
|
||||||
|
|
||||||
|
lint_and_fmt:
|
||||||
|
name: Check format and lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
run: npm i -g pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Check formatting
|
- name: Install NodeJS Dependencies
|
||||||
run: pnpm format:check
|
uses: ./.github/actions/install-node-dependencies
|
||||||
|
|
||||||
- name: Lint
|
- name: Check formatting
|
||||||
run: pnpm lint
|
run: pnpm format:check
|
||||||
|
|
||||||
- name: Types
|
- name: Lint
|
||||||
run: pnpm types
|
run: pnpm lint
|
||||||
|
|
||||||
|
- name: Types
|
||||||
|
run: pnpm types
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
|
## Node related ignores
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
.turbo/
|
.turbo/
|
||||||
|
|
||||||
*/**/routeTree.gen.ts
|
*/**/routeTree.gen.ts
|
||||||
|
|
||||||
pnpm-lock.yaml
|
examples/*/pnpm-lock.yaml
|
||||||
|
|
||||||
examples/playground/
|
vite.config.ts.timestamp*
|
||||||
|
|
||||||
## Rust related ignores
|
## Rust related ignores
|
||||||
|
|
||||||
@@ -24,3 +26,6 @@ examples/playground/
|
|||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
|
# Files, generated by the code editors
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
dist
|
||||||
|
.tuono
|
||||||
|
|
||||||
|
packages/tuono-lazy-fn-vite-plugin/tests/sources/*
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all"
|
"trailingComma": "all"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
* @Valerioageno @marcalexiei
|
||||||
|
|
||||||
|
# Rust
|
||||||
|
/crates/ @Valerioageno
|
||||||
|
/Cargo.toml @Valerioageno
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
/.github/ @marcalexiei
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Our Pledge
|
## Our Pledge
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
We, as members, contributors, and leaders, pledge to make participation in our
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
|||||||
Examples of behavior that contributes to a positive environment for our
|
Examples of behavior that contributes to a positive environment for our
|
||||||
community include:
|
community include:
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
- Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
- Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
and learning from the experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
- Focusing on what is best, not just for us as individuals, but for the
|
||||||
overall community
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
- The use of sexualized language or imagery, and sexual attention or
|
||||||
advances of any kind
|
Advances of any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
- Publishing others' private information, such as a physical or email
|
||||||
address, without their explicit permission
|
address, without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
- Other conduct that could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
@@ -45,13 +45,13 @@ or harmful.
|
|||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
not aligned with this Code of Conduct, and will communicate reasons for moderation
|
||||||
decisions when appropriate.
|
decisions when appropriate.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
an individual is officially representing the community in public spaces.
|
An individual is officially representing the community in public spaces.
|
||||||
Examples of representing our community include using an official e-mail address,
|
Examples of representing our community include using an official e-mail address,
|
||||||
posting via an official social media account, or acting as an appointed
|
posting via an official social media account, or acting as an appointed
|
||||||
representative at an online or offline event.
|
representative at an online or offline event.
|
||||||
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
|||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||

|

|
||||||

|

|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
# How to contribute to Tuono
|
# How to Contribute to Tuono
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
## Contributions
|
|
||||||
Any feature contribution or suggestion is strongly appreciated.
|
Any feature contribution or suggestion is strongly appreciated.
|
||||||
Since the current project size there isn't yet a defined way to start a discussion. Consider to [open a new issue](https://github.com/Valerioageno/tuono/issues/new)
|
|
||||||
or to reach me using my email address [valerioageno@yahoo.it](mailto:valerioageno@ahoo.it). I'm also available on twitter (X) DMs @valerioageno.
|
We are currently managing the tasks' prioritization in the [Road to V1](https://github.com/orgs/tuono-labs/projects/2) project. You can pick any task that you see in the `ready` column that is not already assigned.
|
||||||
|
|
||||||
|
If you want to contribute on something that is in the `backlog` column, or it hasn't been scoped yet, please drop a message in the [#general](https://discord.com/invite/khQzPa654B) discord channel so we can all have visibility of it.
|
||||||
|
|
||||||
|
Consider [opening a new issue](https://github.com/tuono-labs/tuono/issues/new/choose) if you find a bug.
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
**Did you find a bug?**
|
**Did you find a bug?**
|
||||||
- Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/Valerioageno/tuono/issues).
|
|
||||||
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Valerioageno/tuono/issues/new). Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring.
|
- Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/tuono-labs/tuono/issues).
|
||||||
|
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/tuono-labs/tuono/issues/new/choose). Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring.
|
||||||
|
|
||||||
**Did you write a patch that fixes a bug?**
|
**Did you write a patch that fixes a bug?**
|
||||||
|
|
||||||
- Open a new GitHub pull request with the patch.
|
- Open a new GitHub pull request with the patch.
|
||||||
- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
|
- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number, if applicable.
|
||||||
- The pull requests must pass all the CI pipelines
|
- The pull requests must pass all the CI pipelines
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ members = [
|
|||||||
"crates/tuono_lib_macros",
|
"crates/tuono_lib_macros",
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"examples/playground",
|
"apps/documentation",
|
||||||
"examples/tuono",
|
"examples/with-mdx",
|
||||||
"examples/tutorial"
|
"examples/tuono-app",
|
||||||
|
"examples/tuono-tutorial",
|
||||||
|
"benches/tuono"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,98 +1,79 @@
|
|||||||
<p align="center">
|
[Documentation](https://tuono.dev) | [Tutorial](https://tuono.dev/documentation/tutorial) |
|
||||||
<img src="https://raw.githubusercontent.com/Valerioageno/tuono/main/assets/logo.png" width="200px">
|
[Discord](https://discord.com/invite/khQzPa654B) | [✨Contributing](https://tuono.dev/documentation/contributing)
|
||||||
</p>
|
|
||||||
<h1 align="center">Tuono<br>The react/rust fullstack framework</h1>
|
|
||||||
<p align="center">
|
|
||||||
⚠️ This project is under heavy development ⚠️
|
|
||||||
</p>
|
|
||||||
<div align="center">
|
|
||||||
<img src="https://github.com/Valerioageno/tuono/actions/workflows/rust.yml/badge.svg" />
|
|
||||||
<img src="https://github.com/Valerioageno/tuono/actions/workflows/typescript.yml/badge.svg" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
# Tuono
|
||||||
<br>
|
|
||||||
|
|
||||||
|
<img align="right" src="https://raw.githubusercontent.com/tuono-labs/tuono/main/assets/logo.png" width="160px">
|
||||||
|
|
||||||
Tuono (Italian word for "thunder", pronounced /2 Oh No/).
|

|
||||||
|

|
||||||
|
|
||||||
|
**Tuono is a full-stack web framework for building React applications using Rust as the backend with
|
||||||
|
a strong focus on usability and performance.**
|
||||||
|
|
||||||
|
Tuono (Italian word for "thunder", pronounced /2 Oh No/).
|
||||||
Why Tuono? Just a badass name.
|
Why Tuono? Just a badass name.
|
||||||
|
|
||||||
> Check out the [tutorial](https://github.com/Valerioageno/tuono/blob/main/docs/tutorial.md) to get started.
|
If you have experience with [Next.js](https://nextjs.org/) you will feel home here.
|
||||||
|
|
||||||
|
Some of its features are:
|
||||||
|
|
||||||
|
- 🟦 Native Typescript
|
||||||
|
- 🌐 Next.js like Routing
|
||||||
|
- 🍭 CSS/SCSS modules
|
||||||
|
- 🧬 Server Side Rendering
|
||||||
|
- 🔥 Hot Module Reload
|
||||||
|
|
||||||
|
## 📖 Documentation
|
||||||
|
|
||||||
|
The [documentation](https://tuono.dev/) is available on
|
||||||
|
[tuono.dev](https://tuono.dev/).
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
**NodeJs/Deno/Bun are the only runtimes that allow a React app to be fullstack right? (no)**
|
**NodeJs/Deno/Bun are the only runtimes that allow a React app to be full-stack right? (no)**
|
||||||
|
|
||||||
Tuono is a fullstack React framework with the server side written in Rust.
|
Tuono is a full-stack React framework with the server side written in Rust.
|
||||||
Because of this Tuono is extremely fast and the requests are handled by multithreaded Rust server.
|
Because of this, Tuono is extremely fast, and the requests are handled by a multithreaded Rust server.
|
||||||
React is still React - it is just superpowered.
|
React is still React - it is just superpowered.
|
||||||
|
|
||||||
**Rust is an hard language then writing server side code is hard as well right? (no again)**
|
**Rust is a hard language, then writing server-side code is hard as well, right? (no again)**
|
||||||
|
|
||||||
Tuono provides a collection of utilities to handle the server side code seamlessly with the React code.
|
Tuono provides a collection of utilities to handle the server side code seamlessly with the React code.
|
||||||
Each server side route is managed with a separate file alongside the React route. The routing is handled
|
Each server-side route is managed in a separate file alongside the React route. The routing is handled
|
||||||
by Tuono based on the files defined within the `./src/routes` directory.
|
by Tuono based on the files defined within the `./src/routes` directory.
|
||||||
|
|
||||||
## Features
|
**How is Tuono different from Next.js?**
|
||||||
|
|
||||||
- 🟦 Typescript
|
The Tuono API tries to stick as much as possible to the Next.js one (or at least takes a huge inspiration
|
||||||
- 🌐 Routing
|
from it). The major difference is the backend system. While Next.js relies entirely on Node/Deno/Bun,
|
||||||
- 🔥 Hot Module Reload
|
Tuono runs the server without any intermediary runtime. This enables impressive performance improvements
|
||||||
- 🍭 CSS modules
|
(check the benchmarks [here](https://github.com/tuono-labs/tuono/tree/main/benches)).
|
||||||
- 🧬 Server Side Rendering
|
|
||||||
- 🧵 Multi thread backend
|
|
||||||
- ⚙️ Build optimizations
|
|
||||||
- Custom APIs*
|
|
||||||
- Image optimization*
|
|
||||||
- Server streamed content*
|
|
||||||
|
|
||||||
> *Development in progress
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
As already mentioned above I strongly suggest you to take a look at the
|
Tuono is the CLI that provides all the needed commands to handle the full-stack project.
|
||||||
[tutorial](https://github.com/Valerioageno/tuono/blob/main/docs/tutorial.md).
|
To download it is required [cargo](https://doc.rust-lang.org/cargo/), which is the [rust](https://www.rust-lang.org/)
|
||||||
|
|
||||||
Tuono is basically a CLI that provides all the commands to handle the fullstack project.
|
|
||||||
To download it you need [cargo](https://doc.rust-lang.org/cargo/) which is the [rust](https://www.rust-lang.org/)
|
|
||||||
package manager.
|
package manager.
|
||||||
|
|
||||||
To download and install it you just need to run `cargo install tuono`.
|
Then run `cargo install tuono`.
|
||||||
|
|
||||||
To create a new project run `tuono new [NAME]` (optionally you can pass the `--template` flag - check the
|
To list all the available commands, run `tuono -h`
|
||||||
[examples](https://github.com/Valerioageno/tuono/tree/main/examples) folder).
|
|
||||||
|
|
||||||
Then to run the local development environment run inside the project folder `tuono dev`
|
To create a new project, run `tuono new [NAME]` (optionally, you can pass the `--template` (or `-t`) flag - check the
|
||||||
|
[examples](https://github.com/tuono-labs/tuono/tree/main/examples) folder).
|
||||||
|
|
||||||
Finally when the project will be ready to be deployed just run `tuono build` to create the final React assets
|
Then, to run the local development environment, install the JS dependencies (`npm install`) and run inside the project folder `tuono dev`
|
||||||
and to set the server project to the `production` mode.
|
|
||||||
|
Finally, when the project is ready to be deployed, just run `tuono build` to create the final React assets
|
||||||
|
and to set the server project in `production` mode.
|
||||||
|
|
||||||
Now to execute it just run `cargo run --release`.
|
Now to execute it just run `cargo run --release`.
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- rust
|
|
||||||
- cargo
|
|
||||||
- node
|
|
||||||
- npm/pnpm/yarn*
|
|
||||||
|
|
||||||
> yarn [pnp](https://yarnpkg.com/features/pnp) is not supported yet
|
|
||||||
|
|
||||||
## Folder structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── package.json
|
|
||||||
├── public
|
|
||||||
├── src
|
|
||||||
│ ├── routes
|
|
||||||
│ └── styles
|
|
||||||
├── Cargo.toml
|
|
||||||
├── README.md
|
|
||||||
└── tsconfig.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Any help or suggestion will be appreciated and encouraged.
|
Any help or suggestion will be appreciated and encouraged.
|
||||||
|
Check the [✨Contributing](https://tuono.dev/documentation/contributing) page
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
# Stadalone apps folder
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "tuono"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "tuono"
|
||||||
|
path = ".tuono/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tuono_lib = "0.16.4"
|
||||||
|
glob = "0.3.1"
|
||||||
|
time = { version = "0.3", features = ["macros"] }
|
||||||
|
serde = { version = "1.0.202", features = ["derive"] }
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Tuono starter
|
||||||
|
|
||||||
|
This is the starter tuono project. To download it run in your terminal:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ tuono new [NAME]
|
||||||
|
```
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "documentation",
|
||||||
|
"description": "The react/rust fullstack framework documentation",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write --ignore-unknown .",
|
||||||
|
"format:check": "prettier --check --ignore-unknown .",
|
||||||
|
"types": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mantine/code-highlight": "7.14.2",
|
||||||
|
"@mantine/core": "7.14.2",
|
||||||
|
"@mantine/hooks": "7.14.2",
|
||||||
|
"@mdx-js/react": "3.1.0",
|
||||||
|
"@tabler/icons-react": "3.22.0",
|
||||||
|
"clsx": "2.1.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-dom": "18.3.1",
|
||||||
|
"tuono": "npm:tuono@0.16.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mdx-js/rollup": "3.1.0",
|
||||||
|
"@types/react": "18.3.13",
|
||||||
|
"@types/react-dom": "18.3.0",
|
||||||
|
"postcss": "8.4.49",
|
||||||
|
"postcss-preset-mantine": "1.17.0",
|
||||||
|
"postcss-simple-vars": "7.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
'postcss-preset-mantine': {},
|
||||||
|
'postcss-simple-vars': {
|
||||||
|
variables: {
|
||||||
|
'mantine-breakpoint-xs': '36em',
|
||||||
|
'mantine-breakpoint-sm': '48em',
|
||||||
|
'mantine-breakpoint-md': '62em',
|
||||||
|
'mantine-breakpoint-lg': '75em',
|
||||||
|
'mantine-breakpoint-xl': '88em',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"polyfills": [
|
||||||
|
{
|
||||||
|
"identifier": "HTMLElement",
|
||||||
|
"import": {
|
||||||
|
"package": "@lit-labs/ssr-dom-shim",
|
||||||
|
"name": "HTMLElement"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "customElements",
|
||||||
|
"import": {
|
||||||
|
"package": "@lit-labs/ssr-dom-shim",
|
||||||
|
"name": "customElements"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Breadcrumbs, Button } from '@mantine/core'
|
||||||
|
import { Link, Head } from 'tuono'
|
||||||
|
|
||||||
|
import { IconChevronRight, IconBolt } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
interface Breadcrumb {
|
||||||
|
href?: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
interface BreadcrumbsProps {
|
||||||
|
breadcrumbs: Array<Breadcrumb>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TuonoBreadcrumbs({
|
||||||
|
breadcrumbs = [],
|
||||||
|
}: BreadcrumbsProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{`{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "Tuono - The React/Rust fullstack framework",
|
||||||
|
"item": "https://tuono.dev"
|
||||||
|
}${breadcrumbs.length > 0 ? ',' : ''}
|
||||||
|
${breadcrumbs
|
||||||
|
.map((br, i) =>
|
||||||
|
JSON.stringify({
|
||||||
|
'@type': 'ListItem',
|
||||||
|
position: i + 3,
|
||||||
|
name: br.label,
|
||||||
|
item: br.href ? `https://tuono.dev${br.href}` : undefined,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.join(',')}]
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</script>
|
||||||
|
</Head>
|
||||||
|
<Breadcrumbs
|
||||||
|
separator={<IconChevronRight size="1.1rem" stroke={1.5} />}
|
||||||
|
mb="md"
|
||||||
|
mt="md"
|
||||||
|
>
|
||||||
|
<Button href="/" component={Link} variant="subtle" radius="xl" p={5}>
|
||||||
|
<IconBolt />
|
||||||
|
</Button>
|
||||||
|
{breadcrumbs.map((br) => (
|
||||||
|
<BreadcrumbElement href={br.href} label={br.label} key={br.label} />
|
||||||
|
))}
|
||||||
|
</Breadcrumbs>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BreadcrumbElementProps {
|
||||||
|
href?: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BreadcrumbElement({
|
||||||
|
href,
|
||||||
|
label,
|
||||||
|
}: BreadcrumbElementProps): JSX.Element {
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<Button component={Link} href={href} variant="subtle" radius="xl" px={10}>
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button component="span" variant="light" radius="xl">
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Breadcrumbs, { BreadcrumbElement } from './breadcrumbs'
|
||||||
|
|
||||||
|
export default Breadcrumbs
|
||||||
|
|
||||||
|
export { BreadcrumbElement as Element }
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Button } from '@mantine/core'
|
||||||
|
import { IconEdit } from '@tabler/icons-react'
|
||||||
|
import { useRouter } from 'tuono'
|
||||||
|
|
||||||
|
const GITHUB_URL =
|
||||||
|
'https://github.com/tuono-labs/tuono/tree/main/apps/documentation/src/routes'
|
||||||
|
|
||||||
|
export default function EditPage(): JSX.Element {
|
||||||
|
const { pathname } = useRouter()
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
p={0}
|
||||||
|
mt={60}
|
||||||
|
component="a"
|
||||||
|
variant="transparent"
|
||||||
|
leftSection={<IconEdit />}
|
||||||
|
target="_blank"
|
||||||
|
href={GITHUB_URL.concat(pathname).concat('.mdx')}
|
||||||
|
>
|
||||||
|
Edit page
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import EditPage from './edit-page'
|
||||||
|
|
||||||
|
export default EditPage
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Container,
|
||||||
|
CopyButton,
|
||||||
|
Group,
|
||||||
|
rem,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import { IconCopy, IconCheck } from '@tabler/icons-react'
|
||||||
|
import { Link } from 'tuono'
|
||||||
|
|
||||||
|
export default function Hero(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Container size={1100} my={100}>
|
||||||
|
<Center>
|
||||||
|
<Title order={1}>Superfast fullstack react framework</Title>
|
||||||
|
</Center>
|
||||||
|
<Center mt={50}>
|
||||||
|
<Text fz="18px">
|
||||||
|
The technologies we love seamlessly working together to unleash the
|
||||||
|
<strong> highest web performance</strong> ever met on React
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
<Center mt={50}>
|
||||||
|
<Group>
|
||||||
|
<Button component={Link} href="/documentation" size="lg">
|
||||||
|
Get Started
|
||||||
|
</Button>
|
||||||
|
<CopyButton value="cargo install tuono">
|
||||||
|
{({ copied, copy }) => (
|
||||||
|
<Button
|
||||||
|
onClick={copy}
|
||||||
|
size="lg"
|
||||||
|
style={{ border: 'solid 1px var(--mantine-color-violet-1)' }}
|
||||||
|
color="gray"
|
||||||
|
leftSection="$ cargo install tuono"
|
||||||
|
rightSection={
|
||||||
|
copied ? (
|
||||||
|
<IconCheck style={{ width: rem(20) }} />
|
||||||
|
) : (
|
||||||
|
<IconCopy style={{ width: rem(20) }} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</CopyButton>
|
||||||
|
</Group>
|
||||||
|
</Center>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import Hero from './hero'
|
||||||
|
|
||||||
|
export default Hero
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MdxProvider from './mdx-provider'
|
||||||
|
|
||||||
|
export default MdxProvider
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Text, type TextProps } from '@mantine/core'
|
||||||
|
|
||||||
|
export default function MdxBold(props: TextProps): JSX.Element {
|
||||||
|
return <Text fw={700} {...props} />
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MdxCode from './mdx-code'
|
||||||
|
|
||||||
|
export default MdxCode
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { JSX, HTMLAttributes } from 'react'
|
||||||
|
import { Code } from '@mantine/core'
|
||||||
|
|
||||||
|
export default function MdxCode(
|
||||||
|
props: HTMLAttributes<HTMLPreElement>,
|
||||||
|
): JSX.Element {
|
||||||
|
return <Code {...props} style={{ fontSize: 'inherit' }} />
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MdxLink from './mdx-link'
|
||||||
|
|
||||||
|
export default MdxLink
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import type { JSX, AnchorHTMLAttributes } from 'react'
|
||||||
|
import { Button } from '@mantine/core'
|
||||||
|
import { Link } from 'tuono'
|
||||||
|
import { IconExternalLink } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
export default function MdxLink(
|
||||||
|
props: AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||||
|
): JSX.Element {
|
||||||
|
if (props.href?.startsWith('http') || props.href?.startsWith('mailto')) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
component="a"
|
||||||
|
{...props}
|
||||||
|
target="_blank"
|
||||||
|
rightSection={
|
||||||
|
<IconExternalLink size="16px" style={{ marginLeft: -5 }} />
|
||||||
|
}
|
||||||
|
variant="transparent"
|
||||||
|
style={{ height: '20px' }}
|
||||||
|
mt={-2}
|
||||||
|
p={0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
component={Link}
|
||||||
|
{...props}
|
||||||
|
target="_blank"
|
||||||
|
variant="transparent"
|
||||||
|
style={{ height: '20px' }}
|
||||||
|
mt={-2}
|
||||||
|
p={0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MdxPre from './mdx-pre'
|
||||||
|
|
||||||
|
export default MdxPre
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.pre {
|
||||||
|
code {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { CodeHighlight } from '@mantine/code-highlight'
|
||||||
|
|
||||||
|
import styles from './mdx-pre.module.css'
|
||||||
|
|
||||||
|
interface PreProps {
|
||||||
|
children: {
|
||||||
|
props: {
|
||||||
|
children: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MdxPre({ children }: PreProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<CodeHighlight
|
||||||
|
className={styles.pre}
|
||||||
|
code={children.props.children || ''}
|
||||||
|
style={{ borderRadius: 8, fontWeight: 100 }}
|
||||||
|
language={children.props.className?.replace('language-', '')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import type { JSX, ReactNode } from 'react'
|
||||||
|
import { MDXProvider } from '@mdx-js/react'
|
||||||
|
|
||||||
|
import MdxLink from './mdx-link'
|
||||||
|
import MdxPre from './mdx-pre'
|
||||||
|
import MdxQuote from './mdx-quote'
|
||||||
|
import MdxCode from './mdx-code'
|
||||||
|
import { h } from './mdx-title'
|
||||||
|
import MdxBold from './mdx-bold/mdx-bold'
|
||||||
|
|
||||||
|
interface MdxProviderProps {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MdxProvider({
|
||||||
|
children,
|
||||||
|
}: MdxProviderProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<MDXProvider
|
||||||
|
components={{
|
||||||
|
a: MdxLink,
|
||||||
|
// @ts-expect-error: useless finding the correct props types
|
||||||
|
pre: MdxPre,
|
||||||
|
blockquote: MdxQuote,
|
||||||
|
code: MdxCode,
|
||||||
|
h1: h(1),
|
||||||
|
h2: h(2),
|
||||||
|
h3: h(3),
|
||||||
|
h4: h(4),
|
||||||
|
h5: h(5),
|
||||||
|
h6: h(6),
|
||||||
|
strong: MdxBold,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MDXProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MdxQuote from './mdx-quote'
|
||||||
|
|
||||||
|
export default MdxQuote
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import type { JSX, HTMLAttributes } from 'react'
|
||||||
|
import { Blockquote, Space } from '@mantine/core'
|
||||||
|
|
||||||
|
export default function MdxQuote(
|
||||||
|
props: HTMLAttributes<HTMLQuoteElement>,
|
||||||
|
): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Blockquote
|
||||||
|
color="violet"
|
||||||
|
py={1}
|
||||||
|
px={20}
|
||||||
|
mt={30}
|
||||||
|
iconSize={30}
|
||||||
|
{...props}
|
||||||
|
style={{ borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
<Space h="md" />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import MdxTitle, { h } from './mdx-title'
|
||||||
|
|
||||||
|
export default MdxTitle
|
||||||
|
|
||||||
|
export { h }
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Title, type TitleProps } from '@mantine/core'
|
||||||
|
|
||||||
|
export default function MdxTitle(props: TitleProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Title
|
||||||
|
data-heading={props.children}
|
||||||
|
data-order={props.order}
|
||||||
|
mt={20}
|
||||||
|
{...props}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||||
|
id={String(props.children ?? '')
|
||||||
|
.toLowerCase()
|
||||||
|
.replaceAll(' ', '-')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const h = (
|
||||||
|
order: 1 | 2 | 3 | 4 | 5 | 6,
|
||||||
|
): React.ElementType<TitleProps> => {
|
||||||
|
function render(props: TitleProps): JSX.Element {
|
||||||
|
return <MdxTitle order={order} {...props} />
|
||||||
|
}
|
||||||
|
render.displayName = 'H'
|
||||||
|
return render
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MetaTags from './meta-tags'
|
||||||
|
|
||||||
|
export default MetaTags
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import type { ReactNode } from 'react'
|
||||||
|
import { Head } from 'tuono'
|
||||||
|
|
||||||
|
interface MetaTagsProps {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
type?: 'website' | 'article' | 'book' | 'video.movie' | 'music.song'
|
||||||
|
canonical: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MetaTags({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
type = 'article',
|
||||||
|
canonical,
|
||||||
|
}: MetaTagsProps): ReactNode {
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<title>{title}</title>
|
||||||
|
<link rel="canonical" href={canonical} />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content={
|
||||||
|
description ??
|
||||||
|
'The technologies we love seamlessly working together to unleash the highest web performance ever met on React'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="ReactJs, Typescript, Rust, JavaScript, Fullstack framework, Web development"
|
||||||
|
/>
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:type" content={type} />
|
||||||
|
<meta property="og:image" content="https://tuono.dev/og-cover.png" />
|
||||||
|
<meta property="og:url" content={canonical} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:site_name" content="Tuono" />
|
||||||
|
<meta property="og:locale" content="en_US" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:site" content="@tuono_dev" />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content="https://tuono.dev/og-cover.png" />
|
||||||
|
</Head>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Flex, ActionIcon, Group } from '@mantine/core'
|
||||||
|
import { IconBrandGithub, IconBrandDiscord } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
import ThemeBtn from '../theme-btn'
|
||||||
|
|
||||||
|
export default function Actions(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Flex gap={8}>
|
||||||
|
<Group gap={8}>
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label="Check the project on github"
|
||||||
|
href="https://github.com/tuono-labs/tuono"
|
||||||
|
target="_blank"
|
||||||
|
component="a"
|
||||||
|
>
|
||||||
|
<IconBrandGithub />
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label="Join the Tuono's community on Discord!"
|
||||||
|
href="https://discord.com/invite/khQzPa654B"
|
||||||
|
target="_blank"
|
||||||
|
component="a"
|
||||||
|
>
|
||||||
|
<IconBrandDiscord />
|
||||||
|
</ActionIcon>
|
||||||
|
</Group>
|
||||||
|
<ThemeBtn />
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import Navbar from './navbar'
|
||||||
|
|
||||||
|
export default Navbar
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { AppShell, Box, Burger, Button, Flex } from '@mantine/core'
|
||||||
|
import { Link } from 'tuono'
|
||||||
|
|
||||||
|
import Actions from './actions'
|
||||||
|
|
||||||
|
interface NavbarProps {
|
||||||
|
toggle: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Navbar({ toggle }: NavbarProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<AppShell.Header p="sm">
|
||||||
|
<Flex justify="space-between">
|
||||||
|
<Button
|
||||||
|
component={Link}
|
||||||
|
href="/"
|
||||||
|
variant="transparent"
|
||||||
|
p={0}
|
||||||
|
fz={28}
|
||||||
|
hiddenFrom="sm"
|
||||||
|
>
|
||||||
|
Tuono
|
||||||
|
</Button>
|
||||||
|
<Box />
|
||||||
|
<Flex align="center" gap={8}>
|
||||||
|
<Actions />
|
||||||
|
<Burger onClick={toggle} hiddenFrom="sm" size="sm" />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</AppShell.Header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import NavigationButtons from './navigation-buttons'
|
||||||
|
|
||||||
|
export default NavigationButtons
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import { Box, Button, Text, Title, Flex } from '@mantine/core'
|
||||||
|
import { Link } from 'tuono'
|
||||||
|
import { IconArrowRight, IconArrowLeft } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
interface NavigationButton {
|
||||||
|
href: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigationButtonsProps {
|
||||||
|
prev?: NavigationButton
|
||||||
|
next?: NavigationButton
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NavigationButtons({
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
}: NavigationButtonsProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Flex mt={50} gap={10}>
|
||||||
|
{prev && <NavigationBtn type="prev" {...prev} />}
|
||||||
|
{next && <NavigationBtn type="next" {...next} />}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigationButtonProps extends NavigationButton {
|
||||||
|
type: 'next' | 'prev'
|
||||||
|
}
|
||||||
|
|
||||||
|
const NavigationBtn = ({
|
||||||
|
type,
|
||||||
|
title,
|
||||||
|
href,
|
||||||
|
}: NavigationButtonProps): JSX.Element => {
|
||||||
|
const heading = type === 'next' ? 'Next' : 'Previous'
|
||||||
|
const textAlign = type === 'next' ? 'left' : 'right'
|
||||||
|
const variant = type === 'next' ? 'filled' : 'outline'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
component={Link}
|
||||||
|
fullWidth
|
||||||
|
variant={variant}
|
||||||
|
href={href}
|
||||||
|
justify="space-between"
|
||||||
|
h="auto"
|
||||||
|
rightSection={type === 'next' && <IconArrowRight />}
|
||||||
|
leftSection={type === 'prev' && <IconArrowLeft />}
|
||||||
|
p="20"
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<Title component="span" display="block" order={4} style={{ textAlign }}>
|
||||||
|
{heading}
|
||||||
|
</Title>
|
||||||
|
<Text component="span" display="block" style={{ textAlign }}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
|
interface SidebarElementBase<T extends string> {
|
||||||
|
type: T
|
||||||
|
}
|
||||||
|
|
||||||
|
type SidebarDivider = SidebarElementBase<'divider'>
|
||||||
|
|
||||||
|
interface SidebarLink extends SidebarElementBase<'element'> {
|
||||||
|
label: string
|
||||||
|
href: string
|
||||||
|
children?: Array<SidebarLink>
|
||||||
|
leftIcon?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SidebarTitle extends SidebarElementBase<'title'> {
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SidebarElement = SidebarDivider | SidebarLink | SidebarTitle
|
||||||
|
|
||||||
|
export const sidebarElements: Array<SidebarElement> = [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Home',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Getting started',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Installation',
|
||||||
|
href: '/documentation/installation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'How is tuono different?',
|
||||||
|
href: '/documentation/how-is-tuono-different',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Tutorial',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial',
|
||||||
|
label: 'Intro',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/development-setup',
|
||||||
|
label: 'Development setup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/api-fetching',
|
||||||
|
label: 'API fetching',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/components',
|
||||||
|
label: 'Components',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/dynamic-routes',
|
||||||
|
label: 'Dynamic routes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/error-handling',
|
||||||
|
label: 'Error handling',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/seo',
|
||||||
|
label: 'SEO and meta tags',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/redirections',
|
||||||
|
label: 'Redirections',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/production',
|
||||||
|
label: 'Production build',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
href: '/documentation/tutorial/conclusion',
|
||||||
|
label: 'Conclusion',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{ type: 'title', label: 'Overview' },
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Routing',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Defining routes',
|
||||||
|
href: '/documentation/routing/defining-routes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Dynamic routes',
|
||||||
|
href: '/documentation/routing/dynamic-routes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Link and navigation',
|
||||||
|
href: '/documentation/routing/link-and-navigation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Pages',
|
||||||
|
href: '/documentation/routing/pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Loading state',
|
||||||
|
href: '/documentation/routing/loading-state',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Layouts',
|
||||||
|
href: '/documentation/routing/layouts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Redirecting',
|
||||||
|
href: '/documentation/routing/redirecting',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Rendering',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Server side rendering',
|
||||||
|
href: '/documentation/rendering/server-side-rendering',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Static site rendering',
|
||||||
|
href: '/documentation/rendering/static-site-rendering',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Styles',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'CSS modules',
|
||||||
|
href: '/documentation/styles/css-modules',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Integrations',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'MDX',
|
||||||
|
href: '/documentation/integrations/mdx',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Core concepts',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Multithreading',
|
||||||
|
href: '/documentation/core-concepts/multithreading',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{ type: 'title', label: 'API reference' },
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Components',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Head',
|
||||||
|
href: '/documentation/components/head',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Link',
|
||||||
|
href: '/documentation/components/link',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Hooks',
|
||||||
|
href: '#focus',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'useRouter',
|
||||||
|
href: '/documentation/hooks/use-router',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'CLI',
|
||||||
|
href: '/documentation/cli',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Application state',
|
||||||
|
href: '/documentation/application-state',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'tuono.config.ts',
|
||||||
|
href: '/documentation/configuration',
|
||||||
|
},
|
||||||
|
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Contributing',
|
||||||
|
href: '#focus',
|
||||||
|
leftIcon: '✨',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Guildelines',
|
||||||
|
href: '/documentation/contributing',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
label: 'Local development',
|
||||||
|
href: '/documentation/contributing/local-development',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import Sidebar from './sidebar'
|
||||||
|
|
||||||
|
export default Sidebar
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.link {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link span {
|
||||||
|
font-size: 14px !important;
|
||||||
|
color: var(--mantine-color-sidebar-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover span,
|
||||||
|
.active span {
|
||||||
|
color: var(--mantine-color-sidebar-text-hover);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import type { JSX, ReactNode } from 'react'
|
||||||
|
import { NavLink, type NavLinkProps } from '@mantine/core'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { Link, useRouter } from 'tuono'
|
||||||
|
import { IconChevronRight } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
import styles from './sidebar-link.module.css'
|
||||||
|
|
||||||
|
interface SidebarLinkProps {
|
||||||
|
label: string
|
||||||
|
href: string
|
||||||
|
onClick?: () => void
|
||||||
|
children?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SidebarLink(
|
||||||
|
props: SidebarLinkProps & NavLinkProps,
|
||||||
|
): JSX.Element {
|
||||||
|
const { pathname } = useRouter()
|
||||||
|
|
||||||
|
const isActive = pathname === props.href
|
||||||
|
|
||||||
|
const internalProps = {
|
||||||
|
active: isActive,
|
||||||
|
className: clsx(styles.link, isActive && styles.active),
|
||||||
|
rightSection: props.children && (
|
||||||
|
<IconChevronRight
|
||||||
|
size="1.2rem"
|
||||||
|
stroke={1.5}
|
||||||
|
className="mantine-rotate-rtl"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
autoContrast: true,
|
||||||
|
...props,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.href.startsWith('#')) {
|
||||||
|
return <NavLink component="button" {...internalProps} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <NavLink component={Link} {...internalProps} />
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
AppShell,
|
||||||
|
Badge,
|
||||||
|
Flex,
|
||||||
|
Divider,
|
||||||
|
Title,
|
||||||
|
Button,
|
||||||
|
ScrollArea,
|
||||||
|
Text,
|
||||||
|
} from '@mantine/core'
|
||||||
|
|
||||||
|
import { IconX } from '@tabler/icons-react'
|
||||||
|
import { useMediaQuery } from '@mantine/hooks'
|
||||||
|
|
||||||
|
import { sidebarElements } from './config'
|
||||||
|
import SidebarLink from './sidebar-link'
|
||||||
|
|
||||||
|
interface SidebarProps {
|
||||||
|
close: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarHeader({ close }: SidebarProps): JSX.Element {
|
||||||
|
const isSm = useMediaQuery('(min-width: 48em)')
|
||||||
|
return (
|
||||||
|
<AppShell.Section>
|
||||||
|
<Flex mb={20} w="100%" justify="space-between">
|
||||||
|
<Flex
|
||||||
|
gap={10}
|
||||||
|
align="center"
|
||||||
|
w={isSm ? '100%' : 'auto'}
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
<Title order={3}>Tuono</Title>
|
||||||
|
<Badge mt={4} size="xs" variant="outline">
|
||||||
|
Docs
|
||||||
|
</Badge>
|
||||||
|
</Flex>
|
||||||
|
<Button onClick={close} hiddenFrom="sm" variant="transparent" p="0">
|
||||||
|
<IconX />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</AppShell.Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarElements({ close }: SidebarProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<AppShell.Section component={ScrollArea}>
|
||||||
|
{sidebarElements.map((el, i) => {
|
||||||
|
if (el.type === 'divider') {
|
||||||
|
return <Divider key={`${el.type}-${i}`} my="md" mx={10} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.type === 'title') {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
key={`${el.type}-${i}`}
|
||||||
|
size="xs"
|
||||||
|
fw={700}
|
||||||
|
fz={12}
|
||||||
|
pl={12}
|
||||||
|
py={5}
|
||||||
|
>
|
||||||
|
{el.label}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.children?.length) {
|
||||||
|
return (
|
||||||
|
<SidebarLink
|
||||||
|
key={`${el.type}-${i}`}
|
||||||
|
href={el.href}
|
||||||
|
label={el.label}
|
||||||
|
leftSection={el.leftIcon}
|
||||||
|
>
|
||||||
|
{el.children.map((child, index) => (
|
||||||
|
<SidebarLink
|
||||||
|
href={child.href}
|
||||||
|
label={child.label}
|
||||||
|
key={index}
|
||||||
|
onClick={close}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SidebarLink>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarLink
|
||||||
|
key={`${el.type}-${i}`}
|
||||||
|
href={el.href}
|
||||||
|
label={el.label}
|
||||||
|
onClick={close}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</AppShell.Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Sidebar({ close }: SidebarProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<AppShell.Navbar p="md">
|
||||||
|
<SidebarHeader close={close} />
|
||||||
|
<SidebarElements close={close} />
|
||||||
|
</AppShell.Navbar>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
|
||||||
|
*/
|
||||||
|
export interface Heading {
|
||||||
|
depth: number
|
||||||
|
content: string
|
||||||
|
id: string
|
||||||
|
getNode: () => HTMLHeadingElement
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeadingsData(headings: Array<HTMLHeadingElement>): Array<Heading> {
|
||||||
|
const result: Array<Heading> = []
|
||||||
|
|
||||||
|
for (const heading of headings) {
|
||||||
|
if (heading.id) {
|
||||||
|
result.push({
|
||||||
|
depth: parseInt(heading.getAttribute('data-order') || '1', 10),
|
||||||
|
content: heading.getAttribute('data-heading') || '',
|
||||||
|
id: heading.id,
|
||||||
|
getNode: () =>
|
||||||
|
document.getElementById(heading.id) as HTMLHeadingElement,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHeadings(): Array<Heading> {
|
||||||
|
const root = document.getElementById('mdx-root')
|
||||||
|
console.log(root)
|
||||||
|
|
||||||
|
if (!root) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return getHeadingsData(Array.from(root.querySelectorAll('[data-heading]')))
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
|
||||||
|
*/
|
||||||
|
import { TableOfContents } from './table-of-content'
|
||||||
|
|
||||||
|
export default TableOfContents
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
|
||||||
|
*/
|
||||||
|
.wrapper {
|
||||||
|
padding-left: var(--mantine-spacing-md);
|
||||||
|
position: sticky;
|
||||||
|
top: var(--mantine-spacing-xl);
|
||||||
|
right: 0;
|
||||||
|
padding-top: 55px;
|
||||||
|
flex: 0 0 calc(var(--docs-table-of-contents-width) - 20px);
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
@mixin rtl {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--mantine-spacing-md);
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-with-tabs] {
|
||||||
|
padding-top: 0;
|
||||||
|
top: calc(var(--mantine-spacing-xl) + 60px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
padding-bottom: var(--mantine-spacing-xl);
|
||||||
|
padding-left: var(--mantine-spacing-md);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
@mixin rtl {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--mantine-spacing-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
display: block;
|
||||||
|
border-left: 1px solid transparent;
|
||||||
|
padding: 8px var(--mantine-spacing-md);
|
||||||
|
margin-left: -1px;
|
||||||
|
padding-left: calc(var(--toc-link-offset) * var(--mantine-spacing-lg));
|
||||||
|
border-top-right-radius: var(--mantine-radius-sm);
|
||||||
|
border-bottom-right-radius: var(--mantine-radius-sm);
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
color: var(--mantine-color-gray-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
color: var(--mantine-color-dark-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin rtl {
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 1px solid transparent;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: -1px;
|
||||||
|
border-top-left-radius: var(--mantine-radius-sm);
|
||||||
|
border-bottom-left-radius: var(--mantine-radius-sm);
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-active] {
|
||||||
|
border-color: var(--mantine-color-violet-5);
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
color: var(--mantine-color-violet-8);
|
||||||
|
background-color: var(--mantine-color-violet-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
color: var(--mantine-color-violet-1);
|
||||||
|
background-color: #534a6d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: var(--mantine-spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-left: var(--mantine-spacing-md);
|
||||||
|
|
||||||
|
@mixin rtl {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--mantine-spacing-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
border-left: 1px solid;
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
border-color: var(--mantine-color-gray-3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
border-color: var(--mantine-color-dark-4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin rtl {
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 1px solid;
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
border-color: var(--mantine-color-gray-3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
border-color: var(--mantine-color-dark-4) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
|
||||||
|
*/
|
||||||
|
import type { JSX } from 'react'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useRouter, Link } from 'tuono'
|
||||||
|
import { IconList } from '@tabler/icons-react'
|
||||||
|
import { Box, rem, ScrollArea, Text } from '@mantine/core'
|
||||||
|
|
||||||
|
import { getHeadings, type Heading } from './get-headings'
|
||||||
|
import classes from './table-of-content.module.css'
|
||||||
|
|
||||||
|
interface TableOfContentsProps {
|
||||||
|
withTabs: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveElement(rects: Array<DOMRect>): number {
|
||||||
|
if (rects.length === 0) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const closest = rects.reduce(
|
||||||
|
(acc, item, index) => {
|
||||||
|
if (Math.abs(acc.position) < Math.abs(item.y)) {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
position: item.y,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ index: 0, position: rects[0].y },
|
||||||
|
)
|
||||||
|
|
||||||
|
return closest.index
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TableOfContents({
|
||||||
|
withTabs,
|
||||||
|
}: TableOfContentsProps): JSX.Element | null {
|
||||||
|
const [active, setActive] = useState(0)
|
||||||
|
const [headings, setHeadings] = useState<Array<Heading>>([])
|
||||||
|
const headingsRef = useRef<Array<Heading>>([])
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const filteredHeadings = headings.filter((heading) => heading.depth > 1)
|
||||||
|
|
||||||
|
const handleScroll = (): void => {
|
||||||
|
setActive(
|
||||||
|
getActiveElement(
|
||||||
|
headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const _headings = getHeadings()
|
||||||
|
headingsRef.current = _headings
|
||||||
|
setHeadings(_headings)
|
||||||
|
setActive(
|
||||||
|
getActiveElement(
|
||||||
|
_headings.map((d) => d.getNode().getBoundingClientRect()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
window.addEventListener('scroll', handleScroll)
|
||||||
|
return (): void => {
|
||||||
|
window.removeEventListener('scroll', handleScroll)
|
||||||
|
}
|
||||||
|
}, [router.pathname])
|
||||||
|
|
||||||
|
if (filteredHeadings.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = filteredHeadings.map((heading, index) => (
|
||||||
|
<Text
|
||||||
|
key={heading.id}
|
||||||
|
component={Link}
|
||||||
|
fz="sm"
|
||||||
|
p={10}
|
||||||
|
className={classes.link}
|
||||||
|
mod={{ active: active === index }}
|
||||||
|
href={`#${heading.id}`}
|
||||||
|
__vars={{ '--toc-link-offset': `${heading.depth - 1}` }}
|
||||||
|
>
|
||||||
|
{heading.content}
|
||||||
|
</Text>
|
||||||
|
))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component="nav"
|
||||||
|
mod={{ 'with-tabs': withTabs }}
|
||||||
|
className={classes.wrapper}
|
||||||
|
>
|
||||||
|
<div className={classes.inner}>
|
||||||
|
<div>
|
||||||
|
<div className={classes.header}>
|
||||||
|
<IconList
|
||||||
|
style={{ width: rem(20), height: rem(20) }}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
<Text className={classes.title}>Table of contents</Text>
|
||||||
|
</div>
|
||||||
|
<ScrollArea.Autosize
|
||||||
|
mah={`calc(100vh - ${rem(140)})`}
|
||||||
|
type="never"
|
||||||
|
offsetScrollbars
|
||||||
|
>
|
||||||
|
<div className={classes.items}>{items}</div>
|
||||||
|
</ScrollArea.Autosize>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import ThemeBtn from './theme-btn'
|
||||||
|
|
||||||
|
export default ThemeBtn
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
.icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
@mixin dark {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
@mixin light {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import type { JSX } from 'react'
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
useMantineColorScheme,
|
||||||
|
useComputedColorScheme,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import { IconSun, IconMoon } from '@tabler/icons-react'
|
||||||
|
import cx from 'clsx'
|
||||||
|
|
||||||
|
import classes from './theme-btn.module.css'
|
||||||
|
|
||||||
|
export default function ThemeBtn(): JSX.Element {
|
||||||
|
const { setColorScheme } = useMantineColorScheme()
|
||||||
|
const computedColorScheme = useComputedColorScheme('light', {
|
||||||
|
getInitialValueInEffect: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => {
|
||||||
|
setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')
|
||||||
|
}}
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label="Toggle color scheme"
|
||||||
|
>
|
||||||
|
<IconSun className={cx(classes.icon, classes.light)} stroke={1.5} />
|
||||||
|
<IconMoon className={cx(classes.icon, classes.dark)} stroke={1.5} />
|
||||||
|
</ActionIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// declaration.d.ts
|
||||||
|
declare module '*.css' {
|
||||||
|
const CSSModule: Record<string, string>
|
||||||
|
export default CSSModule
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import type { ReactNode, JSX } from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColorSchemeScript,
|
||||||
|
createTheme,
|
||||||
|
MantineProvider,
|
||||||
|
AppShell,
|
||||||
|
Container,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import type { CSSVariablesResolver } from '@mantine/core'
|
||||||
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
|
import { Head } from 'tuono'
|
||||||
|
|
||||||
|
import EditPage from '@/components/edit-page'
|
||||||
|
import MdxProvider from '@/components/mdx-provider'
|
||||||
|
import Navbar from '@/components/navbar'
|
||||||
|
import Sidebar from '@/components/sidebar'
|
||||||
|
|
||||||
|
import '@mantine/core/styles.css'
|
||||||
|
import '@mantine/code-highlight/styles.css'
|
||||||
|
|
||||||
|
interface RootRouteProps {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const theme = createTheme({
|
||||||
|
primaryColor: 'violet',
|
||||||
|
primaryShade: { light: 6, dark: 9 },
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
respectReducedMotion: true,
|
||||||
|
radius: {
|
||||||
|
xs: '8px',
|
||||||
|
lg: '8px',
|
||||||
|
xl: '8px',
|
||||||
|
md: '8px',
|
||||||
|
sm: '8px',
|
||||||
|
},
|
||||||
|
fontSizes: {
|
||||||
|
// 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||||
|
xs: '16px',
|
||||||
|
sm: '16px',
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
dark: [
|
||||||
|
'#d5d7e0',
|
||||||
|
'#acaebf',
|
||||||
|
'#8c8fa3',
|
||||||
|
'#666980',
|
||||||
|
'#4d4f66',
|
||||||
|
'#34354a',
|
||||||
|
'#2b2c3d',
|
||||||
|
'#1d1e30',
|
||||||
|
'#0c0d21',
|
||||||
|
'#01010a',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
headings: {
|
||||||
|
sizes: {
|
||||||
|
h1: {
|
||||||
|
fontSize: '48px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
sidebarGrayLight: '#495057',
|
||||||
|
sidebarGrayDark: '#adb5bd',
|
||||||
|
sidebarTextHoverLight: '#212529',
|
||||||
|
sidebarTextHoverDark: '#f8f9fa',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const resolver: CSSVariablesResolver = (th) => ({
|
||||||
|
variables: {},
|
||||||
|
light: {
|
||||||
|
'--mantine-color-sidebar-gray': th.other.sidebarGrayLight as string,
|
||||||
|
'--mantine-color-sidebar-text-hover': th.other
|
||||||
|
.sidebarTextHoverLight as string,
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
'--mantine-color-sidebar-gray': th.other.sidebarGrayDark as string,
|
||||||
|
'--mantine-color-sidebar-text-hover': th.other
|
||||||
|
.sidebarTextHoverDark as string,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
|
||||||
|
const [opened, { toggle }] = useDisclosure()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<meta charSet="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
</Head>
|
||||||
|
<ColorSchemeScript />
|
||||||
|
<MantineProvider theme={theme} cssVariablesResolver={resolver}>
|
||||||
|
<AppShell
|
||||||
|
layout="alt"
|
||||||
|
header={{ height: 60 }}
|
||||||
|
navbar={{
|
||||||
|
width: 300,
|
||||||
|
breakpoint: 'sm',
|
||||||
|
collapsed: { mobile: !opened },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Navbar toggle={toggle} />
|
||||||
|
<Sidebar close={toggle} />
|
||||||
|
<AppShell.Main>
|
||||||
|
<Container id="mdx-root" component="article" size="md" p={20}>
|
||||||
|
<MdxProvider>{children}</MdxProvider>
|
||||||
|
<EditPage />
|
||||||
|
</Container>
|
||||||
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
|
</MantineProvider>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Application state"
|
||||||
|
canonical="https://tuono.dev/documentation/application-state"
|
||||||
|
description="Learn how to add features to your Tuono application"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Application state' }]} />
|
||||||
|
|
||||||
|
# Application state
|
||||||
|
|
||||||
|
The main reason Tuono is fast is that it loads just the features that are needed for the project.
|
||||||
|
|
||||||
|
To define them, you need to fill the `ApplicationState` struct in the `./src/app.rs` file, and
|
||||||
|
they will be automatically available in all the handlers you will define across the application.
|
||||||
|
|
||||||
|
```rs
|
||||||
|
// src/app.rs
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ApplicationState {
|
||||||
|
pub website_name: String,
|
||||||
|
pub base_path: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> ApplicationState {
|
||||||
|
let website_name = "tuono".to_string();
|
||||||
|
let base_path = "http://localhost:3000".to_string();
|
||||||
|
|
||||||
|
ApplicationState {
|
||||||
|
website_name,
|
||||||
|
base_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> For the sake of simplicity here we just enable a `String` but you can add Database connections or
|
||||||
|
> HTTP clients (take a look [here](/documentation/tutorial/api-fetching) for an example with an HTTP client).
|
||||||
|
|
||||||
|
Now the `ApplicationState` is available on all the handlers as following:
|
||||||
|
|
||||||
|
```rs
|
||||||
|
// src/routes/index.rs
|
||||||
|
|
||||||
|
#[tuono_lib::handler]
|
||||||
|
// The first argument always is the Request
|
||||||
|
// The ApplicationState arguments are optional. You can use just the ones you need
|
||||||
|
// to use in the handler (with no specific order).
|
||||||
|
async fn my_handler(req: Request, website_name: String) -> Response {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - CLI"
|
||||||
|
canonical="https://tuono.dev/documentation/cli"
|
||||||
|
description="Tuono is the CLI that provides all the needed commands to handle the full-stack project."
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'CLI' }]} />
|
||||||
|
|
||||||
|
# CLI
|
||||||
|
|
||||||
|
Tuono is the CLI that provides all the needed commands to handle the full-stack project.
|
||||||
|
|
||||||
|
> ☝️ Check the [installation](/documentation/installation) page if you haven't installed the
|
||||||
|
> `tuono` CLI yet.
|
||||||
|
|
||||||
|
To list all the available commands, run `tuono -h`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
The React/Rust full-stack framework
|
||||||
|
|
||||||
|
Usage: tuono <COMMAND>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
dev Start the development environment
|
||||||
|
build Build the production assets
|
||||||
|
new Scaffold a new project
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a new project, run `tuono new [NAME]` (optionally you can pass the --template (or -t) flag - check the examples' folder).
|
||||||
|
|
||||||
|
Then to run the local development environment run inside the project folder `tuono dev`
|
||||||
|
|
||||||
|
Finally, when the project will be ready to be deployed just run `tuono build` to create the final React assets and to set the server project in the production mode.
|
||||||
|
|
||||||
|
Now to execute it, just run `cargo run --release`.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Head"
|
||||||
|
canonical="https://tuono.dev/documentation/components/head"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Components' }]} />
|
||||||
|
|
||||||
|
# Head
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Link"
|
||||||
|
canonical="https://tuono.dev/documentation/components/link"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Components' }]} />
|
||||||
|
|
||||||
|
# Link
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - tuono.config.ts"
|
||||||
|
canonical="https://tuono.dev/documentation/configuration"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Configuration' }]} />
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Contributing"
|
||||||
|
canonical="https://tuono.dev/documentation/contributing"
|
||||||
|
description="The project is massive - if you like it, do consider contributing!"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
The project is massive - if you like it, do consider contributing!
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
The `tuono` project can mostly be split into the following subdomains:
|
||||||
|
|
||||||
|
- The CLI
|
||||||
|
- The Rust backend
|
||||||
|
- The React frontend
|
||||||
|
- The documentation website (which is written with tuono 🚀)
|
||||||
|
|
||||||
|
To check what the knowledge requirements are for each domain, check the
|
||||||
|
[requirements](#requirements) section below.
|
||||||
|
|
||||||
|
Currently, I'm keeping a private dashboard to prioritize new features and bug fixes, but if you
|
||||||
|
want to propose something, please open a new issue on Github or reach out to me using
|
||||||
|
my email address [valerioageno@yahoo.it](mailto:valerioageno@yahoo.it). I'm also available
|
||||||
|
on Twitter (X) DMs `@valerioageno`, [Linkedin](https://www.linkedin.com/in/valerioageno)
|
||||||
|
and discord `@__v__v__`.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
It's not strictly required to know both React (& typescript) and Rust (even though it
|
||||||
|
would be a great nice to have).
|
||||||
|
|
||||||
|
Without taking into account specific cases, we can mostly split the domain requirements by:
|
||||||
|
|
||||||
|
- The `CLI` needs Rust knowledge (even though a couple of scenarios might also need Typescript)
|
||||||
|
- The Backend needs just `Rust`
|
||||||
|
- The Frontend needs just `React` & `Typescript`
|
||||||
|
- The documentation website needs just `React` & `Typescript` (or even less, since most of the
|
||||||
|
code is markdown).
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Contributing"
|
||||||
|
canonical="https://tuono.dev/documentation/contributing"
|
||||||
|
description="The project is massive - if you like it, do consider contributing!"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
The project is massive - if you like it, do consider contributing!
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
The `tuono` project can mostly be split into the following subdomains:
|
||||||
|
|
||||||
|
- The CLI
|
||||||
|
- The Rust backend
|
||||||
|
- The React frontend
|
||||||
|
- The documentation website (which is written with tuono 🚀)
|
||||||
|
|
||||||
|
To check what the knowledge requirements are for each domain, check the
|
||||||
|
[requirements](#requirements) section below.
|
||||||
|
|
||||||
|
We are currently managing the tasks' prioritization in the [Road to V1](https://github.com/orgs/tuono-labs/projects/2) project.
|
||||||
|
You can pick any task that you see in the `ready` column that is not already assigned.
|
||||||
|
|
||||||
|
If you want to contribute on something that is in the `backlog` column, or it hasn't been scoped yet, please drop a message in the [#general](https://discord.com/invite/khQzPa654B) discord channel so we can all have visibility of it.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
It's not strictly required to know both React (& typescript) and Rust (even though it
|
||||||
|
would be a great nice to have).
|
||||||
|
|
||||||
|
Without taking into account specific cases, we can mostly split the domain requirements by:
|
||||||
|
|
||||||
|
- The `CLI` needs Rust knowledge (even though a couple of scenarios might also need Typescript)
|
||||||
|
- The Backend needs just `Rust`
|
||||||
|
- The Frontend needs just `React` & `Typescript`
|
||||||
|
- The documentation website needs just `React` & `Typescript` (or even less, since most of the
|
||||||
|
code is markdown).
|
||||||
|
|
||||||
|
import NavigationButtons from '../../../components/navigation-buttons'
|
||||||
|
|
||||||
|
<NavigationButtons
|
||||||
|
next={{
|
||||||
|
title: 'Local development',
|
||||||
|
href: '/documentation/contributing/local-development',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Contributing - Local development"
|
||||||
|
canonical="https://tuono.dev/documentation/contributing/local-development"
|
||||||
|
description="Contribute to Tuono. Learn here how to setup the repository for local development"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs
|
||||||
|
breadcrumbs={[
|
||||||
|
{ label: '✨ Contributing', href: '/documentation/contributing' },
|
||||||
|
{ label: 'Local development' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
# Local development
|
||||||
|
|
||||||
|
Thanks for your interest! In this page, you find the instructions to set up `tuono` on your local environment!
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Fork and clone repository
|
||||||
|
|
||||||
|
After [forking the repo on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/<your-name-here>/tuono
|
||||||
|
cd tuono
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rust tool chain
|
||||||
|
|
||||||
|
Install the Rust programming language tool chain (`rust` and `cargo`).
|
||||||
|
Follows instructions in the official [docs](https://rustup.rs/)
|
||||||
|
|
||||||
|
### Node.js — runtime
|
||||||
|
|
||||||
|
Install `Node.js`.
|
||||||
|
You can follow the instructions from the [Node official site](https://nodejs.org/en/download/package-manager)
|
||||||
|
|
||||||
|
> 💡 This project has a `.nvmrc` file to specify the node version used in development.
|
||||||
|
>
|
||||||
|
> Consider to use [nvm](https://github.com/nvm-sh/nvm) so you can run
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> nvm use
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> to simply pick up the correct version!
|
||||||
|
|
||||||
|
### Node.js — package manager
|
||||||
|
|
||||||
|
We use [`pnpm`](https://pnpm.io) as Node.js package manager.
|
||||||
|
|
||||||
|
You can see which version of yarn we use by checking the `packageManager` field in the root `package.json`.
|
||||||
|
|
||||||
|
### Pre-flight checks
|
||||||
|
|
||||||
|
To check that everything is working properly, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
turbo run check-all
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tuono development
|
||||||
|
|
||||||
|
1. Start tuono frontend build using
|
||||||
|
|
||||||
|
```sh
|
||||||
|
turbo run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. In another terminal run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
To automatically rebuild crates on code change, consider using `cargo-watch` crate
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo watch -x build -w crates/
|
||||||
|
```
|
||||||
|
|
||||||
|
3. You can now use the binary inside `/target/debug/tuono` in another folder on your local machine
|
||||||
|
|
||||||
|
> Consider adding an alias to your shell setup file
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> alias t="/path-to-repo/target/debug/tuono"
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## Documentation development
|
||||||
|
|
||||||
|
1. Change the current working directory to the documentation folder:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd apps/documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
tuono dev
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Open the localhost URL.
|
||||||
|
|
||||||
|
> On the documentation remember that `tuono` `npm` package is installed from the registry and
|
||||||
|
> it is not linked to the repository.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Multithreading"
|
||||||
|
canonical="https://tuono.dev/documentation/core-concepts/multithreading"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Core concepts' }]} />
|
||||||
|
|
||||||
|
# Multithreading
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - useRouter"
|
||||||
|
canonical="https://tuono.dev/documentation/hooks/use-router"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Hooks' }]} />
|
||||||
|
|
||||||
|
# useRouter
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - tuono.config.ts"
|
||||||
|
canonical="https://tuono.dev/documentation/how-is-tuono-different"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'How is tuono different?' }]} />
|
||||||
|
|
||||||
|
# How is tuono different?
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Installation"
|
||||||
|
canonical="https://tuono.dev/documentation/installation"
|
||||||
|
description="Tuono is a development environment built in rust and typescript that outputs a website written in both the languages"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Installation' }]} />
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
`Tuono` is a development environment built in rust and typescript that outputs a website written in both the
|
||||||
|
languages. Since then, you need the following tools installed on your computer to work with it:
|
||||||
|
|
||||||
|
- `Rust` - The Rust programming language tool chain (Go [here](https://rustup.rs/) for installing both `rust` and `cargo`)
|
||||||
|
- `Cargo` - The rust package manager
|
||||||
|
- `NodeJs` - The JavaScript runtime (Go [here](https://nodejs.org/en/download/package-manager) for installing both `nodejs` and `npm`)
|
||||||
|
- `npm` or `yarn` or `pnpm` - A javaScript package manager
|
||||||
|
|
||||||
|
> Node.js is needed just for the development environment. The final output will run on a Rust server.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The tuono `CLI` is hosted on [crates.io](https://crates.io/crates/tuono) to download and install it, just run it on a terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install tuono
|
||||||
|
```
|
||||||
|
|
||||||
|
To check that it is correctly installed, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tuono --version
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `tuono -h` to see all the available commands.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
The React/Rust full-stack framework
|
||||||
|
|
||||||
|
Usage: tuono <COMMAND>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
dev Start the development environment
|
||||||
|
build Build the production assets
|
||||||
|
new Scaffold a new project
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
```
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - MDX"
|
||||||
|
canonical="https://tuono.dev/documentation/integrations/mdx"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Integrations' }]} />
|
||||||
|
|
||||||
|
# MDX
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Server side rendering"
|
||||||
|
canonical="https://tuono.dev/documentation/rendering/server-side-rendering"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Rendering' }]} />
|
||||||
|
|
||||||
|
# SSR
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Static site rendering"
|
||||||
|
canonical="https://tuono.dev/documentation/rendering/static-site-rendering"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Rendering' }]} />
|
||||||
|
|
||||||
|
# Static Site Rendering
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Routing"
|
||||||
|
canonical="https://tuono.dev/documentation/routing/defining-routes"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Defining routes' }]} />
|
||||||
|
|
||||||
|
# Defining routes
|
||||||
|
|
||||||
|
Tuono has a file-system based router built on the concept of routes.
|
||||||
|
|
||||||
|
When a file is added to the `src/routes` directory, it's automatically available as a route.
|
||||||
|
|
||||||
|
In Tuono, a page is a [React Component](https://react.dev/learn/your-first-component) exported from a `.tsx` file in the `src/routes` directory.
|
||||||
|
Each page is associated with a route based on its file name.
|
||||||
|
|
||||||
|
Every page is rendered server side by default.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
If you create `src/routes/about.tsx` that exports a React component like:
|
||||||
|
|
||||||
|
```
|
||||||
|
export default function About() {
|
||||||
|
return <div>About</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `About` component will be rendered when loading `/about` route.
|
||||||
|
|
||||||
|
## Index routes
|
||||||
|
|
||||||
|
The router will automatically route files named `index` to the root of the directory.
|
||||||
|
|
||||||
|
- `src/routes/index.tsx` → `/`
|
||||||
|
- `src/routes/blog/index.tsx`→ `/blog`
|
||||||
|
|
||||||
|
## Nested routes
|
||||||
|
|
||||||
|
You can also create a nested folder structure, and files will still be routed in the same way.
|
||||||
|
|
||||||
|
- `src/routes/blog/first-post.tsx` → `/blog/first-post`
|
||||||
|
- `src/routes/dashboard/settings/username.tsx` → `/dashboard/settings/username`
|
||||||
|
|
||||||
|
## Pages with Dynamic Routes
|
||||||
|
|
||||||
|
Tuono supports pages with dynamic routes segments.
|
||||||
|
For example, if you create a file called `src/routes/posts/[id].tsx`,
|
||||||
|
then it will be accessible at `posts/1`, `posts/2`, etc.
|
||||||
|
|
||||||
|
## The Root route (Layout components)
|
||||||
|
|
||||||
|
Tuono allows you to have a layout component to wrap all the routes included within the same folder.
|
||||||
|
To define such component you will have to create a `__root.tsx` file (is allowed only a single `__root` file per folder).
|
||||||
|
|
||||||
|
This file won't generate any route.
|
||||||
|
|
||||||
|
### Layout component for all routes
|
||||||
|
|
||||||
|
A file created in this location will wrap all project route.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// src/routes/__root.tsx
|
||||||
|
export default function RootLayout({ children }) {
|
||||||
|
return <main>{children}</main>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Layout component for specific routes
|
||||||
|
|
||||||
|
A file created in this location will wrap only the routes defined within the `src/routes/posts` folder.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// src/routes/posts/__root.tsx
|
||||||
|
export default function PostRootLayout({ children }) {
|
||||||
|
return <article>{children}</article>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Referring to the two examples above consider that:
|
||||||
|
|
||||||
|
- `/posts/examples-post` → will be wrapped by:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<RootLayout>
|
||||||
|
<PostRootLayout>
|
||||||
|
<ExamplePost />
|
||||||
|
</PostRootLayout>
|
||||||
|
</RootLayout>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `/about` → will be wrapped by:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<RootLayout>
|
||||||
|
<About />
|
||||||
|
</RootLayout>
|
||||||
|
```
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Routing"
|
||||||
|
canonical="https://tuono.dev/documentation/routing/dynamic-routes"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Dynamic routes' }]} />
|
||||||
|
|
||||||
|
# Dynamic routes
|
||||||
|
|
||||||
|
> 🚧 Only available for SSR apps. This feature will be soon available also for static generated apps.
|
||||||
|
|
||||||
|
When the exact segment names are not known in advance and you need to generate routes from dynamic data,
|
||||||
|
you can use dynamic segments, which are populated at request time.
|
||||||
|
|
||||||
|
## Convention
|
||||||
|
|
||||||
|
A dynamic segment can be created by wrapping a
|
||||||
|
file or a folder name in square brackets: `[segmentName]`.
|
||||||
|
|
||||||
|
The dynamic segment can be used in folders as well as file names:
|
||||||
|
|
||||||
|
- `src/routes/blog/[slug].tsx`
|
||||||
|
- `src/routes/blog/[slug]/index.tsx`
|
||||||
|
|
||||||
|
A path can contain multiple dynamic segments, allowing for more flexible routing:
|
||||||
|
|
||||||
|
- `src/routes/blog/[post]/[comment].tsx`
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Assuming you're working on a blog with multiple posts,
|
||||||
|
you can use a dynamic segment to define the post page: `src/routes/blog/[slug].tsx`.
|
||||||
|
|
||||||
|
The `[slug]` is the dynamic segment which will be used to identify individual blog posts.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useRouter } from 'tuono'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const router = useRouter()
|
||||||
|
return <p>Post: {router.pathname}</p>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Catch all segments
|
||||||
|
|
||||||
|
Dynamic Segments can be extended to catch-all subsequent segments by adding
|
||||||
|
an ellipsis inside the brackets `[...segmentName]`.
|
||||||
|
|
||||||
|
Given the following `src/routes/shop/[...slug].tsx`,
|
||||||
|
the following paths will be matched:
|
||||||
|
|
||||||
|
- `/shop/clothes`
|
||||||
|
- `/shop/clothes/tops`
|
||||||
|
- `/shop`
|
||||||
|
- `/shop/clothes/tops/t-shirts`
|
||||||
|
- and so on.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import MetaTags from '@/components/meta-tags'
|
||||||
|
|
||||||
|
<MetaTags
|
||||||
|
title="Tuono - Routing"
|
||||||
|
canonical="https://tuono.dev/documentation/routing/intro"
|
||||||
|
/>
|
||||||
|
|
||||||
|
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
|
||||||
|
|
||||||
|
<Breadcrumbs breadcrumbs={[{ label: 'Routing' }]} />
|
||||||
|
|
||||||
|
# Routing index
|
||||||
|
|
||||||
|
TODO
|
||||||