Compare commits

..

2 Commits

Author SHA1 Message Date
virgile-deville
faf38de099 📝(readme) add changelog entry
Added changelog entry so the pr passes the test
2025-02-23 11:48:54 +01:00
virgile-deville
c120ad4b84 📝(readme) move front-end local run instructions
New comers should only see the main info.
I removed the special commands from the readme.
And moved them to the /docs/local.md
2025-02-21 15:21:21 +01:00
56 changed files with 338 additions and 1607 deletions

View File

@@ -8,16 +8,12 @@ and this project adheres to
## [Unreleased]
## Added
- 💄(frontend) add error pages #643
## Changed
- 🛂(frontend) Restore version visibility #629
- 📝(doc) minor README.md formatting and wording enhancements
-Stop setting a default title on doc creation #634
- ♻️(frontend) misc ui improvements #644
- 📝(readme) remove front-end local run instructions local.md #651
## Fixed

View File

@@ -40,7 +40,7 @@ Docs is a collaborative text editor designed to address common challenges in kno
* 📚 Built-in wiki functionality to turn your team's collaborative work into organized knowledge `ETA 02/2025`
### Self-host
* 🚀 Easy to install, scalable and secure alternative to Notion, Outline or Confluence
* 🚀 Easy to install, scalable and secure alternative to Notion and Outline.
## Getting started 🔧
@@ -100,26 +100,6 @@ password: impress
$ make run
```
⚠️ For the frontend developer, it is often better to run the frontend in development mode locally.
To do so, install the frontend dependencies with the following command:
```shellscript
$ make frontend-development-install
```
And run the frontend locally in development mode with the following command:
```shellscript
$ make run-frontend-development
```
To start all the services, except the frontend container, you can use the following command:
```shellscript
$ make run-backend
```
**Adding content**
You can create a basic demo site by running:

92
docs/local.md Normal file
View File

@@ -0,0 +1,92 @@
# Run Docs locally
> ⚠️ Running Docs locally using the methods described below is for testing purposes only. It is based on building Docs using Minio as the S3 storage solution: if you want to use Minio for production deployment of Docs, you will need to comply with Minio's AGPL-3.0 licence.
**Prerequisite**
Make sure you have a recent version of Docker and [Docker Compose](https://docs.docker.com/compose/install) installed on your laptop:
```shellscript
$ docker -v
Docker version 20.10.2, build 2291f61
$ docker compose version
Docker Compose version v2.32.4
```
> ⚠️ You may need to run the following commands with sudo but this can be avoided by adding your user to the `docker` group.
**Project bootstrap**
The easiest way to start working on the project is to use GNU Make:
```shellscript
$ make bootstrap FLUSH_ARGS='--no-input'
```
This command builds the `app` container, installs dependencies, performs database migrations and compile translations. It's a good idea to use this command each time you are pulling code from the project repository to avoid dependency-related or migration-related issues.
Your Docker services should now be up and running 🎉
You can access to the project by going to <http://localhost:3000>.
You will be prompted to log in, the default credentials are:
```
username: impress
password: impress
```
📝 Note that if you need to run them afterwards, you can use the eponym Make rule:
```shellscript
$ make run
```
**Adding content**
You can create a basic demo site by running:
```shellscript
$ make demo
```
Finally, you can check all available Make rules using:
```shellscript
$ make help
```
**Django admin**
You can access the Django admin site at
<http://localhost:8071/admin>.
You first need to create a superuser account:
```shellscript
$ make superuser
```
## Front-end dev instructions
⚠️ For the frontend developer, it is often better to run the frontend in development mode locally.
To do so, install the frontend dependencies with the following command:
```shellscript
$ make frontend-development-install
```
And run the frontend locally in development mode with the following command:
```shellscript
$ make run-frontend-development
```
To start all the services, except the frontend container, you can use the following command:
```shellscript
$ make run-backend
```

745
package-lock.json generated
View File

@@ -1,745 +0,0 @@
{
"name": "docs",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@ag-media/react-pdf-table": "^2.0.1"
}
},
"node_modules/@ag-media/react-pdf-table": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@ag-media/react-pdf-table/-/react-pdf-table-2.0.1.tgz",
"integrity": "sha512-UMNdGYAfuI6L1wLRziYmwcp/8I2JgbwX+PY7bHXGb2+P6MwgFJH8W71qZO1bxfxrmVUTP8YblQwl1PkXG2m6Rg==",
"license": "MIT",
"peerDependencies": {
"@react-pdf/renderer": "^2.0.2 || ^3.0.0 || ^4.0.0",
"@react-pdf/stylesheet": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
"react": "^16.8.6 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==",
"license": "MIT",
"peer": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@react-pdf/fns": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.1.tgz",
"integrity": "sha512-fYvgOWWRxTdkCciLSla2iek8W/oDLhExPTLPw3aArGPJHgVUc86V2c3YLULNHIBuy/64QVpPLB7gwNkTEW5m/A==",
"license": "MIT",
"peer": true
},
"node_modules/@react-pdf/font": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-3.1.0.tgz",
"integrity": "sha512-5q+r3DhZK41gVZp2Uw5M69FEVWeoasnM/HscW3kdpYnwjcB2bhCRWmBGCjm8fmuwQstwNPM1ZxyCWZRTRchwnA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/types": "^2.8.0",
"fontkit": "^2.0.2",
"is-url": "^1.2.4"
}
},
"node_modules/@react-pdf/image": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.2.tgz",
"integrity": "sha512-GrlApNDxLdFKN1ia+nt1svrnpBJIwf2ncK4Km/hQzAkbALn0HQ5YVrOEtMpnp/c0L0o9zO4hSoPL9iEQne5vzw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/png-js": "^3.0.0",
"jay-peg": "^1.1.1"
}
},
"node_modules/@react-pdf/layout": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.2.3.tgz",
"integrity": "sha512-sSL14ki0nC8YwSrjkOzKI1ZV4xZC68v/wA5EFWW6IhmJ3qjX4+KbNdprWBCtih/Xbq2Kt9K1erZpKFiWoVf5/Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@react-pdf/fns": "3.1.1",
"@react-pdf/image": "^3.0.2",
"@react-pdf/pdfkit": "^4.0.2",
"@react-pdf/primitives": "^4.1.1",
"@react-pdf/stylesheet": "^6.0.0",
"@react-pdf/textkit": "^5.0.3",
"@react-pdf/types": "^2.8.0",
"emoji-regex": "^10.3.0",
"queue": "^6.0.1",
"yoga-layout": "^3.2.1"
}
},
"node_modules/@react-pdf/layout/node_modules/@react-pdf/stylesheet": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.0.0.tgz",
"integrity": "sha512-uAwuMjbcEaxhRl7tGlqxAbLzo/KoYr6v9JksUJwgzd+rkvAp8jDq8NcG3sUp88tzgIyyRjBGl4FewgdxbAa2uw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/fns": "3.1.1",
"@react-pdf/types": "^2.8.0",
"color-string": "^1.9.1",
"hsl-to-hex": "^1.0.0",
"media-engine": "^1.0.3",
"postcss-value-parser": "^4.1.0"
}
},
"node_modules/@react-pdf/pdfkit": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.2.tgz",
"integrity": "sha512-pyYFAI7YL5Oud60W+wcu9zsN73tg8XgHGtEM8FQ6PY4RgEKp+AXkj+YE2hKuX3eOVB65MPzbJbVWtTjO3MPa5g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@react-pdf/png-js": "^3.0.0",
"browserify-zlib": "^0.2.0",
"crypto-js": "^4.2.0",
"fontkit": "^2.0.2",
"jay-peg": "^1.1.1",
"linebreak": "^1.1.0",
"vite-compatible-readable-stream": "^3.6.1"
}
},
"node_modules/@react-pdf/png-js": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz",
"integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==",
"license": "MIT",
"peer": true,
"dependencies": {
"browserify-zlib": "^0.2.0"
}
},
"node_modules/@react-pdf/primitives": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz",
"integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==",
"license": "MIT",
"peer": true
},
"node_modules/@react-pdf/reconciler": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.3.tgz",
"integrity": "sha512-4vqY0klmUH32kTFvuqdAszkOpwfZYKMLO4VpJ5xZWTsoUOLQSyhC2QM2QCj9eaxpB2Nd5Kl9uW+KfyutvZnMzQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"object-assign": "^4.1.1",
"scheduler": "0.25.0-rc-603e6108-20241029"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@react-pdf/reconciler/node_modules/scheduler": {
"version": "0.25.0-rc-603e6108-20241029",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
"integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==",
"license": "MIT",
"peer": true
},
"node_modules/@react-pdf/render": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.1.2.tgz",
"integrity": "sha512-x9R7yaU/EisU2loWLAeVZqUEhkPR1EDa4CXM6PPiPhB2hTZAXgqeZCTVOODX0iGkUBM3scOjzrf5gPPnoMf0jg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@react-pdf/fns": "3.1.1",
"@react-pdf/primitives": "^4.1.1",
"@react-pdf/textkit": "^5.0.3",
"@react-pdf/types": "^2.8.0",
"abs-svg-path": "^0.1.1",
"color-string": "^1.9.1",
"normalize-svg-path": "^1.1.0",
"parse-svg-path": "^0.1.2",
"svg-arc-to-cubic-bezier": "^3.2.0"
}
},
"node_modules/@react-pdf/renderer": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.2.2.tgz",
"integrity": "sha512-ldWT9Mi+Ie50oqH0NxYZ1UsnZF7BmhoUiI9GMyXBxMvaiG4lIGR5ki8KWLmA6Ti6+Yp7jXNWg0sYDrvZRLiLjg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@react-pdf/fns": "3.1.1",
"@react-pdf/font": "^3.1.0",
"@react-pdf/layout": "^4.2.3",
"@react-pdf/pdfkit": "^4.0.2",
"@react-pdf/primitives": "^4.1.1",
"@react-pdf/reconciler": "^1.1.3",
"@react-pdf/render": "^4.1.2",
"@react-pdf/types": "^2.8.0",
"events": "^3.3.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"queue": "^6.0.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@react-pdf/stylesheet": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-5.2.2.tgz",
"integrity": "sha512-oHP+hZakETrecnZCSRPqNvFhSyBgoZSDOkonY9WJOxRkUb6P6A+mAVSOWBaNt2eM4FHMDpYDeR9stx+gAWn6gg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@react-pdf/fns": "3.1.0",
"@react-pdf/types": "^2.7.1",
"color-string": "^1.9.1",
"hsl-to-hex": "^1.0.0",
"media-engine": "^1.0.3",
"postcss-value-parser": "^4.1.0"
}
},
"node_modules/@react-pdf/stylesheet/node_modules/@react-pdf/fns": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.0.tgz",
"integrity": "sha512-BjT7C/IeYlrF4Pevlrlo+fILhSxsWSm6Ka/rQrQzYsyQuOsqI6bmBzsTW+T6ghqrD5HLRKr1n8vjAaE9g4rFhA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13"
}
},
"node_modules/@react-pdf/textkit": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-5.0.3.tgz",
"integrity": "sha512-gRQBw2lOlGl/gZR2O9Joxu3TqlP0u3wy8KVMw3R6glqDSrgLH43cNfdOWIchNvL6adRIjxd8l/FCv2u7zcHqOQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/fns": "3.1.1",
"bidi-js": "^1.0.2",
"hyphen": "^1.6.4",
"unicode-properties": "^1.4.1"
}
},
"node_modules/@react-pdf/types": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.8.0.tgz",
"integrity": "sha512-lBnLonM2GupyTzUGlWTEoUUGvsRcgbWLn0Py3i3lK/tgn2rPCYwJ9gQ5A3warT5g4jQWyc7HmaNoPU/Zy5iBbQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/font": "^3.1.0",
"@react-pdf/primitives": "^4.1.1",
"@react-pdf/stylesheet": "^6.0.0"
}
},
"node_modules/@react-pdf/types/node_modules/@react-pdf/stylesheet": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.0.0.tgz",
"integrity": "sha512-uAwuMjbcEaxhRl7tGlqxAbLzo/KoYr6v9JksUJwgzd+rkvAp8jDq8NcG3sUp88tzgIyyRjBGl4FewgdxbAa2uw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-pdf/fns": "3.1.1",
"@react-pdf/types": "^2.8.0",
"color-string": "^1.9.1",
"hsl-to-hex": "^1.0.0",
"media-engine": "^1.0.3",
"postcss-value-parser": "^4.1.0"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/abs-svg-path": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
"integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==",
"license": "MIT",
"peer": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true
},
"node_modules/bidi-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
"license": "MIT",
"peer": true,
"dependencies": {
"require-from-string": "^2.0.2"
}
},
"node_modules/brotli": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
"license": "MIT",
"peer": true,
"dependencies": {
"base64-js": "^1.1.2"
}
},
"node_modules/browserify-zlib": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
"license": "MIT",
"peer": true,
"dependencies": {
"pako": "~1.0.5"
}
},
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.8"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT",
"peer": true
},
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"license": "MIT",
"peer": true,
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT",
"peer": true
},
"node_modules/dfa": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
"license": "MIT",
"peer": true
},
"node_modules/emoji-regex": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
"license": "MIT",
"peer": true
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT",
"peer": true
},
"node_modules/fontkit": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@swc/helpers": "^0.5.12",
"brotli": "^1.3.2",
"clone": "^2.1.2",
"dfa": "^1.2.0",
"fast-deep-equal": "^3.1.3",
"restructure": "^3.0.0",
"tiny-inflate": "^1.0.3",
"unicode-properties": "^1.4.0",
"unicode-trie": "^2.0.0"
}
},
"node_modules/hsl-to-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
"integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
"license": "MIT",
"peer": true,
"dependencies": {
"hsl-to-rgb-for-reals": "^1.1.0"
}
},
"node_modules/hsl-to-rgb-for-reals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
"license": "ISC",
"peer": true
},
"node_modules/hyphen": {
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz",
"integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==",
"license": "ISC",
"peer": true
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC",
"peer": true
},
"node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT",
"peer": true
},
"node_modules/is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
"license": "MIT",
"peer": true
},
"node_modules/jay-peg": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
"integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
"license": "MIT",
"peer": true,
"dependencies": {
"restructure": "^3.0.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT",
"peer": true
},
"node_modules/linebreak": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"base64-js": "0.0.8",
"unicode-trie": "^2.0.0"
}
},
"node_modules/linebreak/node_modules/base64-js": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/media-engine": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
"integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
"license": "MIT",
"peer": true
},
"node_modules/normalize-svg-path": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
"integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
"license": "MIT",
"peer": true,
"dependencies": {
"svg-arc-to-cubic-bezier": "^3.0.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)",
"peer": true
},
"node_modules/parse-svg-path": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
"integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
"license": "MIT",
"peer": true
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT",
"peer": true
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/queue": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
"license": "MIT",
"peer": true,
"dependencies": {
"inherits": "~2.0.3"
}
},
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT",
"peer": true
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT",
"peer": true
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/restructure": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
"license": "MIT",
"peer": true
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"license": "MIT",
"peer": true,
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/svg-arc-to-cubic-bezier": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
"license": "ISC",
"peer": true
},
"node_modules/tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"license": "MIT",
"peer": true
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD",
"peer": true
},
"node_modules/unicode-properties": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
"license": "MIT",
"peer": true,
"dependencies": {
"base64-js": "^1.3.0",
"unicode-trie": "^2.0.0"
}
},
"node_modules/unicode-trie": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"pako": "^0.2.5",
"tiny-inflate": "^1.0.0"
}
},
"node_modules/unicode-trie/node_modules/pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
"license": "MIT",
"peer": true
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT",
"peer": true
},
"node_modules/vite-compatible-readable-stream": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
"integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/yoga-layout": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
"license": "MIT",
"peer": true
}
}
}

View File

@@ -1,5 +0,0 @@
{
"dependencies": {
"@ag-media/react-pdf-table": "^2.0.1"
}
}

314
q
View File

@@ -1,314 +0,0 @@
SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
Commands marked with * may be preceded by a number, _N.
Notes in parentheses indicate the behavior if _N is given.
A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
h H Display this help.
q :q Q :Q ZZ Exit.
---------------------------------------------------------------------------
MMOOVVIINNGG
e ^E j ^N CR * Forward one line (or _N lines).
y ^Y k ^K ^P * Backward one line (or _N lines).
f ^F ^V SPACE * Forward one window (or _N lines).
b ^B ESC-v * Backward one window (or _N lines).
z * Forward one window (and set window to _N).
w * Backward one window (and set window to _N).
ESC-SPACE * Forward one window, but don't stop at end-of-file.
d ^D * Forward one half-window (and set half-window to _N).
u ^U * Backward one half-window (and set half-window to _N).
ESC-) RightArrow * Right one half screen width (or _N positions).
ESC-( LeftArrow * Left one half screen width (or _N positions).
ESC-} ^RightArrow Right to last column displayed.
ESC-{ ^LeftArrow Left to first column.
F Forward forever; like "tail -f".
ESC-F Like F but stop when search pattern is found.
r ^R ^L Repaint screen.
R Repaint screen, discarding buffered input.
---------------------------------------------------
Default "window" is the screen height.
Default "half-window" is half of the screen height.
---------------------------------------------------------------------------
SSEEAARRCCHHIINNGG
/_p_a_t_t_e_r_n * Search forward for (_N-th) matching line.
?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line.
n * Repeat previous search (for _N-th occurrence).
N * Repeat previous search in reverse direction.
ESC-n * Repeat previous search, spanning files.
ESC-N * Repeat previous search, reverse dir. & spanning files.
^O^N ^On * Search forward for (_N-th) OSC8 hyperlink.
^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink.
^O^L ^Ol Jump to the currently selected OSC8 hyperlink.
ESC-u Undo (toggle) search highlighting.
ESC-U Clear search highlighting.
&_p_a_t_t_e_r_n * Display only matching lines.
---------------------------------------------------
A search pattern may begin with one or more of:
^N or ! Search for NON-matching lines.
^E or * Search multiple files (pass thru END OF FILE).
^F or @ Start search at FIRST file (for /) or last file (for ?).
^K Highlight matches, but don't move (KEEP position).
^R Don't use REGULAR EXPRESSIONS.
^S _n Search for match in _n-th parenthesized subpattern.
^W WRAP search if no match found.
^L Enter next character literally into pattern.
---------------------------------------------------------------------------
JJUUMMPPIINNGG
g < ESC-< * Go to first line in file (or line _N).
G > ESC-> * Go to last line in file (or line _N).
p % * Go to beginning of file (or _N percent into file).
t * Go to the (_N-th) next tag.
T * Go to the (_N-th) previous tag.
{ ( [ * Find close bracket } ) ].
} ) ] * Find open bracket { ( [.
ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>.
ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>.
---------------------------------------------------
Each "find close bracket" command goes forward to the close bracket
matching the (_N-th) open bracket in the top line.
Each "find open bracket" command goes backward to the open bracket
matching the (_N-th) close bracket in the bottom line.
m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
'_<_l_e_t_t_e_r_> Go to a previously marked position.
'' Go to the previous position.
^X^X Same as '.
ESC-m_<_l_e_t_t_e_r_> Clear a mark.
---------------------------------------------------
A mark is any upper-case or lower-case letter.
Certain marks are predefined:
^ means beginning of the file
$ means end of the file
---------------------------------------------------------------------------
CCHHAANNGGIINNGG FFIILLEESS
:e [_f_i_l_e] Examine a new file.
^X^V Same as :e.
:n * Examine the (_N-th) next file from the command line.
:p * Examine the (_N-th) previous file from the command line.
:x * Examine the first (or _N-th) file from the command line.
^O^O Open the currently selected OSC8 hyperlink.
:d Delete the current file from the command line list.
= ^G :f Print current file name.
---------------------------------------------------------------------------
MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS
-_<_f_l_a_g_> Toggle a command line option [see OPTIONS below].
--_<_n_a_m_e_> Toggle a command line option, by name.
__<_f_l_a_g_> Display the setting of a command line option.
___<_n_a_m_e_> Display the setting of an option, by name.
+_c_m_d Execute the less cmd each time a new file is examined.
!_c_o_m_m_a_n_d Execute the shell command with $SHELL.
#_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt.
|XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command.
s _f_i_l_e Save input to a file.
v Edit the current file with $VISUAL or $EDITOR.
V Print version number of "less".
---------------------------------------------------------------------------
OOPPTTIIOONNSS
Most options may be changed either on the command line,
or from within less by using the - or -- command.
Options may be given in one of two forms: either a single
character preceded by a -, or a name preceded by --.
-? ........ --help
Display help (from command line).
-a ........ --search-skip-screen
Search skips current screen.
-A ........ --SEARCH-SKIP-SCREEN
Search starts just after target line.
-b [_N] .... --buffers=[_N]
Number of buffers.
-B ........ --auto-buffers
Don't automatically allocate buffers for pipes.
-c ........ --clear-screen
Repaint by clearing rather than scrolling.
-d ........ --dumb
Dumb terminal.
-D xx_c_o_l_o_r . --color=xx_c_o_l_o_r
Set screen colors.
-e -E .... --quit-at-eof --QUIT-AT-EOF
Quit at end of file.
-f ........ --force
Force open non-regular files.
-F ........ --quit-if-one-screen
Quit if entire file fits on first screen.
-g ........ --hilite-search
Highlight only last match for searches.
-G ........ --HILITE-SEARCH
Don't highlight any matches for searches.
-h [_N] .... --max-back-scroll=[_N]
Backward scroll limit.
-i ........ --ignore-case
Ignore case in searches that do not contain uppercase.
-I ........ --IGNORE-CASE
Ignore case in all searches.
-j [_N] .... --jump-target=[_N]
Screen position of target lines.
-J ........ --status-column
Display a status column at left edge of screen.
-k _f_i_l_e ... --lesskey-file=_f_i_l_e
Use a compiled lesskey file.
-K ........ --quit-on-intr
Exit less in response to ctrl-C.
-L ........ --no-lessopen
Ignore the LESSOPEN environment variable.
-m -M .... --long-prompt --LONG-PROMPT
Set prompt style.
-n ......... --line-numbers
Suppress line numbers in prompts and messages.
-N ......... --LINE-NUMBERS
Display line number at start of each line.
-o [_f_i_l_e] .. --log-file=[_f_i_l_e]
Copy to log file (standard input only).
-O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e]
Copy to log file (unconditionally overwrite).
-p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n]
Start at pattern (from command line).
-P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t]
Define new prompt.
-q -Q .... --quiet --QUIET --silent --SILENT
Quiet the terminal bell.
-r -R .... --raw-control-chars --RAW-CONTROL-CHARS
Output "raw" control characters.
-s ........ --squeeze-blank-lines
Squeeze multiple blank lines.
-S ........ --chop-long-lines
Chop (truncate) long lines rather than wrapping.
-t _t_a_g .... --tag=[_t_a_g]
Find a tag.
-T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e]
Use an alternate tags file.
-u -U .... --underline-special --UNDERLINE-SPECIAL
Change handling of backspaces, tabs and carriage returns.
-V ........ --version
Display the version number of "less".
-w ........ --hilite-unread
Highlight first new line after forward-screen.
-W ........ --HILITE-UNREAD
Highlight first new line after any forward movement.
-x [_N[,...]] --tabs=[_N[,...]]
Set tab stops.
-X ........ --no-init
Don't use termcap init/deinit strings.
-y [_N] .... --max-forw-scroll=[_N]
Forward scroll limit.
-z [_N] .... --window=[_N]
Set size of window.
-" [_c[_c]] . --quotes=[_c[_c]]
Set shell quote characters.
-~ ........ --tilde
Don't display tildes after end of file.
-# [_N] .... --shift=[_N]
Set horizontal scroll amount (0 = one half screen width).
--exit-follow-on-close
Exit F command on a pipe when writer closes pipe.
--file-size
Automatically determine the size of the input file.
--follow-name
The F command changes files if the input file is renamed.
--header=[_L[,_C[,_N]]]
Use _L lines (starting at line _N) and _C columns as headers.
--incsearch
Search file as each pattern character is typed in.
--intr=[_C]
Use _C instead of ^X to interrupt a read.
--lesskey-context=_t_e_x_t
Use lesskey source file contents.
--lesskey-src=_f_i_l_e
Use a lesskey source file.
--line-num-width=[_N]
Set the width of the -N line number field to _N characters.
--match-shift=[_N]
Show at least _N characters to the left of a search match.
--modelines=[_N]
Read _N lines from the input file and look for vim modelines.
--mouse
Enable mouse input.
--no-keypad
Don't send termcap keypad init/deinit strings.
--no-histdups
Remove duplicates from command history.
--no-number-headers
Don't give line numbers to header lines.
--no-search-header-lines
Searches do not include header lines.
--no-search-header-columns
Searches do not include header columns.
--no-search-headers
Searches do not include header lines or columns.
--no-vbell
Disable the terminal's visual bell.
--redraw-on-quit
Redraw final screen when quitting.
--rscroll=[_C]
Set the character used to mark truncated lines.
--save-marks
Retain marks across invocations of less.
--search-options=[EFKNRW-]
Set default options for every search.
--show-preproc-errors
Display a message if preprocessor exits with an error status.
--proc-backspace
Process backspaces for bold/underline.
--PROC-BACKSPACE
Treat backspaces as control characters.
--proc-return
Delete carriage returns before newline.
--PROC-RETURN
Treat carriage returns as control characters.
--proc-tab
Expand tabs to spaces.
--PROC-TAB
Treat tabs as control characters.
--status-col-width=[_N]
Set the width of the -J status column to _N characters.
--status-line
Highlight or color the entire line containing a mark.
--use-backslash
Subsequent options use backslash as escape char.
--use-color
Enables colored text.
--wheel-lines=[_N]
Each click of the mouse wheel moves _N lines.
--wordwrap
Wrap lines at spaces.
---------------------------------------------------------------------------
LLIINNEE EEDDIITTIINNGG
These keys can be used to edit text being entered
on the "command line" at the bottom of the screen.
RightArrow ..................... ESC-l ... Move cursor right one character.
LeftArrow ...................... ESC-h ... Move cursor left one character.
ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
HOME ........................... ESC-0 ... Move cursor to start of line.
END ............................ ESC-$ ... Move cursor to end of line.
BACKSPACE ................................ Delete char to left of cursor.
DELETE ......................... ESC-x ... Delete char under cursor.
ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
UpArrow ........................ ESC-k ... Retrieve previous command line.
DownArrow ...................... ESC-j ... Retrieve next command line.
TAB ...................................... Complete filename & cycle.
SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
ctrl-L ................................... Complete filename, list all.

View File

@@ -17,13 +17,13 @@ test.describe('404', () => {
'It seems that the page you are looking for does not exist or cannot be displayed correctly.',
),
).toBeVisible();
await expect(page.getByText('Home')).toBeVisible();
await expect(page.getByText('Back to home page')).toBeVisible();
});
test('checks go back to home page redirects to home page', async ({
page,
}) => {
await page.getByText('Home').click();
await page.getByText('Back to home page').click();
await expect(page).toHaveURL('/');
});
});

View File

@@ -97,7 +97,7 @@ export const addNewMember = async (
// Choose a role
await page.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: role }).click();
await page.getByRole('button', { name: role }).click();
await page.getByRole('button', { name: 'Invite' }).click();
return users[index].email;

View File

@@ -37,10 +37,10 @@ test.describe('Doc Editor', () => {
// Change language to French
await header.click();
await header.getByRole('button', { name: /Language/ }).click();
await page.getByRole('menuitem', { name: 'Français' }).click();
await header.getByRole('combobox').getByText('English').click();
await header.getByRole('option', { name: 'Français' }).click();
await expect(
header.getByRole('button').getByText('Français'),
header.getByRole('combobox').getByText('Français'),
).toBeVisible();
// Trigger slash menu to show french menu
@@ -130,7 +130,7 @@ test.describe('Doc Editor', () => {
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Connected',
})
.click();

View File

@@ -7,9 +7,17 @@ type SmallDoc = {
title: string;
};
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Documents Grid mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('it checks the grid when mobile', async ({ page }) => {
await page.route('**/documents/**', async (route) => {
const request = route.request();
@@ -86,10 +94,6 @@ test.describe('Documents Grid mobile', () => {
});
test.describe('Document grid item options', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('it pins a document', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, `Favorite doc`, browserName);
@@ -208,8 +212,6 @@ test.describe('Document grid item options', () => {
test.describe('Documents filters', () => {
test('it checks the prebuild left panel filters', async ({ page }) => {
await page.goto('/');
// All Docs
const response = await page.waitForResponse(
(response) =>
@@ -280,9 +282,11 @@ test.describe('Documents filters', () => {
});
test.describe('Documents Grid', () => {
test('checks all the elements are visible', async ({ page }) => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('checks all the elements are visible', async ({ page }) => {
let docs: SmallDoc[] = [];
const response = await page.waitForResponse(
(response) =>
@@ -310,12 +314,11 @@ test.describe('Documents Grid', () => {
test('checks the infinite scroll', async ({ page }) => {
let docs: SmallDoc[] = [];
const responsePromisePage1 = page.waitForResponse((response) => {
return (
const responsePromisePage1 = page.waitForResponse(
(response) =>
response.url().endsWith(`/documents/?page=1`) &&
response.status() === 200
);
});
response.status() === 200,
);
const responsePromisePage2 = page.waitForResponse(
(response) =>
@@ -323,8 +326,6 @@ test.describe('Documents Grid', () => {
response.status() === 200,
);
await page.goto('/');
const responsePage1 = await responsePromisePage1;
expect(responsePage1.ok()).toBeTruthy();
let result = await responsePage1.json();

View File

@@ -89,7 +89,7 @@ test.describe('Doc Header', () => {
await page.getByLabel('Open the document options').click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Delete document',
})
.click();
@@ -153,7 +153,7 @@ test.describe('Doc Header', () => {
await page.getByLabel('Open the document options').click();
await expect(
page.getByRole('menuitem', { name: 'Delete document' }),
page.getByRole('button', { name: 'Delete document' }),
).toBeDisabled();
// Click somewhere else to close the options
@@ -177,7 +177,7 @@ test.describe('Doc Header', () => {
await invitationCard.getByRole('button', { name: 'more_horiz' }).click();
await expect(
page.getByRole('menuitem', {
page.getByRole('button', {
name: 'delete',
}),
).toBeEnabled();
@@ -195,7 +195,7 @@ test.describe('Doc Header', () => {
await memberCard.getByRole('button', { name: 'more_horiz' }).click();
await expect(
page.getByRole('menuitem', {
page.getByRole('button', {
name: 'delete',
}),
).toBeEnabled();
@@ -233,7 +233,7 @@ test.describe('Doc Header', () => {
await page.getByLabel('Open the document options').click();
await expect(
page.getByRole('menuitem', { name: 'Delete document' }),
page.getByRole('button', { name: 'Delete document' }),
).toBeDisabled();
// Click somewhere else to close the options
@@ -295,7 +295,7 @@ test.describe('Doc Header', () => {
await page.getByLabel('Open the document options').click();
await expect(
page.getByRole('menuitem', { name: 'Delete document' }),
page.getByRole('button', { name: 'Delete document' }),
).toBeDisabled();
// Click somewhere else to close the options
@@ -352,7 +352,7 @@ test.describe('Doc Header', () => {
// Copy content to clipboard
await page.getByLabel('Open the document options').click();
await page.getByRole('menuitem', { name: 'Copy as Markdown' }).click();
await page.getByRole('button', { name: 'Copy as Markdown' }).click();
await expect(page.getByText('Copied to clipboard')).toBeVisible();
// Test that clipboard is in Markdown format
@@ -387,7 +387,7 @@ test.describe('Doc Header', () => {
// Copy content to clipboard
await page.getByLabel('Open the document options').click();
await page.getByRole('menuitem', { name: 'Copy as HTML' }).click();
await page.getByRole('button', { name: 'Copy as HTML' }).click();
await expect(page.getByText('Copied to clipboard')).toBeVisible();
// Test that clipboard is in HTML format
@@ -460,7 +460,7 @@ test.describe('Documents Header mobile', () => {
await expect(page.getByRole('button', { name: 'Copy link' })).toBeHidden();
await page.getByLabel('Open the document options').click();
await page.getByRole('menuitem', { name: 'Share' }).click();
await page.getByRole('button', { name: 'Share' }).click();
await page.getByRole('button', { name: 'Copy link' }).click();
await expect(page.getByText('Link Copied !')).toBeVisible();
// Test that clipboard is in HTML format
@@ -494,7 +494,7 @@ test.describe('Documents Header mobile', () => {
await goToGridDoc(page);
await page.getByLabel('Open the document options').click();
await page.getByRole('menuitem', { name: 'Share' }).click();
await page.getByRole('button', { name: 'Share' }).click();
await expect(page.getByLabel('Share modal')).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();

View File

@@ -65,15 +65,15 @@ test.describe('Document create member', () => {
// Check roles are displayed
await list.getByLabel('doc-role-dropdown').click();
await expect(page.getByRole('menuitem', { name: 'Reader' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Editor' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Owner' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Reader' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Editor' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Owner' })).toBeVisible();
await expect(
page.getByRole('menuitem', { name: 'Administrator' }),
page.getByRole('button', { name: 'Administrator' }),
).toBeVisible();
// Validate
await page.getByRole('menuitem', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Invite' }).click();
// Check invitation added
@@ -121,7 +121,7 @@ test.describe('Document create member', () => {
// Choose a role
const container = page.getByTestId('doc-share-add-member-list');
await container.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Owner' }).click();
await page.getByRole('button', { name: 'Owner' }).click();
const responsePromiseCreateInvitation = page.waitForResponse(
(response) =>
@@ -139,7 +139,7 @@ test.describe('Document create member', () => {
// Choose a role
await container.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Owner' }).click();
await page.getByRole('button', { name: 'Owner' }).click();
const responsePromiseCreateInvitationFail = page.waitForResponse(
(response) =>
@@ -162,8 +162,8 @@ test.describe('Document create member', () => {
await createDoc(page, 'user-invitation', browserName, 1);
const header = page.locator('header').first();
await header.getByRole('button', { name: /Language/ }).click();
await page.getByRole('menuitem', { name: 'Français' }).click();
await header.getByRole('combobox').getByText('EN').click();
await header.getByRole('option', { name: 'translate Français' }).click();
await page.getByRole('button', { name: 'Partager' }).click();
@@ -178,7 +178,7 @@ test.describe('Document create member', () => {
// Choose a role
const container = page.getByTestId('doc-share-add-member-list');
await container.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Administrateur' }).click();
await page.getByRole('button', { name: 'Administrateur' }).click();
const responsePromiseCreateInvitation = page.waitForResponse(
(response) =>
@@ -212,7 +212,7 @@ test.describe('Document create member', () => {
// Choose a role
const container = page.getByTestId('doc-share-add-member-list');
await container.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Administrator' }).click();
const responsePromiseCreateInvitation = page.waitForResponse(
(response) =>
@@ -232,14 +232,14 @@ test.describe('Document create member', () => {
await expect(userInvitation).toBeVisible();
await userInvitation.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Reader' }).click();
await page.getByRole('button', { name: 'Reader' }).click();
const moreActions = userInvitation.getByRole('button', {
name: 'more_horiz',
});
await moreActions.click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'Delete' }).click();
await expect(userInvitation).toBeHidden();
});

View File

@@ -161,12 +161,12 @@ test.describe('Document list members', () => {
await list.click();
await currentUserRole.click();
await page.getByRole('menuitem', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Administrator' }).click();
await list.click();
await expect(currentUserRole).toBeVisible();
await currentUserRole.click();
await page.getByRole('menuitem', { name: 'Reader' }).click();
await page.getByRole('button', { name: 'Reader' }).click();
await list.click();
await expect(currentUserRole).toBeHidden();
});
@@ -215,13 +215,13 @@ test.describe('Document list members', () => {
await expect(mySelfMoreActions).toBeVisible();
await userReaderMoreActions.click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'Delete' }).click();
await expect(userReader).toBeHidden();
await mySelfMoreActions.click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'Delete' }).click();
await expect(
page.getByText('You do not have permission to view this document.'),
page.getByText('You do not have permission to perform this action.'),
).toBeVisible();
});
});

View File

@@ -19,7 +19,7 @@ test.describe('Doc Version', () => {
await page.getByLabel('Open the document options').click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Version history',
})
.click();
@@ -59,7 +59,7 @@ test.describe('Doc Version', () => {
await page.getByLabel('Open the document options').click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Version history',
})
.click();
@@ -91,7 +91,7 @@ test.describe('Doc Version', () => {
await page.getByLabel('Open the document options').click();
await expect(
page.getByRole('menuitem', { name: 'Version history' }),
page.getByRole('button', { name: 'Version history' }),
).toBeDisabled();
});
@@ -120,7 +120,7 @@ test.describe('Doc Version', () => {
await page.getByLabel('Open the document options').click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Version history',
})
.click();

View File

@@ -50,7 +50,7 @@ test.describe('Doc Visibility', () => {
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Connected',
})
.click();
@@ -60,7 +60,7 @@ test.describe('Doc Visibility', () => {
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Public',
})
.click();
@@ -100,9 +100,7 @@ test.describe('Doc Visibility: Restricted', () => {
await page.goto(urlDoc);
await expect(
page.getByText('Log in to access the document.'),
).toBeVisible();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
test('A doc is not accessible when authentified but not member.', async ({
@@ -135,7 +133,7 @@ test.describe('Doc Visibility: Restricted', () => {
await page.goto(urlDoc);
await expect(
page.getByText('You do not have permission to view this document.'),
page.getByText('You do not have permission to perform this action.'),
).toBeVisible();
});
@@ -162,7 +160,7 @@ test.describe('Doc Visibility: Restricted', () => {
// Choose a role
const container = page.getByTestId('doc-share-add-member-list');
await container.getByLabel('doc-role-dropdown').click();
await page.getByRole('menuitem', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Invite' }).click();
@@ -215,7 +213,7 @@ test.describe('Doc Visibility: Public', () => {
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Public',
})
.click();
@@ -227,7 +225,7 @@ test.describe('Doc Visibility: Public', () => {
await expect(page.getByLabel('Visibility mode')).toBeVisible();
await page.getByLabel('Visibility mode').click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Reading',
})
.click();
@@ -289,7 +287,7 @@ test.describe('Doc Visibility: Public', () => {
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Public',
})
.click();
@@ -357,7 +355,7 @@ test.describe('Doc Visibility: Authenticated', () => {
const selectVisibility = page.getByLabel('Visibility', { exact: true });
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Connected',
})
.click();
@@ -381,10 +379,7 @@ test.describe('Doc Visibility: Authenticated', () => {
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeHidden();
await expect(
page.getByText('Log in to access the document.'),
).toBeVisible();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
test('It checks a authenticated doc in read only mode', async ({
@@ -407,7 +402,7 @@ test.describe('Doc Visibility: Authenticated', () => {
const selectVisibility = page.getByLabel('Visibility', { exact: true });
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Connected',
})
.click();
@@ -416,14 +411,6 @@ test.describe('Doc Visibility: Authenticated', () => {
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await expect(
page
.getByLabel('It is the card information about the document.')
.getByText('Document accessible to any connected person', {
exact: true,
}),
).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();
const urlDoc = page.url();
@@ -469,7 +456,7 @@ test.describe('Doc Visibility: Authenticated', () => {
const selectVisibility = page.getByLabel('Visibility', { exact: true });
await selectVisibility.click();
await page
.getByRole('menuitem', {
.getByRole('button', {
name: 'Connected',
})
.click();

View File

@@ -12,7 +12,7 @@ test.describe('Home page', () => {
const footer = page.locator('footer').first();
await expect(header).toBeVisible();
await expect(
header.getByRole('button', { name: /Language/ }),
header.getByRole('combobox', { name: 'Language' }),
).toBeVisible();
await expect(
header.getByRole('button', { name: 'Les services de La Suite numé' }),

View File

@@ -9,20 +9,19 @@ test.describe('Language', () => {
await expect(page.getByLabel('Logout')).toBeVisible();
const header = page.locator('header').first();
await header
.getByRole('button', { name: /Language/ })
.getByText('English')
.click();
await page.getByRole('menuitem', { name: 'Français' }).click();
await header.getByRole('combobox').getByText('English').click();
await header.getByRole('option', { name: 'Français' }).click();
await expect(
header.getByRole('button').getByText('Français'),
header.getByRole('combobox').getByText('Français'),
).toBeVisible();
await expect(page.getByLabel('Se déconnecter')).toBeVisible();
await header.getByRole('button').getByText('Français').click();
await page.getByRole('menuitem', { name: 'Deutsch' }).click();
await expect(header.getByRole('button').getByText('Deutsch')).toBeVisible();
await header.getByRole('combobox').getByText('Français').click();
await header.getByRole('option', { name: 'Deutsch' }).click();
await expect(
header.getByRole('combobox').getByText('Deutsch'),
).toBeVisible();
await expect(page.getByLabel('Abmelden')).toBeVisible();
});
@@ -54,11 +53,8 @@ test.describe('Language', () => {
// Switch language to French
const header = page.locator('header').first();
await header
.getByRole('button', { name: /Language/ })
.getByText('English')
.click();
await page.getByRole('menuitem', { name: 'Français' }).click();
await header.getByRole('combobox').getByText('English').click();
await header.getByRole('option', { name: 'Français' }).click();
// Check for French 404 response
await check404Response('Pas trouvé.');

View File

@@ -29,7 +29,7 @@ test.describe('Left panel mobile', () => {
const header = page.locator('header').first();
const homeButton = page.getByRole('button', { name: 'house' });
const newDocButton = page.getByRole('button', { name: 'New doc' });
const languageButton = page.getByRole('button', { name: /Language/ });
const languageButton = page.getByRole('combobox', { name: 'Language' });
const logoutButton = page.getByRole('button', { name: 'Logout' });
await expect(homeButton).not.toBeInViewport();

View File

@@ -381,7 +381,6 @@ const config = {
'color-active': 'var(--c--theme--colors--primary-100)',
},
'color-hover': 'var(--c--theme--colors--primary-text)',
color: 'var(--c--theme--colors--primary-800)',
},
secondary: {
background: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

View File

@@ -8,20 +8,17 @@ import {
import { Button, Popover } from 'react-aria-components';
import styled from 'styled-components';
import { BoxProps } from './Box';
const StyledPopover = styled(Popover)`
background-color: white;
border-radius: 4px;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
border: 1px solid #dddddd;
transition: opacity 0.2s ease-in-out;
`;
interface StyledButtonProps {
$css?: BoxProps['$css'];
}
const StyledButton = styled(Button)<StyledButtonProps>`
const StyledButton = styled(Button)`
cursor: pointer;
border: none;
background: none;
@@ -32,12 +29,10 @@ const StyledButton = styled(Button)<StyledButtonProps>`
font-size: 0.938rem;
padding: 0;
text-wrap: nowrap;
${({ $css }) => $css};
`;
export interface DropButtonProps {
button: ReactNode;
buttonCss?: BoxProps['$css'];
isOpen?: boolean;
onOpenChange?: (isOpen: boolean) => void;
label?: string;
@@ -45,7 +40,6 @@ export interface DropButtonProps {
export const DropButton = ({
button,
buttonCss,
isOpen = false,
onOpenChange,
children,
@@ -53,14 +47,11 @@ export const DropButton = ({
}: PropsWithChildren<DropButtonProps>) => {
const [isLocalOpen, setIsLocalOpen] = useState(isOpen);
const triggerRef = useRef<HTMLButtonElement>(null);
const firstFocusableRef = useRef<HTMLButtonElement>(null);
const triggerRef = useRef(null);
useEffect(() => {
if (isLocalOpen && firstFocusableRef.current) {
firstFocusableRef.current.focus();
}
}, [isLocalOpen]);
setIsLocalOpen(isOpen);
}, [isOpen]);
const onOpenChangeHandler = (isOpen: boolean) => {
setIsLocalOpen(isOpen);
@@ -73,7 +64,6 @@ export const DropButton = ({
ref={triggerRef}
onPress={() => onOpenChangeHandler(true)}
aria-label={label}
$css={buttonCss}
>
{button}
</StyledButton>

View File

@@ -1,4 +1,4 @@
import { PropsWithChildren, useRef, useState } from 'react';
import { PropsWithChildren, useState } from 'react';
import { css } from 'styled-components';
import { Box, BoxButton, BoxProps, DropButton, Icon, Text } from '@/components';
@@ -20,7 +20,6 @@ export type DropdownMenuProps = {
showArrow?: boolean;
label?: string;
arrowCss?: BoxProps['$css'];
buttonCss?: BoxProps['$css'];
disabled?: boolean;
topMessage?: string;
};
@@ -31,7 +30,6 @@ export const DropdownMenu = ({
disabled = false,
showArrow = false,
arrowCss,
buttonCss,
label,
topMessage,
}: PropsWithChildren<DropdownMenuProps>) => {
@@ -39,7 +37,6 @@ export const DropdownMenu = ({
const spacings = theme.spacingsTokens();
const colors = theme.colorsTokens();
const [isOpen, setIsOpen] = useState(false);
const blockButtonRef = useRef<HTMLDivElement>(null);
const onOpenChange = (isOpen: boolean) => {
setIsOpen(isOpen);
@@ -54,17 +51,10 @@ export const DropdownMenu = ({
isOpen={isOpen}
onOpenChange={onOpenChange}
label={label}
buttonCss={buttonCss}
button={
showArrow ? (
<Box
ref={blockButtonRef}
$direction="row"
$align="center"
$position="relative"
aria-controls="menu"
>
<Box>{children}</Box>
<Box $direction="row" $align="center">
<div>{children}</div>
<Icon
$variation="600"
$css={
@@ -77,17 +67,11 @@ export const DropdownMenu = ({
/>
</Box>
) : (
<Box ref={blockButtonRef} aria-controls="menu">
{children}
</Box>
children
)
}
>
<Box
$maxWidth="320px"
$minWidth={`${blockButtonRef.current?.clientWidth}px`}
role="menu"
>
<Box $maxWidth="320px">
{topMessage && (
<Text
$variation="700"
@@ -106,7 +90,6 @@ export const DropdownMenu = ({
const isDisabled = option.disabled !== undefined && option.disabled;
return (
<BoxButton
role="menuitem"
aria-label={option.label}
data-testid={option.testId}
$direction="row"

View File

@@ -25,9 +25,7 @@ export interface TextProps extends BoxProps {
$size?: TextSizes | (string & {});
$theme?:
| 'primary'
| 'primary-text'
| 'secondary'
| 'secondary-text'
| 'info'
| 'success'
| 'warning'

View File

@@ -406,10 +406,6 @@ input:-webkit-autofill:focus {
);
}
.c__button--primary-text {
color: var(--c--components--button--primary-text--color);
}
.c__button--primary-text:hover,
.c__button--primary-text:focus-visible {
background-color: var(
@@ -609,20 +605,3 @@ input:-webkit-autofill:focus {
.c__tooltip {
padding: 4px 6px;
}
button:focus {
background-color: var(
--c--components--button--primary-text--background--color-hover
);
border-radius: var(--c--components--button--border-radius--focus);
box-shadow: 0 0 0 2px var(--c--theme--colors--primary-400)
}
.new-doc-button {
color: white !important;
}
.new-doc-button:focus,
.new-doc-button:focus-visible {
color: white !important;
}

View File

@@ -505,9 +505,6 @@
--c--components--button--primary-text--color-hover: var(
--c--theme--colors--primary-text
);
--c--components--button--primary-text--color: var(
--c--theme--colors--primary-800
);
--c--components--button--secondary--background--color-hover: #f6f6f6;
--c--components--button--secondary--background--color-active: #ededed;
--c--components--button--secondary--border--color: var(

View File

@@ -505,7 +505,6 @@ export const tokens = {
'color-active': 'var(--c--theme--colors--primary-100)',
},
'color-hover': 'var(--c--theme--colors--primary-text)',
color: 'var(--c--theme--colors--primary-800)',
},
secondary: {
background: { 'color-hover': '#F6F6F6', 'color-active': '#EDEDED' },

View File

@@ -14,11 +14,7 @@ export const ButtonLogin = () => {
if (!authenticated) {
return (
<Button
onClick={() => gotoLogin()}
color="primary-text"
aria-label={t('Login')}
>
<Button onClick={gotoLogin} color="primary-text" aria-label={t('Login')}>
{t('Login')}
</Button>
);
@@ -36,7 +32,7 @@ export const ProConnectButton = () => {
return (
<BoxButton
onClick={() => gotoLogin()}
onClick={gotoLogin}
aria-label={t('Proconnect Login')}
$css={css`
background-color: var(--c--theme--colors--primary-text);

View File

@@ -16,11 +16,8 @@ export const setAuthUrl = () => {
}
};
export const gotoLogin = (withRedirect = true) => {
if (withRedirect) {
setAuthUrl();
}
export const gotoLogin = () => {
setAuthUrl();
window.location.replace(LOGIN_URL);
};

View File

@@ -1,4 +1,9 @@
import { BlockNoteSchema, Dictionary, locales } from '@blocknote/core';
import {
BlockNoteSchema,
Dictionary,
locales,
withPageBreak,
} from '@blocknote/core';
import '@blocknote/core/fonts/inter.css';
import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css';
@@ -22,7 +27,7 @@ import { randomColor } from '../utils';
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
import { BlockNoteToolbar } from './BlockNoteToolbar';
export const blockNoteSchema = BlockNoteSchema.create();
export const blockNoteSchema = withPageBreak(BlockNoteSchema.create());
interface BlockNoteEditorProps {
doc: Doc;

View File

@@ -76,13 +76,13 @@ export const cssEditor = (readonly: boolean) => css`
.bn-block-outer:not(:first-child) {
&:has(h1) {
margin-top: 32px;
padding-top: 32px;
}
&:has(h2) {
margin-top: 24px;
padding-top: 24px;
}
&:has(h3) {
margin-top: 16px;
padding-top: 16px;
}
}
@@ -92,16 +92,9 @@ export const cssEditor = (readonly: boolean) => css`
border-radius: 4px;
}
@media screen and (width <= 768px) {
& .bn-editor {
padding-right: 36px;
}
}
@media screen and (width <= 560px) {
& .bn-editor {
${readonly && `padding-left: 10px;`}
padding-right: 10px;
}
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 46px;

View File

@@ -27,7 +27,6 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
const { t } = useTranslation();
const docIsPublic = doc.link_reach === LinkReach.PUBLIC;
const docIsAuth = doc.link_reach === LinkReach.AUTHENTICATED;
const { transRole } = useTrans();
@@ -39,7 +38,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$gap={spacings['base']}
aria-label={t('It is the card information about the document.')}
>
{(docIsPublic || docIsAuth) && (
{docIsPublic && (
<Box
aria-label={t('Public document')}
$color={colors['primary-800']}
@@ -58,12 +57,10 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$theme="primary"
$variation="800"
data-testid="public-icon"
iconName={docIsPublic ? 'public' : 'vpn_lock'}
iconName="public"
/>
<Text $theme="primary" $variation="800">
{docIsPublic
? t('Public document')
: t('Document accessible to any connected person')}
{t('Public document')}
</Text>
</Box>
)}
@@ -79,9 +76,8 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$css="flex:1;"
$gap="0.5rem 1rem"
$align="center"
$maxWidth="100%"
>
<Box $gap={spacings['3xs']} $overflow="auto">
<Box $gap={spacings['3xs']}>
<DocTitle doc={doc} />
<Box $direction="row">

View File

@@ -101,35 +101,39 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
}, [doc]);
return (
<Tooltip content={t('Rename')} placement="top">
<Box
as="span"
role="textbox"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label="doc title input"
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
$color={colorsTokens()['greyscale-1000']}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: grey;
pointer-events: none;
font-style: italic;
<>
<Tooltip content={t('Rename')} placement="top">
<Box
as="span"
role="textbox"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label="doc title input"
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
font-size: ${isDesktop
? css`var(--c--theme--font--sizes--h2)`
: css`var(--c--theme--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
</Tooltip>
$color={colorsTokens()['greyscale-1000']}
$margin={{ left: '-2px', right: '10px' }}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: grey;
pointer-events: none;
font-style: italic;
}
font-size: ${isDesktop
? css`var(--c--theme--font--sizes--h2)`
: css`var(--c--theme--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
</Tooltip>
</>
);
};

View File

@@ -18,11 +18,7 @@ import {
} from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useEditorStore } from '@/features/docs/doc-editor/';
import {
Doc,
ModalRemoveDoc,
useCopyDocLink,
} from '@/features/docs/doc-management';
import { Doc, ModalRemoveDoc } from '@/features/docs/doc-management';
import { DocShareModal } from '@/features/docs/doc-share';
import {
KEY_LIST_DOC_VERSIONS,
@@ -38,7 +34,7 @@ interface DocToolBoxProps {
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const { t } = useTranslation();
const hasAccesses = doc.nb_accesses > 1 && doc.abilities.accesses_view;
const hasAccesses = doc.nb_accesses > 1;
const queryClient = useQueryClient();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
@@ -54,7 +50,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const { isSmallMobile, isDesktop } = useResponsiveStore();
const { editor } = useEditorStore();
const { toast } = useToastProvider();
const copyDocLink = useCopyDocLink(doc.id);
const options: DropdownMenuOption[] = [
...(isSmallMobile
@@ -71,11 +66,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
setIsModalExportOpen(true);
},
},
{
label: t('Copy link'),
icon: 'add_link',
callback: copyDocLink,
},
]
: []),

View File

@@ -71,15 +71,9 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
</Button>
</>
}
size={ModalSize.SMALL}
size={ModalSize.MEDIUM}
title={
<Text
$size="h6"
as="h6"
$margin={{ all: '0' }}
$align="flex-start"
$variation="1000"
>
<Text $size="h6" as="h6" $margin={{ all: '0' }} $align="flex-start">
{t('Delete a doc')}
</Text>
}

View File

@@ -33,7 +33,9 @@ export const DocShareModalFooter = ({ doc, onClose }: Props) => {
>
<Button
fullWidth={false}
onClick={copyDocLink}
onClick={() => {
copyDocLink();
}}
color="tertiary"
icon={<span className="material-icons">add_link</span>}
>

View File

@@ -100,21 +100,17 @@ export const ModalConfirmationVersion = ({
</Button>
</>
}
size={ModalSize.SMALL}
size={ModalSize.MEDIUM}
title={
<Text $size="h6" $align="flex-start" $variation="1000">
<Text $size="h6" $align="flex-start">
{t('Warning')}
</Text>
}
>
<Box aria-label={t('Modal confirmation to restore the version')}>
<Box>
<Text $variation="600">
{t('Your current document will revert to this version.')}
</Text>
<Text $variation="600">
{t('If a member is editing, his works can be lost.')}
</Text>
<Text>{t('Your current document will revert to this version.')}</Text>
<Text>{t('If a member is editing, his works can be lost.')}</Text>
</Box>
</Box>
</Modal>

View File

@@ -61,8 +61,12 @@ export const DocsGrid = ({
$position="relative"
$width="100%"
$maxWidth="960px"
$maxHeight="calc(100vh - 52px - 2rem)"
$maxHeight="calc(100vh - 52px - 1rem)"
$align="center"
$css={css`
overflow-x: hidden;
overflow-y: auto;
`}
>
<DocsGridLoader isLoading={isRefetching || loading} />
<Card
@@ -71,7 +75,8 @@ export const DocsGrid = ({
$height="100%"
$width="100%"
$css={css`
${!isDesktop ? 'border: none;' : ''}
overflow-x: hidden;
overflow-y: auto;
`}
$padding={{
top: 'base',
@@ -96,7 +101,7 @@ export const DocsGrid = ({
</Box>
)}
{hasDocs && (
<Box $gap="6px" $overflow="auto">
<Box $gap="6px">
<Box
$direction="row"
$padding={{ horizontal: 'xs' }}
@@ -117,30 +122,28 @@ export const DocsGrid = ({
)}
</Box>
{/* Body */}
{data?.pages.map((currentPage) => {
return currentPage.results.map((doc) => (
<DocsGridItem doc={doc} key={doc.id} />
));
})}
{hasNextPage && !loading && (
<InView
data-testid="infinite-scroll-trigger"
as="div"
onChange={loadMore}
>
{!isFetching && hasNextPage && (
<Button
onClick={() => void fetchNextPage()}
color="primary-text"
>
{t('More docs')}
</Button>
)}
</InView>
)}
</Box>
)}
{hasNextPage && !loading && (
<InView
data-testid="infinite-scroll-trigger"
as="div"
onChange={loadMore}
>
{!isFetching && hasNextPage && (
<Button onClick={() => void fetchNextPage()} color="primary-text">
{t('More docs')}
</Button>
)}
</InView>
)}
</Card>
</Box>
);

View File

@@ -54,10 +54,6 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
$css={css`
flex: ${flexLeft};
align-items: center;
&:focus {
outline: 2px solidrgb(33, 34, 82);
}
min-width: 0;
`}
href={`/docs/${doc.id}`}
>
@@ -68,7 +64,6 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
$gap={spacings.xs}
$flex={flexLeft}
$padding={{ right: isDesktop ? 'md' : '3xs' }}
$maxWidth="100%"
>
<SimpleDocItem isPinned={doc.is_favorite} doc={doc} />
{showAccesses && (

View File

@@ -38,7 +38,7 @@ export const SimpleDocItem = ({
const { untitledDocument } = useTrans();
return (
<Box $direction="row" $gap={spacings.sm} $overflow="auto">
<Box $direction="row" $gap={spacings.sm}>
<Box
$direction="row"
$align="center"
@@ -50,10 +50,10 @@ export const SimpleDocItem = ({
{isPinned ? (
<PinnedDocumentIcon aria-label={t('Pin document icon')} />
) : (
<SimpleFileIcon aria-hidden="true" />
<SimpleFileIcon aria-label={t('Simple document icon')} />
)}
</Box>
<Box $justify="center" $overflow="auto">
<Box $justify="center">
<Text
aria-describedby="doc-title"
aria-label={doc.title}

View File

@@ -7,11 +7,9 @@ import { createGlobalStyle } from 'styled-components';
import { useCunninghamTheme } from '@/cunningham';
const GaufreStyle = createGlobalStyle`
&:focus {
outline: 2px solidrgb(33, 34, 82);
.lasuite-gaufre-btn{
box-shadow: inset 0 0 0 0 !important;
}
`;
export const LaGaufre = () => {

View File

@@ -55,7 +55,7 @@ export default function HomeBanner() {
$textAlign="center"
$margin="none"
$css={css`
line-height: ${!isMobile ? '56px' : '45px'};
line-height: 56px;
`}
>
{t('Collaborative writing, Simplified.')}
@@ -74,7 +74,7 @@ export default function HomeBanner() {
<ProConnectButton />
) : (
<Button
onClick={() => gotoLogin()}
onClick={gotoLogin}
icon={
<Text $isMaterialIcon $color="white">
bolt

View File

@@ -116,8 +116,8 @@ export const HomeSection = ({
`}
$variation="1000"
$weight="bold"
$size={!isSmallDevice ? 'xs-alt' : isSmallMobile ? 'h6' : 'h4'}
$textAlign="left"
$size={!isSmallDevice ? 'xs-alt' : 'h4'}
$textAlign={isSmallMobile ? 'center' : 'left'}
$margin="none"
>
{title}

View File

@@ -1,16 +1,41 @@
import { Select } from '@openfun/cunningham-react';
import { Settings } from 'luxon';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import styled from 'styled-components';
import { DropdownMenu, Text } from '@/components/';
import { Box, Text } from '@/components/';
import { LANGUAGES_ALLOWED } from '@/i18n/conf';
const SelectStyled = styled(Select)<{ $isSmall?: boolean }>`
flex-shrink: 0;
width: auto;
.c__select__wrapper {
min-height: 2rem;
height: auto;
border-color: transparent;
padding: 0 0.15rem 0 0.45rem;
border-radius: 1px;
.labelled-box .labelled-box__children {
padding-right: 2rem;
.c_select__render .typo-text {
${({ $isSmall }) => $isSmall && `display: none;`}
}
}
&:hover {
box-shadow: none !important;
}
}
`;
export const LanguagePicker = () => {
const { t, i18n } = useTranslation();
const { preload: languages } = i18n.options;
const language = i18n.language;
Settings.defaultLocale = language;
Settings.defaultLocale = i18n.language;
const optionsPicker = useMemo(() => {
return (languages || []).map((lang) => ({
@@ -32,53 +57,28 @@ export const LanguagePicker = () => {
>
translate
</Text>
<Text $theme="primary" $weight="500" $variation="800" lang={lang}>
<Text $theme="primary" $weight="500" $variation="800">
{LANGUAGES_ALLOWED[lang]}
</Text>
</Box>
),
label: LANGUAGES_ALLOWED[lang],
isSelected: language === lang,
callback: () => {
i18n.changeLanguage(lang).catch((err) => {
console.error('Error changing language', err);
});
},
}));
}, [i18n, language, languages]);
}, [languages]);
return (
<DropdownMenu
<SelectStyled
label={t('Language')}
showLabelWhenSelected={false}
clearable={false}
hideLabel
defaultValue={i18n.language}
className="c_select__no_bg"
options={optionsPicker}
showArrow
buttonCss={css`
&:hover {
background-color: var(
--c--components--button--primary-text--background--color-hover
);
}
border-radius: 4px;
padding: 0.5rem 0.6rem;
& > div {
gap: 0.2rem;
display: flex;
}
& .material-icons {
color: var(--c--components--button--primary-text--color) !important;
}
`}
>
<Text
$theme="primary"
aria-label={t('Language')}
$direction="row"
$gap="0.5rem"
>
<Text $isMaterialIcon $color="inherit" $size="xl">
translate
</Text>
{LANGUAGES_ALLOWED[language]}
</Text>
</DropdownMenu>
onChange={(e) => {
i18n.changeLanguage(e.target.value as string).catch((err) => {
console.error('Error changing language', err);
});
}}
/>
);
};

View File

@@ -50,7 +50,6 @@ export const LeftPanel = () => {
overflow: hidden;
border-right: 1px solid ${colors['greyscale-200']};
`}
$background={colors['greyscale-000']}
>
<Box
$css={css`

View File

@@ -39,7 +39,7 @@ export const LeftPanelFavoriteItem = ({ doc }: LeftPanelFavoriteItemProps) => {
`}
key={doc.id}
>
<StyledLink href={`/docs/${doc.id}`} $css="overflow: auto;">
<StyledLink href={`/docs/${doc.id}`}>
<SimpleDocItem showAccesses doc={doc} />
</StyledLink>
<div className="pinned-actions">

View File

@@ -50,11 +50,8 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
onClick={goToHome}
size="medium"
color="tertiary-text"
aria-label={t('Back to home page')}
icon={
<span aria-hidden="true">
<Icon $variation="800" $theme="primary" iconName="house" />
</span>
<Icon $variation="800" $theme="primary" iconName="house" />
}
/>
{authenticated && (
@@ -62,27 +59,14 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
onClick={searchModal.open}
size="medium"
color="tertiary-text"
aria-label={t('Search')}
icon={
<span aria-hidden="true">
<Icon
$variation="800"
$theme="primary"
iconName="search"
/>
</span>
<Icon $variation="800" $theme="primary" iconName="search" />
}
/>
)}
</Box>
{authenticated && (
<Button
onClick={createNewDoc}
className="new-doc-button"
aria-label={t('New document')}
>
{t('New doc')}
</Button>
<Button onClick={createNewDoc}>{t('New doc')}</Button>
)}
</Box>
</SeparatedSection>

View File

@@ -270,7 +270,6 @@
"My docs": "Mes documents",
"Name": "Nom",
"New doc": "Nouveau doc",
"New document": "Nouveau document",
"No active search": "Aucune recherche active",
"No document found": "Aucun document trouvé",
"No documents found": "Aucun document trouvé",

View File

@@ -19,8 +19,8 @@ export function MainLayout({
}: PropsWithChildren<MainLayoutProps>) {
const { isDesktop } = useResponsiveStore();
const { colorsTokens } = useCunninghamTheme();
const colors = colorsTokens();
const currentBackgroundColor = !isDesktop ? 'white' : backgroundColor;
return (
<div>
@@ -39,10 +39,10 @@ export function MainLayout({
$width="100%"
$height={`calc(100dvh - ${HEADER_HEIGHT}px)`}
$padding={{
all: isDesktop ? 'base' : '0',
all: isDesktop ? 'base' : '2xs',
}}
$background={
currentBackgroundColor === 'white'
backgroundColor === 'white'
? colors['greyscale-000']
: colors['greyscale-050']
}

View File

@@ -6,27 +6,17 @@ import { HEADER_HEIGHT, Header } from '@/features/header';
import { LeftPanel } from '@/features/left-panel';
import { useResponsiveStore } from '@/stores';
interface PageLayoutProps {
withFooter?: boolean;
}
export function PageLayout({
children,
withFooter = true,
}: PropsWithChildren<PageLayoutProps>) {
export function PageLayout({ children }: PropsWithChildren) {
const { isDesktop } = useResponsiveStore();
return (
<Box
$minHeight={`calc(100vh - ${HEADER_HEIGHT}px)`}
$margin={{ top: `${HEADER_HEIGHT}px` }}
>
<Box $minHeight="100vh" $margin={{ top: `${HEADER_HEIGHT}px` }}>
<Header />
<Box as="main" $width="100%" $css="flex-grow:1;">
{!isDesktop && <LeftPanel />}
{children}
</Box>
{withFooter && <Footer />}
<Footer />
</Box>
);
}

View File

@@ -1,57 +0,0 @@
import { Button } from '@openfun/cunningham-react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { ReactElement, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import img401 from '@/assets/icons/icon-401.png';
import { Box, Text } from '@/components';
import { gotoLogin, useAuth } from '@/features/auth';
import { PageLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
const { authenticated } = useAuth();
const { replace } = useRouter();
useEffect(() => {
if (authenticated) {
void replace(`/`);
}
}, [authenticated, replace]);
return (
<Box
$align="center"
$margin="auto"
$gap="1rem"
$padding={{ bottom: '2rem' }}
>
<Image
src={img401}
alt={t('Image 401')}
style={{
maxWidth: '100%',
height: 'auto',
}}
/>
<Box $align="center" $gap="0.8rem">
<Text as="p" $textAlign="center" $maxWidth="350px" $theme="primary">
{t('Log in to access the document.')}
</Text>
<Button onClick={() => gotoLogin(false)} aria-label={t('Login')}>
{t('Login')}
</Button>
</Box>
</Box>
);
};
Page.getLayout = function getLayout(page: ReactElement) {
return <PageLayout withFooter={false}>{page}</PageLayout>;
};
export default Page;

View File

@@ -1,60 +0,0 @@
import { Button } from '@openfun/cunningham-react';
import Image from 'next/image';
import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import img403 from '@/assets/icons/icon-403.png';
import { Box, StyledLink, Text } from '@/components';
import { PageLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const StyledButton = styled(Button)`
width: fit-content;
`;
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
return (
<Box
$align="center"
$margin="auto"
$gap="1rem"
$padding={{ bottom: '2rem' }}
>
<Image
src={img403}
alt={t('Image 403')}
style={{
maxWidth: '100%',
height: 'auto',
}}
/>
<Box $align="center" $gap="0.8rem">
<Text as="p" $textAlign="center" $maxWidth="350px" $theme="primary">
{t('You do not have permission to view this document.')}
</Text>
<StyledLink href="/">
<StyledButton
icon={
<Text $isMaterialIcon $color="white">
house
</Text>
}
>
{t('Home')}
</StyledButton>
</StyledLink>
</Box>
</Box>
);
};
Page.getLayout = function getLayout(page: ReactElement) {
return <PageLayout withFooter={false}>{page}</PageLayout>;
};
export default Page;

View File

@@ -5,7 +5,7 @@ import styled from 'styled-components';
import Icon404 from '@/assets/icons/icon-404.svg';
import { Box, StyledLink, Text } from '@/components';
import { PageLayout } from '@/layouts';
import { MainLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const StyledButton = styled(Button)`
@@ -33,15 +33,7 @@ const Page: NextPageWithLayout = () => {
<Box $margin={{ top: 'large' }}>
<StyledLink href="/">
<StyledButton
icon={
<Text $isMaterialIcon $color="white">
house
</Text>
}
>
{t('Home')}
</StyledButton>
<StyledButton>{t('Back to home page')}</StyledButton>
</StyledLink>
</Box>
</Box>
@@ -49,7 +41,7 @@ const Page: NextPageWithLayout = () => {
};
Page.getLayout = function getLayout(page: ReactElement) {
return <PageLayout withFooter={false}>{page}</PageLayout>;
return <MainLayout>{page}</MainLayout>;
};
export default Page;

View File

@@ -16,11 +16,7 @@ type AppPropsWithLayout = AppProps & {
export default function App({ Component, pageProps }: AppPropsWithLayout) {
useSWRegister();
const getLayout = Component.getLayout ?? ((page) => page);
const { t, i18n } = useTranslation();
useEffect(() => {
document.documentElement.lang = i18n.language;
}, [i18n.language]);
const { t } = useTranslation();
useEffect(() => {
console.log(

View File

@@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { Box, Text, TextErrors } from '@/components';
import { setAuthUrl } from '@/features/auth';
import { gotoLogin } from '@/features/auth';
import { DocEditor } from '@/features/docs/doc-editor';
import {
Doc,
@@ -99,19 +99,13 @@ const DocPage = ({ id }: DocProps) => {
}, [addTask, doc?.id, queryClient]);
if (isError && error) {
if (error.status === 403) {
void replace(`/403`);
return null;
}
if (error.status === 404) {
void replace(`/404`);
return null;
}
if (error.status === 401) {
setAuthUrl();
void replace(`/401`);
gotoLogin();
return null;
}

View File

@@ -31,15 +31,7 @@ const Page: NextPageWithLayout = () => {
<Box $margin={{ top: 'large' }}>
<StyledLink href="/">
<StyledButton
icon={
<Text $isMaterialIcon $color="white">
house
</Text>
}
>
{t('Home')}
</StyledButton>
<StyledButton>{t('Back to home page')}</StyledButton>
</StyledLink>
</Box>
</Box>