Compare commits

..

1 Commits

Author SHA1 Message Date
Anthony LC 44581d3880 🔖(minor) release 4.6.0
Added:
- (frontend) integrate new Blocknote AI feature
- 👷(docker) add arm64 platform support for image builds
- (tracking) add UTM parameters to shared document links
- (frontend) add floating bar with leftpanel collapse button
- (frontend) Can print a doc
- (backend) manage reconciliation requests for user accounts
- 👷(CI) add GHCR workflow for forked repo testing
- (frontend) Move doc modal
- ️(backend) remove content from Document serializer when asked
- (backend) allow the duplication of subpages
- (backend) Onboarding docs for new users
- 🩺(trivy) add trivyignore file and add minimatch CVE
- 🚩 Add feature flags for the AI feature

Changed:
- ️(frontend) prevent dates from being focusable
- ️(frontend) Focus main container after navigation
- 💄(frontend) align colors and logo with ui-kit v2
- 🚸(backend) sort user search results by proximity
  with the active user
- 🚸(oidc) ignore case when fallback on email
- ️(CI) optimize Docker Hub workflow

Fixed:
- 🐛(frontend) fix broadcast store sync
- 🐛(helm) use celery resources instead of backend resources
- 🐛(helm) reverse liveness and readiness for backend deployment
- 🐛(y-provider) use CONVERSION_FILE_MAX_SIZE settings
- 🐛(frontend) fix callout block spacing for old browsers
2026-03-05 09:30:41 +01:00
102 changed files with 608 additions and 1232 deletions
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Create empty source files
run: |
touch src/backend/locale/django.pot
@@ -48,7 +48,7 @@ jobs:
CROWDIN_BASE_PATH: "../src/"
# frontend i18n
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
+4 -4
View File
@@ -20,10 +20,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
# Backend i18n
- name: Install Python
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: "3.13.3"
cache: "pip"
@@ -33,7 +33,7 @@ jobs:
run: pip install --user .
working-directory: src/backend
- name: Restore the mail templates
uses: actions/cache@v5
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
@@ -49,7 +49,7 @@ jobs:
DJANGO_CONFIGURATION=Build python manage.py makemessages -a --keep-pot
# frontend i18n
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
+8 -8
View File
@@ -20,16 +20,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
id: front-node_modules
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Setup Node.js
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Install dependencies
@@ -37,7 +37,7 @@ jobs:
run: cd src/frontend/ && yarn install --frozen-lockfile
- name: Cache install frontend
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -50,10 +50,10 @@ jobs:
working-directory: src/mail
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Restore the mail templates
uses: actions/cache@v5
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
@@ -61,7 +61,7 @@ jobs:
- name: Setup Node.js
if: steps.mail-templates.outputs.cache-hit != 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
@@ -79,7 +79,7 @@ jobs:
- name: Cache mail templates
if: steps.mail-templates.outputs.cache-hit != 'true'
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
+3 -3
View File
@@ -22,7 +22,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@@ -68,7 +68,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@@ -117,7 +117,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
steps:
-
name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Helmfile lint
shell: bash
+19 -19
View File
@@ -23,15 +23,15 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -47,14 +47,14 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -69,15 +69,15 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -111,15 +111,15 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -157,11 +157,11 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Detect relevant changes
id: changes
uses: dorny/paths-filter@v3
uses: dorny/paths-filter@v2
with:
filters: |
lock:
@@ -170,7 +170,7 @@ jobs:
- 'src/frontend/apps/impress/**'
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
@@ -178,7 +178,7 @@ jobs:
- name: Setup Node.js
if: steps.changes.outputs.lock == 'true' || steps.changes.outputs.app == 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
@@ -205,14 +205,14 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: Restore the frontend cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
+9 -9
View File
@@ -19,7 +19,7 @@ jobs:
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: show
@@ -46,7 +46,7 @@ jobs:
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 50
- name: Check that the CHANGELOG has been modified in the current branch
@@ -56,7 +56,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
@@ -70,7 +70,7 @@ jobs:
if: github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install codespell
run: pip install --user codespell
- name: Check for typos
@@ -92,9 +92,9 @@ jobs:
working-directory: src/backend
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: "3.13.3"
cache: "pip"
@@ -146,7 +146,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Create writable /data
run: |
@@ -154,7 +154,7 @@ jobs:
sudo mkdir -p /data/static
- name: Restore the mail templates
uses: actions/cache@v5
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
@@ -190,7 +190,7 @@ jobs:
mc version enable impress/impress-media-storage"
- name: Install Python
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: "3.13.3"
cache: "pip"
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
+1 -25
View File
@@ -6,29 +6,6 @@ and this project adheres to
## [Unreleased]
## [v4.7.0] - 2026-03-09
### Added
- ✨(helm) allow all keys in configMap as env var #1872
### Changed
- 📝(docs) improve README and add documentation hub #1870
- ♿️(frontend) restore focus to triggers after closing menus and modals #1863
- 🚸(frontend) change position elements toolbar #1957
- ♿️(frontend) add focus on open to modals #1948
### Fixed
- 🐛(frontend) analytic feature flags problem #1953
- 🐛(frontend) fix home collapsing panel #1954
- 🐛(frontend) fix disabled color on icon Dropdown #1950
- 🐛(frontend) fix zIndex table of content #1949
- 🐛(frontend) fix bug when language not supported by BN #1957
- 🐛 (backend) prevent privileged users from requesting access #1898
## [v4.6.0] - 2026-03-03
### Added
@@ -1083,8 +1060,7 @@ and this project adheres to
- ✨(frontend) Coming Soon page (#67)
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/suitenumerique/docs/compare/v4.7.0...main
[v4.7.0]: https://github.com/suitenumerique/docs/releases/v4.7.0
[unreleased]: https://github.com/suitenumerique/docs/compare/v4.6.0...main
[v4.6.0]: https://github.com/suitenumerique/docs/releases/v4.6.0
[v4.5.0]: https://github.com/suitenumerique/docs/releases/v4.5.0
[v4.4.0]: https://github.com/suitenumerique/docs/releases/v4.4.0
+7
View File
@@ -14,6 +14,13 @@ FROM base AS back-builder
WORKDIR /builder
# Install Rust and Cargo using Alpine's package manager
RUN apk add --no-cache \
build-base \
libffi-dev \
rust \
cargo
# Copy required python dependencies
COPY ./src/backend /builder
+136 -148
View File
@@ -3,238 +3,226 @@
<img alt="Docs" src="/docs/assets/banner-docs.png" width="100%" />
</a>
</p>
<p align="center">
<a href="https://github.com/suitenumerique/docs/stargazers/">
<img src="https://img.shields.io/github/stars/suitenumerique/docs" alt="">
</a>
<a href="https://github.com/suitenumerique/docs/blob/main/CONTRIBUTING.md">
<img alt="PRs Welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/>
</a>
<a href='https://github.com/suitenumerique/docs/blob/main/CONTRIBUTING.md'><img alt='PRs Welcome' src='https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=shields'/></a>
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/suitenumerique/docs"/>
<img alt="GitHub closed issues" src="https://img.shields.io/github/issues-closed/suitenumerique/docs"/>
<a href="https://github.com/suitenumerique/docs/blob/main/LICENSE">
<img alt="MIT License" src="https://img.shields.io/github/license/suitenumerique/docs"/>
</a>
</p>
<p align="center">
<a href="https://matrix.to/#/#docs-official:matrix.org">
Chat on Matrix
</a> - <a href="/docs/">
Documentation
</a> - <a href="#getting-started-">
Getting started
</a> - <a href="mailto:docs@numerique.gouv.fr">
Reach out
</a>
</p>
<p align="center">
<a href="https://matrix.to/#/#docs-official:matrix.org">Chat on Matrix</a> •
<a href="/docs/">Documentation</a> •
<a href="#try-docs">Try Docs</a> •
<a href="mailto:docs@numerique.gouv.fr">Contact us</a>
</p>
# La Suite Docs : Collaborative Text Editing
Docs, where your notes can become knowledge through live collaboration.
# La Suite Docs: Collaborative Text Editing
<img src="/docs/assets/docs_live_collaboration_light.gif" width="100%" align="center"/>
**Docs, where your notes can become knowledge through live collaboration.**
## Why use Docs ❓
Docs is a collaborative text editor designed to address common challenges in knowledge building and sharing.
Docs is an open-source collaborative editor that helps teams write, organize, and share knowledge together - in real time.
### Write
* 😌 Get simple, accessible online editing for your team.
* 💅 Create clean documents with beautiful formatting options.
* 🖌️ Focus on your content using either the in-line editor, or [the Markdown syntax](https://www.markdownguide.org/basic-syntax/).
* 🧱 Quickly design your page thanks to the many block types, accessible from the `/` slash commands, as well as keyboard shortcuts.
* 🔌 Write offline! Your edits will be synced once you're back online.
* ✨ Save time thanks to our AI actions, such as rephrasing, summarizing, fixing typos, translating, etc. You can even turn your selected text into a prompt!
![Live collaboration demo](/docs/assets/docs_live_collaboration_light.gif)
### Work together
* 🤝 Enjoy live editing! See your team collaborate in real time.
* 🔒 Keep your information secure thanks to granular access control. Only share with the right people.
* 📑 Export your content in multiple formats (`.odt`, `.docx`, `.pdf`) with customizable templates.
* 📚 Turn your team's collaborative work into organized knowledge with Subpages.
### Self-host
## What is Docs?
#### 🚀 Docs is easy to install on your own servers
We use Kubernetes for our [production instance](https://docs.numerique.gouv.fr/) but also support Docker Compose. The community contributed a couple other methods (Nix, YunoHost etc.) check out the [docs](/docs/installation/README.md) to get detailed instructions and examples.
Docs is an open-source alternative to tools like Notion or Google Docs, focused on:
#### 🌍 Known instances
We hope to see many more, here is an incomplete list of public Docs instances. Feel free to make a PR to add ones that are not listed below🙏
- Real-time collaboration
- Clean, structured documents
- Knowledge organization
- Data ownership & self-hosting
| Url | Org | Public |
| --- | --- | ------- |
| [docs.numerique.gouv.fr](https://docs.numerique.gouv.fr/) | DINUM | French public agents working for the central administration and the extended public sphere. ProConnect is required to login in or sign up|
| [docs.suite.anct.gouv.fr](https://docs.suite.anct.gouv.fr/) | ANCT | French public agents working for the territorial administration and the extended public sphere. ProConnect is required to login in or sign up|
| [notes.demo.opendesk.eu](https://notes.demo.opendesk.eu) | ZenDiS | Demo instance of OpenDesk. Request access to get credentials |
| [notes.liiib.re](https://notes.liiib.re/) | lasuite.coop | Free and open demo to all. Content and accounts are reset after one month |
| [docs.federated.nexus](https://docs.federated.nexus/) | federated.nexus | Public instance, but you have to [sign up for a Federated Nexus account](https://federated.nexus/register/). |
| [docs.demo.mosacloud.eu](https://docs.demo.mosacloud.eu/) | mosa.cloud | Demo instance of mosa.cloud, a dutch company providing services around La Suite apps. |
***Built for public organizations, companies, and open communities.***
#### ⚠️ Advanced features
For some advanced features (ex: Export as PDF) Docs relies on XL packages from BlockNote. These are licenced under GPL and are not MIT compatible. You can perfectly use Docs without these packages by setting the environment variable `PUBLISH_AS_MIT` to true. That way you'll build an image of the application without the features that are not MIT compatible. Read the [environment variables documentation](/docs/env.md) for more information.
## Why use Docs?
## Getting started 🔧
### Writing
### Test it
- Rich-text & Markdown editing
- Slash commands & block system
- Beautiful formatting
- Offline editing
- Optional AI writing helpers (rewirite, summarize, translate, fix typos)
You can test Docs on your browser by visiting this [demo document](https://docs.la-suite.eu/docs/9137bbb5-3e8a-4ff7-8a36-fcc4e8bd57f4/)
### Collaboration
### Run Docs locally
- Live cursors & presence
- Comments & sharing
- Granular access control
> ⚠️ The methods described below for running Docs locally is **for testing purposes only**. It is based on building Docs using [Minio](https://min.io/) as an S3-compatible storage solution. Of course you can choose any S3-compatible storage solution.
### Knowledge management
**Prerequisite**
- Subpages & hierarchy
- Searchable content
Make sure you have a recent version of Docker and [Docker Compose](https://docs.docker.com/compose/install) installed on your laptop, then type:
### Export/Import & interoperability
```shellscript
$ docker -v
- Import to `.docx` and `.md`
- Export to `.docx`, `.odt`, `.pdf`
Docker version 20.10.2, build 2291f61
## Try Docs
$ docker compose version
Experience Docs instantly - no installation required.
- 🔗 [Open a live demo document][demo]
- 🌍 [Browse public instances][instances]
[demo]: https://docs.la-suite.eu/docs/9137bbb5-3e8a-4ff7-8a36-fcc4e8bd57f4/
[instances]: /docs/instances.md
## Self-hosting
Docs supports Kubernetes, Docker Compose, and community-provided methods such as Nix and YunoHost.
Get started with self-hosting: [Installation guide](/docs/installation/README.md)
> [!WARNING]
> Some advanced features (for example: `Export as PDF`) rely on XL packages from Blocknote.
> These packages are licensed under GPL and are **not MIT-compatible**
>
> You can run Docs **without these packages** by building with:
>
> ```bash
> PUBLISH_AS_MIT=true
> ```
>
> This builds an image of Docs without non-MIT features.
>
> More details can be found in [environment variables](/docs/env.md)
## Local Development (for contributors)
Run Docs locally for development and testing.
> [!WARNING]
> This setup is intended **for development and testing only**.
> It uses Minio as an S3-compatible storage backend, but any S3-compatible service can be used.
### Prerequisites
- Docker
- Docker Compose
- GNU Make
Verify installation:
```bash
docker -v
docker compose version
Docker Compose version v2.32.4
```
> If you encounounter permission errors, you may need to use `sudo`, or add your user to the `docker` group.
> ⚠️ You may need to run the following commands with `sudo`, but this can be avoided by adding your user to the local `docker` group.
### Bootstrap the project
**Project bootstrap**
The easiest way to start is using GNU Make:
The easiest way to start working on the project is to use [GNU Make](https://www.gnu.org/software/make/):
```bash
make bootstrap FLUSH_ARGS='--no-input'
```shellscript
$ make bootstrap FLUSH_ARGS='--no-input'
```
This builds the `app-dev` and `fronted-dev` containers, installs dependencies, runs database migrations, and compiles translations.
This command builds the `app-dev` and `frontend-dev` containers, installs dependencies, performs database migrations and compiles 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.
It is recommend to run this command after pulling new code.
Your Docker services should now be up and running 🎉
Start services:
You can access the project by going to <http://localhost:3000>.
You will be prompted to log in. The default credentials are:
```bash
make run
```
Open <https://localhost:3000>
Default credentials (development only):
```md
username: impress
password: impress
```
### Frontend development mode
📝 Note that if you need to run them afterwards, you can use the eponymous Make rule:
For frontend work, running outside Docker is often more convenient:
```bash
make frontend-development-install
make run-frontend-development
```shellscript
$ make run
```
### Backend only
⚠️ For the frontend developer, it is often better to run the frontend in development mode locally.
Starting all services except the frontend container:
To do so, install the frontend dependencies with the following command:
```bash
make run-backend
```shellscript
$ make frontend-development-install
```
### Tests & Linting
And run the frontend locally in development mode with the following command:
```bash
make frontend-test
make frontend-lint
```shellscript
$ make run-frontend-development
```
### Demo content
To start all the services, except the frontend container, you can use the following command:
Create a basic demo site:
```bash
make demo
```shellscript
$ make run-backend
```
### More Make targets
To check all available Make rules:
```bash
make help
To execute frontend tests & linting only
```shellscript
$ make frontend-test
$ make frontend-lint
```
### Django admin
**Adding content**
Create a superuser:
You can create a basic demo site by running this command:
```bash
make superuser
```shellscript
$ make demo
```
Admin UI: <http://localhost:8071/admin>
Finally, you can check all available Make rules using this command:
## Contributing
```shellscript
$ make help
```
This project is community-driven and PRs are welcome.
**Django admin**
- [Contribution guide](CONTRIBUTING.md)
- [Translations](https://crowdin.com/project/lasuite-docs)
- [Chat with us!](https://matrix.to/#/#docs-official:matrix.org)
You can access the Django admin site at:
## Roadmap
<http://localhost:8071/admin>.
Curious where Docs is headed?
You first need to create a superuser account:
Explore upcoming features, priorities and long-term direction on our [public roadmap](https://docs.numerique.gouv.fr/docs/d1d3788e-c619-41ff-abe8-2d079da2f084/).
```shellscript
$ make superuser
```
## Feedback 🙋‍♂️🙋‍♀️
We'd love to hear your thoughts, and hear about your experiments, so come and say hi on [Matrix](https://matrix.to/#/#docs-official:matrix.org).
## Roadmap 💡
Want to know where the project is headed? [🗺️ Checkout our roadmap](https://github.com/orgs/numerique-gouv/projects/13/views/11)
## License 📝
This work is released under the MIT License (see [LICENSE](https://github.com/suitenumerique/docs/blob/main/LICENSE)).
While Docs is a public-driven initiative, our license choice is an invitation for private sector actors to use, sell and contribute to the project.
While Docs is a public-driven initiative, our license choice is an invitation for private sector actors to use, sell and contribute to the project.
## Contributing 🙌
This project is intended to be community-driven, so please, do not hesitate to [get in touch](https://matrix.to/#/#docs-official:matrix.org) if you have any question related to our implementation or design decisions.
You can help us with translations on [Crowdin](https://crowdin.com/project/lasuite-docs).
If you intend to make pull requests, see [CONTRIBUTING](https://github.com/suitenumerique/docs/blob/main/CONTRIBUTING.md) for guidelines.
## Directory structure:
```markdown
docs
├── bin - executable scripts or binaries that are used for various tasks, such as setup scripts, utility scripts, or custom commands.
├── crowdin - for crowdin translations, a tool or service that helps manage translations for the project.
├── docker - Dockerfiles and related configuration files used to build Docker images for the project. These images can be used for development, testing, or production environments.
├── docs - documentation for the project, including user guides, API documentation, and other helpful resources.
├── env.d/development - environment-specific configuration files for the development environment. These files might include environment variables, configuration settings, or other setup files needed for development.
├── gitlint - configuration files for `gitlint`, a tool that enforces commit message guidelines to ensure consistency and quality in commit messages.
├── playground - experimental or temporary code, where developers can test new features or ideas without affecting the main codebase.
└── src - main source code directory, containing the core application code, libraries, and modules of the project.
```
## Credits ❤️
### Stack
Docs is built on top of [Django Rest Framework](https://www.django-rest-framework.org/), [Next.js](https://nextjs.org/), [ProseMirror](https://prosemirror.net/), [BlockNote.js](https://www.blocknotejs.org/), [HocusPocus](https://tiptap.dev/docs/hocuspocus/introduction), and [Yjs](https://yjs.dev/). We thank the contributors of all these projects for their awesome work!
Docs is built on top of [Django Rest Framework](https://www.django-rest-framework.org/), [Next.js](https://nextjs.org/), [BlockNote.js](https://www.blocknotejs.org/), [HocusPocus](https://tiptap.dev/docs/hocuspocus/introduction) and [Yjs](https://yjs.dev/). We thank the contributors of all these projects for their awesome work!
We are proud sponsors of [BlockNotejs](https://www.blocknotejs.org/) and [Yjs](https://yjs.dev/).
We are proud sponsors of [BlockNotejs](https://www.blocknotejs.org/) and [Yjs](https://yjs.dev/).
---
### Gov ❤️ open source
Docs is the result of a joint effort led by the French 🇫🇷🥖 ([DINUM](https://www.numerique.gouv.fr/dinum/)) and German 🇩🇪🥨 governments ([ZenDiS](https://zendis.de/)).
Docs is the result of a joint initiative led by the French 🇫🇷 ([DINUM](https://www.numerique.gouv.fr/dinum/)) Government and German 🇩🇪 government ([ZenDiS](https://zendis.de/)).
We are always looking for new public partners (we are currently onboarding the Netherlands 🇳🇱), feel free to [contact us](mailto:docs@numerique.gouv.fr) if you are interested in using or contributing to Docs.
We are always looking for new public partners (we are currently onboarding the Netherlands 🇳🇱🧀), feel free to [reach out](mailto:docs@numerique.gouv.fr) if you are interested in using or contributing to Docs.
<p align="center">
<img src="/docs/assets/europe_opensource.png" width="50%"/ alt="Europe Opensource">
<img src="/docs/assets/europe_opensource.png" width="50%"/>
</p>
-39
View File
@@ -1,39 +0,0 @@
# Docs Documentation
Welcome to the official documentation for Docs.
This documentation is organized by topic and audience.
Use the section below to quickly find what you are looking for.
---
## Table of Contents
- Getting started
- [System requirements](system-requirements.md)
- [Installation overview](installation/README.md)
- [Docker Compose deployment](installation/compose.md)
- [Docker Compose examples](examples/compose/)
- [Kubernetes deployment](installation/kubernetes.md)
- [Helm values examples](examples/helm/)
- Configuration
- [Environment variables](env.md)
- [Customization](customization.md)
- [Language configuration](languages-configuration.md)
- [Search configuration](search.md)
- Architecture & design
- [Architecture overview](architecture.md)
- [Architectural Decision Records (ADR)](adr/)
- Usage & operations
- [Public instances](instances.md)
- [Releases & upgrades](release.md)
- [Troubleshooting](troubleshoot.md)
- Project & product
- [Roadmap](roadmap.md)
- Assets
- [Branding & visuals](assets/)
-77
View File
@@ -1,77 +0,0 @@
# 🌍 Public Docs Instances
This page lists known public instances of **Docs**.
These instances are operated by different organizations and may have different access policies.
If you run a public instance and would like it listed here, feel free to open a pull request.
---
## 🏛️ Public Organizations
### docs.numerique.gouv.fr
**Organization:** DINUM
**Audience:** French public agents working for central administration and extended public sphere
**Access:** ProConnect account required
<https://docs.numerique.gouv.fr/>
### docs.suite.anct.gouv.fr
**Organization:** ANCT
**Audience:** French public agents working for territorial administration and extended public sphere
**Access:** ProConnect account required
<https://docs.suite.anct.gouv.fr/>
### notes.demo.opendesk.eu
**Organization:** ZenDiS
**Type:** OpenDesk demo instance
**Access:** Request credentials
<https://notes.demo.opendesk.eu/>
---
## 🏢 Private Sector
### docs.demo.mosacloud.eu
**Organization:** mosa.cloud
**Type:** Demo instance
<https://docs.demo.mosacloud.eu/>
### notes.liiib.re
**Organization:** lasuite.coop
**Access:** Public demo
**Notes:** Content and accounts reset monthly
<https://notes.liiib.re/>
### notes.lasuite.coop
**Organization:** lasuite.coop
**Access:** Public
<https://notes.lasuite.coop/>
---
## 🤝 NGOs
### docs.federated.nexus
**Organization:** federated.nexus
**Access:** Public with account registration
<https://docs.federated.nexus/>
---
## Add your instance
To add your instance:
1. Fork the repository
2. Edit `docs/instances.md`
3. Add your instance following the existing format
4. Open a pull request
Thank you for helping grow the Docs ecosystem ❤️
-6
View File
@@ -37,12 +37,6 @@
"matchPackageNames": ["celery"],
"allowedVersions": "<5.6.0"
},
{
"groupName": "allowed pydantic-ai-slim versions",
"matchManagers": ["pep621"],
"matchPackageNames": ["pydantic-ai-slim"],
"allowedVersions": "<1.59.0"
},
{
"enabled": false,
"groupName": "ignored js dependencies",
+2 -17
View File
@@ -27,7 +27,6 @@ from django.urls import reverse
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.http import content_disposition_header
from django.utils.text import capfirst, slugify
from django.utils.translation import gettext_lazy as _
@@ -1662,19 +1661,11 @@ class DocumentViewSet(
or serializer.validated_data["is_unsafe"]
):
extra_args.update(
{
"ContentDisposition": content_disposition_header(
as_attachment=True, filename=file_name
)
}
{"ContentDisposition": f'attachment; filename="{file_name:s}"'}
)
else:
extra_args.update(
{
"ContentDisposition": content_disposition_header(
as_attachment=False, filename=file_name
)
}
{"ContentDisposition": f'inline; filename="{file_name:s}"'}
)
file = serializer.validated_data["file"]
@@ -2523,12 +2514,6 @@ class DocumentAskForAccessViewSet(
"""Create a document ask for access resource."""
document = self.get_document_or_404()
if document.get_role(request.user) in models.PRIVILEGED_ROLES:
return drf.response.Response(
{"detail": "You already have privileged access to this document."},
status=drf.status.HTTP_400_BAD_REQUEST,
)
serializer = serializers.DocumentAskForAccessCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@@ -22,7 +22,7 @@ def set_path_on_existing_documents(apps, schema_editor):
# Iterate over all existing documents and make them root nodes
documents = Document.objects.order_by("created_at").values_list("id", flat=True)
numconv = NumConv(ALPHABET)
numconv = NumConv(len(ALPHABET), ALPHABET)
updates = []
for i, pk in enumerate(documents):
@@ -9,7 +9,6 @@ import pytest
from rest_framework.test import APIClient
from core.api.serializers import UserSerializer
from core.choices import PRIVILEGED_ROLES
from core.factories import (
DocumentAskForAccessFactory,
DocumentFactory,
@@ -200,27 +199,6 @@ def test_api_documents_ask_for_access_create_authenticated_already_has_ask_for_a
assert response.json() == {"detail": "You already ask to access to this document."}
@pytest.mark.parametrize("role", PRIVILEGED_ROLES)
def test_api_documents_ask_for_access_create_authenticated_already_has_privileged_access(
role,
):
"""
Authenticated users with privileged access (owner or admin) should not be able to
create a document ask for access.
"""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
client = APIClient()
client.force_login(user)
response = client.post(f"/api/v1.0/documents/{document.id}/ask-for-access/")
assert response.status_code == 400
assert response.json() == {
"detail": "You already have privileged access to this document."
}
## List
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Breton\n"
"Language: br_FR\n"
@@ -75,7 +75,7 @@ msgstr "Ar vaezienn-mañ a zo rekis."
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "eilenn {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "Kopie von {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "copia de {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -75,7 +75,7 @@ msgstr "Ce champ est obligatoire."
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "La portée du lien '%(link_reach)s' n'est pas autorisée en fonction de la configuration du document parent."
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "copie de {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "copia di {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -75,7 +75,7 @@ msgstr "Dit veld is verplicht."
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Link bereik '%(link_reach)s' is niet toegestaan op basis van bovenliggende documentconfiguratie."
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "kopie van {title}"
@@ -516,7 +516,7 @@ msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
#: build/lib/impress/settings.py:702 impress/settings.py:702
msgid "Docs AI"
msgstr "Docs AI"
msgstr ""
#: core/templates/mail/html/template.html:153
#: core/templates/mail/text/template.txt:3
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Language: pt_PT\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "cópia de {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -75,7 +75,7 @@ msgstr "Это поле обязательное."
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Доступ по ссылке '%(link_reach)s' запрещён в соответствии с настройками родительского документа."
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "копия {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Slovenian\n"
"Language: sl_SI\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -75,7 +75,7 @@ msgstr ""
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr ""
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
@@ -75,7 +75,7 @@ msgstr "Це поле є обов’язковим."
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "Доступ до посилання '%(link_reach)s' заборонено на основі конфігурації батьківського документа."
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "копія {title}"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-06 09:37+0000\n"
"PO-Revision-Date: 2026-03-09 14:02\n"
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
"PO-Revision-Date: 2026-02-27 08:24\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -75,7 +75,7 @@ msgstr "此欄位為必填。"
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
msgstr "根據父文件設定,不允許連結範圍「%(link_reach)s」。"
#: build/lib/core/api/viewsets.py:1279 core/api/viewsets.py:1279
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
#, python-brace-format
msgid "copy of {title}"
msgstr "{title} 的副本"
+24 -24
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "4.7.0"
version = "4.6.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -26,7 +26,7 @@ readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"beautifulsoup4==4.14.3",
"boto3==1.42.59",
"boto3==1.42.17",
"Brotli==1.2.0",
"celery[redis]==5.5.3",
"django-configurations==2.5.1",
@@ -34,37 +34,37 @@ dependencies = [
"django-countries==8.2.0",
"django-csp==4.0",
"django-filter==25.2",
"django-lasuite[all]==0.0.24",
"django-lasuite[all]==0.0.22",
"django-parler==2.3",
"django-redis==6.0.0",
"django-storages[s3]==1.14.6",
"django-timezone-field>=5.1",
"django<6.0.0",
"django-treebeard==5.0.5",
"django-treebeard==4.8.0",
"djangorestframework==3.16.1",
"drf_spectacular==0.29.0",
"dockerflow==2026.1.26",
"dockerflow==2024.4.2",
"easy_thumbnails==2.10.1",
"factory_boy==3.3.3",
"gunicorn==25.1.0",
"jsonschema==4.26.0",
"langfuse==3.14.5",
"gunicorn==23.0.0",
"jsonschema==4.25.1",
"langfuse==3.11.2",
"lxml==6.0.2",
"markdown==3.10.2",
"markdown==3.10",
"mozilla-django-oidc==5.0.2",
"nested-multipart-parser==1.6.0",
"openai==2.24.0",
"psycopg[binary]==3.3.3",
"pycrdt==0.12.47",
"openai==2.21.0",
"psycopg[binary]==3.3.2",
"pycrdt==0.12.44",
"pydantic==2.12.5",
"pydantic-ai-slim[openai,logfire,web]==1.58.0",
"PyJWT==2.11.0",
"PyJWT==2.10.1",
"python-magic==0.4.27",
"redis<6.0.0",
"requests==2.32.5",
"sentry-sdk==2.53.0",
"uvicorn==0.41.0",
"whitenoise==6.12.0",
"sentry-sdk==2.48.0",
"uvicorn==0.40.0",
"whitenoise==6.11.0",
]
[project.urls]
@@ -77,21 +77,21 @@ dependencies = [
dev = [
"django-extensions==4.1",
"django-test-migrations==1.5.0",
"drf-spectacular-sidecar==2026.3.1",
"drf-spectacular-sidecar==2025.12.1",
"freezegun==1.5.5",
"ipdb==0.13.13",
"ipython==9.10.0",
"pyfakefs==6.1.3",
"pylint-django==2.7.0",
"ipython==9.8.0",
"pyfakefs==6.0.0",
"pylint-django==2.6.1",
"pylint<4.0.0",
"pytest-cov==7.0.0",
"pytest-django==4.12.0",
"pytest-django==4.11.1",
"pytest==9.0.2",
"pytest-icdiff==0.9",
"pytest-xdist==3.8.0",
"responses==0.26.0",
"ruff==0.15.4",
"types-requests==2.32.4.20260107",
"responses==0.25.8",
"ruff==0.14.10",
"types-requests==2.32.4.20250913",
]
[tool.setuptools]
@@ -41,7 +41,7 @@ test.describe('Doc Comments', () => {
// We add a comment with the first user
const editor = await writeInEditor({ page, text: 'Hello World' });
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Add comment' }).click();
await page.getByRole('button', { name: 'Comment', exact: true }).click();
const thread = page.locator('.bn-thread');
await thread.getByRole('paragraph').first().fill('This is a comment');
@@ -124,7 +124,7 @@ test.describe('Doc Comments', () => {
// Checks add react reaction
const editor = await writeInEditor({ page, text: 'Hello' });
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Add comment' }).click();
await page.getByRole('button', { name: 'Comment', exact: true }).click();
const thread = page.locator('.bn-thread');
await thread.getByRole('paragraph').first().fill('This is a comment');
@@ -191,7 +191,7 @@ test.describe('Doc Comments', () => {
/* Delete the last comment remove the thread */
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Add comment' }).click();
await page.getByRole('button', { name: 'Comment', exact: true }).click();
await thread.getByRole('paragraph').first().fill('This is a new comment');
await thread.locator('[data-test="save"]').click();
@@ -249,7 +249,9 @@ test.describe('Doc Comments', () => {
editor.getByText('Hello, I can edit the document'),
).toBeVisible();
await otherEditor.getByText('Hello').selectText();
await otherPage.getByRole('button', { name: 'Add comment' }).click();
await otherPage
.getByRole('button', { name: 'Comment', exact: true })
.click();
const otherThread = otherPage.locator('.bn-thread');
await otherThread
.getByRole('paragraph')
@@ -280,7 +282,7 @@ test.describe('Doc Comments', () => {
await expect(otherThread).toBeHidden();
await otherEditor.getByText('Hello').selectText();
await expect(
otherPage.getByRole('button', { name: 'Add comment' }),
otherPage.getByRole('button', { name: 'Comment', exact: true }),
).toBeHidden();
await otherPage.reload();
@@ -334,7 +336,7 @@ test.describe('Doc Comments', () => {
// We add a comment in the first document
const editor1 = await writeInEditor({ page, text: 'Document One' });
await editor1.getByText('Document One').selectText();
await page.getByRole('button', { name: 'Add comment' }).click();
await page.getByRole('button', { name: 'Comment', exact: true }).click();
const thread1 = page.locator('.bn-thread');
await thread1.getByRole('paragraph').first().fill('Comment in Doc One');
@@ -388,7 +390,7 @@ test.describe('Doc Comments mobile', () => {
// Checks add react reaction
const editor = await writeInEditor({ page, text: 'Hello' });
await editor.getByText('Hello').selectText();
await page.getByRole('button', { name: 'Add comment' }).click();
await page.getByRole('button', { name: 'Comment', exact: true }).click();
const thread = page.locator('.bn-thread');
await thread.getByRole('paragraph').first().fill('This is a comment');
@@ -216,9 +216,7 @@ test.describe('Doc grid move', () => {
await input.click();
await input.fill(titleDoc2);
await expect(
page.getByRole('option').first().getByText(titleDoc2),
).toBeVisible();
await expect(page.getByRole('option').getByText(titleDoc2)).toBeVisible();
// Select the first result
await page.keyboard.press('Enter');
@@ -361,9 +359,7 @@ test.describe('Doc grid move', () => {
await input.click();
await input.fill(titleDoc2);
await expect(
page.getByRole('option').first().getByText(titleDoc2),
).toBeVisible();
await expect(page.getByRole('option').getByText(titleDoc2)).toBeVisible();
// Select the first result
await page.keyboard.press('Enter');
@@ -380,9 +376,7 @@ test.describe('Doc grid move', () => {
await verifyDocName(page, titleDoc2);
const docTree = page.getByTestId('doc-tree');
await expect(docTree.getByText(titleDoc1)).toBeVisible({
timeout: 15000,
});
await expect(docTree.getByText(titleDoc1)).toBeVisible();
await cleanup();
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "4.7.0",
"version": "4.6.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "4.7.0",
"version": "4.6.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
@@ -20,15 +20,15 @@
"dependencies": {
"@ag-media/react-pdf-table": "2.0.3",
"@ai-sdk/openai": "3.0.19",
"@blocknote/code-block": "0.47.1",
"@blocknote/core": "0.47.1",
"@blocknote/mantine": "0.47.1",
"@blocknote/react": "0.47.1",
"@blocknote/xl-ai": "0.47.1",
"@blocknote/xl-docx-exporter": "0.47.1",
"@blocknote/xl-multi-column": "0.47.1",
"@blocknote/xl-odt-exporter": "0.47.1",
"@blocknote/xl-pdf-exporter": "0.47.1",
"@blocknote/code-block": "0.47.0",
"@blocknote/core": "0.47.0",
"@blocknote/mantine": "0.47.0",
"@blocknote/react": "0.47.0",
"@blocknote/xl-ai": "0.47.0",
"@blocknote/xl-docx-exporter": "0.47.0",
"@blocknote/xl-multi-column": "0.47.0",
"@blocknote/xl-odt-exporter": "0.47.0",
"@blocknote/xl-pdf-exporter": "0.47.0",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@emoji-mart/data": "1.2.1",
@@ -1,6 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9 18C8.45 18 7.97917 17.8042 7.5875 17.4125C7.19583 17.0208 7 16.55 7 16V4C7 3.45 7.19583 2.97917 7.5875 2.5875C7.97917 2.19583 8.45 2 9 2H18C18.55 2 19.0208 2.19583 19.4125 2.5875C19.8042 2.97917 20 3.45 20 4V16C20 16.55 19.8042 17.0208 19.4125 17.4125C19.0208 17.8042 18.55 18 18 18H9ZM9 16H18V4H9V16ZM5 22C4.45 22 3.97917 21.8042 3.5875 21.4125C3.19583 21.0208 3 20.55 3 20V6H5V20H16V22H5Z"
fill="currentColor"
/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 18C8.45 18 7.97917 17.8042 7.5875 17.4125C7.19583 17.0208 7 16.55 7 16V4C7 3.45 7.19583 2.97917 7.5875 2.5875C7.97917 2.19583 8.45 2 9 2H18C18.55 2 19.0208 2.19583 19.4125 2.5875C19.8042 2.97917 20 3.45 20 4V16C20 16.55 19.8042 17.0208 19.4125 17.4125C19.0208 17.8042 18.55 18 18 18H9ZM9 16H18V4H9V16ZM5 22C4.45 22 3.97917 21.8042 3.5875 21.4125C3.19583 21.0208 3 20.55 3 20V6H5V20H16V22H5Z" fill="#222631"/>
</svg>

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 524 B

@@ -1,6 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z"
fill="currentColor"
/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z" fill="#222631"/>
</svg>

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 355 B

@@ -1,6 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 21C9.7 21 7.69583 20.2375 5.9875 18.7125C4.27917 17.1875 3.3 15.2833 3.05 13H5.1C5.33333 14.7333 6.10417 16.1667 7.4125 17.3C8.72083 18.4333 10.25 19 12 19C13.95 19 15.6042 18.3208 16.9625 16.9625C18.3208 15.6042 19 13.95 19 12C19 10.05 18.3208 8.39583 16.9625 7.0375C15.6042 5.67917 13.95 5 12 5C10.85 5 9.775 5.26667 8.775 5.8C7.775 6.33333 6.93333 7.06667 6.25 8H9V10H3V4H5V6.35C5.85 5.28333 6.8875 4.45833 8.1125 3.875C9.3375 3.29167 10.6333 3 12 3C13.25 3 14.4208 3.2375 15.5125 3.7125C16.6042 4.1875 17.5542 4.82917 18.3625 5.6375C19.1708 6.44583 19.8125 7.39583 20.2875 8.4875C20.7625 9.57917 21 10.75 21 12C21 13.25 20.7625 14.4208 20.2875 15.5125C19.8125 16.6042 19.1708 17.5542 18.3625 18.3625C17.5542 19.1708 16.6042 19.8125 15.5125 20.2875C14.4208 20.7625 13.25 21 12 21ZM14.8 16.2L11 12.4V7H13V11.6L16.2 14.8L14.8 16.2Z"
fill="currentColor"
/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 21C9.7 21 7.69583 20.2375 5.9875 18.7125C4.27917 17.1875 3.3 15.2833 3.05 13H5.1C5.33333 14.7333 6.10417 16.1667 7.4125 17.3C8.72083 18.4333 10.25 19 12 19C13.95 19 15.6042 18.3208 16.9625 16.9625C18.3208 15.6042 19 13.95 19 12C19 10.05 18.3208 8.39583 16.9625 7.0375C15.6042 5.67917 13.95 5 12 5C10.85 5 9.775 5.26667 8.775 5.8C7.775 6.33333 6.93333 7.06667 6.25 8H9V10H3V4H5V6.35C5.85 5.28333 6.8875 4.45833 8.1125 3.875C9.3375 3.29167 10.6333 3 12 3C13.25 3 14.4208 3.2375 15.5125 3.7125C16.6042 4.1875 17.5542 4.82917 18.3625 5.6375C19.1708 6.44583 19.8125 7.39583 20.2875 8.4875C20.7625 9.57917 21 10.75 21 12C21 13.25 20.7625 14.4208 20.2875 15.5125C19.8125 16.6042 19.1708 17.5542 18.3625 18.3625C17.5542 19.1708 16.6042 19.8125 15.5125 20.2875C14.4208 20.7625 13.25 21 12 21ZM14.8 16.2L11 12.4V7H13V11.6L16.2 14.8L14.8 16.2Z" fill="#222631"/>
</svg>

Before

Width:  |  Height:  |  Size: 962 B

After

Width:  |  Height:  |  Size: 967 B

@@ -9,12 +9,11 @@ import { Button, Popover } from 'react-aria-components';
import styled, { css } from 'styled-components';
import { useCunninghamTheme } from '@/cunningham';
import { useFocusStore } from '@/stores';
import { BoxProps } from './Box';
const StyledPopover = styled(Popover)`
background-color: var(--c--contextuals--background--surface--primary);
background-color: white;
border-radius: var(--c--globals--spacings--st);
box-shadow: 0 0 6px 0 rgba(0, 0, 145, 0.1);
border: 1px solid var(--c--contextuals--border--surface--primary);
@@ -71,9 +70,8 @@ export const DropButton = ({
const { themeTokens } = useCunninghamTheme();
const font = themeTokens['font']?.['families']['base'];
const [isLocalOpen, setIsLocalOpen] = useState(isOpen);
const addLastFocus = useFocusStore((state) => state.addLastFocus);
const triggerRef = useRef<HTMLButtonElement | null>(null);
const triggerRef = useRef(null);
useEffect(() => {
setIsLocalOpen(isOpen);
@@ -88,10 +86,7 @@ export const DropButton = ({
<>
<StyledButton
ref={triggerRef}
onPress={() => {
addLastFocus(triggerRef.current);
onOpenChangeHandler(true);
}}
onPress={() => onOpenChangeHandler(true)}
aria-label={label}
data-testid={testId}
$css={css`
@@ -193,7 +193,7 @@ export const DropdownMenu = ({
key={option.label}
$align="center"
$justify="space-between"
$background="var(--c--contextuals--background--surface--primary)"
$background={colorsTokens['gray-000']}
$color={colorsTokens['brand-600']}
$padding={{ vertical: 'xs', horizontal: 'base' }}
$width="100%"
@@ -250,21 +250,15 @@ export const DropdownMenu = ({
{option.icon && typeof option.icon === 'string' && (
<Icon
$size="20px"
$theme="neutral"
$theme="gray"
$variation={isDisabled ? 'tertiary' : 'primary'}
iconName={option.icon}
aria-hidden="true"
/>
)}
{option.icon && typeof option.icon !== 'string' && (
<Box
$theme="neutral"
$variation={isDisabled ? 'tertiary' : 'primary'}
>
{option.icon}
</Box>
)}
{option.icon &&
typeof option.icon !== 'string' &&
option.icon}
<Text $variation={isDisabled ? 'tertiary' : 'primary'}>
{option.label}
</Text>
@@ -52,7 +52,6 @@ export const AlertModal = ({
return (
<Modal
closeOnClickOutside
isOpen={isOpen}
size={ModalSize.MEDIUM}
onClose={onClose}
@@ -76,18 +76,6 @@
background-color: transparent;
}
.c__modal__close .c__button {
right: 0;
top: 0;
position: relative;
}
.c__modal__close {
position: absolute;
top: 4px;
right: 4px;
}
/**
* Tooltip
*/
@@ -40,22 +40,14 @@ const AIMenuStyle = createGlobalStyle`
height: 18px;
width: 18px;
}
.--docs--ai-menu {
input[name="ai-prompt"]{
padding-inline-start: 3rem;
}
.mantine-TextInput-wrapper {
display: flex
}
.mantine-TextInput-section[data-position="left"] {
margin-inline: 0.75rem;
}
.mantine-TextInput-section[data-position="right"] {
inset-inline-end: var(--c--globals--spacings--sm);
position: relative;
flex-shrink: 0;
width: auto;
}
.--docs--ai-menu input[name="ai-prompt"]{
padding-inline-start: 3rem;
}
.--docs--ai-menu .mantine-TextInput-section[data-position="left"] {
margin-inline: 0.75rem;
}
.--docs--ai-menu .mantine-TextInput-section[data-position="right"] {
inset-inline-end: 2rem;
}
`;
@@ -8,7 +8,7 @@ import {
} from '@blocknote/core';
import { CommentsExtension } from '@blocknote/core/comments';
import '@blocknote/core/fonts/inter.css';
import * as localesBN from '@blocknote/core/locales';
import * as locales from '@blocknote/core/locales';
import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css';
import { useCreateBlockNote } from '@blocknote/react';
@@ -26,7 +26,7 @@ import { Doc, useProviderStore } from '@/docs/doc-management';
import { avatarUrlFromName, useAuth } from '@/features/auth';
import { useAnalytics } from '@/libs/Analytics';
import { AI_FEATURE_FLAG, DEFAULT_LOCALE } from '../conf';
import { AI_FEATURE_FLAG } from '../conf';
import {
useHeadings,
useSaveDoc,
@@ -52,14 +52,14 @@ import {
const AIMenu = BlockNoteAI?.AIMenu;
const AIMenuController = BlockNoteAI?.AIMenuController;
const useAI = BlockNoteAI?.useAI;
const localesBNAI = BlockNoteAI?.localesAI;
const localesAI = BlockNoteAI?.localesAI;
import {
InterlinkingLinkInlineContent,
InterlinkingSearchInlineContent,
} from './custom-inline-content';
import XLMultiColumn from './xl-multi-column';
const localesBNMultiColumn = XLMultiColumn?.locales;
const multiColumnLocales = XLMultiColumn?.locales;
const withMultiColumn = XLMultiColumn?.withMultiColumn;
const baseBlockNoteSchema = withPageBreak(
@@ -100,20 +100,10 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
const { i18n, t } = useTranslation();
const langLocalesBN =
!i18n.resolvedLanguage || !(i18n.resolvedLanguage in localesBN)
? DEFAULT_LOCALE
: i18n.resolvedLanguage;
const langLocalesBNMultiColumn =
!i18n.resolvedLanguage ||
!localesBNMultiColumn ||
!(i18n.resolvedLanguage in localesBNMultiColumn)
? DEFAULT_LOCALE
: i18n.resolvedLanguage;
const langLocalesBNAI =
!i18n.resolvedLanguage || !(i18n.resolvedLanguage in localesBNAI)
? DEFAULT_LOCALE
: i18n.resolvedLanguage;
let lang = i18n.resolvedLanguage;
if (!lang || !(lang in locales)) {
lang = 'en';
}
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
const conf = useConfig().data;
@@ -190,13 +180,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
showCursorLabels: showCursorLabels as 'always' | 'activity',
},
dictionary: {
...localesBN[langLocalesBN as keyof typeof localesBN],
...(localesBNMultiColumn && {
...locales[lang as keyof typeof locales],
...(multiColumnLocales && {
multi_column:
localesBNMultiColumn[
langLocalesBNMultiColumn as keyof typeof localesBNMultiColumn
],
ai: localesBNAI?.[langLocalesBNAI as keyof typeof localesBNAI],
multiColumnLocales[lang as keyof typeof multiColumnLocales],
ai: localesAI?.[lang as keyof typeof localesAI],
}),
},
pasteHandler: ({ event, defaultPasteHandler }) => {
@@ -240,9 +228,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
[
aiExtension,
cursorName,
langLocalesBN,
langLocalesBNMultiColumn,
langLocalesBNAI,
lang,
provider,
uploadFile,
threadStore,
@@ -72,11 +72,11 @@ export const BlockNoteToolbar = ({ aiAllowed }: { aiAllowed: boolean }) => {
const formattingToolbar = useCallback(() => {
return (
<FormattingToolbar>
{toolbarItems}
{aiAllowed && AIToolbarButton && <AIToolbarButton />}
<CommentToolbarButton />
{aiAllowed && AIToolbarButton && <AIToolbarButton />}
{toolbarItems}
{/* Extra button to do some AI powered actions - only if AIToolbarButton is not available because of MIT license */}
{conf?.AI_FEATURE_ENABLED && conf?.AI_FEATURE_LEGACY_ENABLED && (
@@ -24,7 +24,6 @@ export const ModalConfirmDownloadUnsafe = ({
<>
<Button
aria-label={t('Cancel the download')}
autoFocus
variant="secondary"
onClick={() => onClose()}
>
@@ -59,7 +59,7 @@ export const DocEditorContainer = ({
<Box $css="flex:1;" $position="relative" $width="100%">
<Box
$padding={{ top: 'md', bottom: '2rem' }}
$background="var(--c--contextuals--background--surface--primary)"
$background="white"
className={clsx('--docs--editor-container', {
'--docs--doc-readonly': readOnly,
'--docs--doc-deleted': isDeletedDoc,
@@ -29,7 +29,7 @@ export const CommentToolbarButton = () => {
const Components = useComponentsContext();
const { currentDoc } = useDocStore();
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
const comments = useExtension('comments') as unknown as ReturnType<
ReturnType<typeof CommentsExtension>
>;
@@ -78,16 +78,22 @@ export const CommentToolbarButton = () => {
}}
aria-haspopup="dialog"
data-test="comment-toolbar-button"
aria-label={t('Add comment')}
mainTooltip={t('Add comment')}
>
<Icon
iconName="comment"
className="--docs--icon-bg"
$theme="gray"
$padding="0.15rem"
$size="md"
/>
<Box
$direction="row"
$align="center"
$gap={spacingsTokens['xs']}
$padding={{ right: '2xs' }}
>
<Icon
iconName="comment"
className="--docs--icon-bg"
$theme="gray"
$padding="0.15rem"
$size="md"
/>
{t('Comment')}
</Box>
</Components.Generic.Toolbar.Button>
<Box
$background={colorsTokens['gray-100']}
@@ -1,3 +1,2 @@
export const ANALYZE_URL = 'media-check';
export const AI_FEATURE_FLAG = 'ai_blocknote';
export const DEFAULT_LOCALE = 'en';
@@ -203,7 +203,6 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
aria-label={t('Cancel the download')}
variant="secondary"
fullWidth
autoFocus
onClick={() => onClose()}
>
{t('Cancel')}
@@ -75,12 +75,7 @@ export const AlertNetworkModal = ({ onClose }: AlertNetworkModalProps) => {
onClose={() => onClose()}
rightActions={
<>
<Button
aria-label={t('OK')}
onClick={onClose}
color="error"
autoFocus
>
<Button aria-label={t('OK')} onClick={onClose} color="error">
{t('I understand')}
</Button>
</>
@@ -6,7 +6,6 @@ import { css } from 'styled-components';
import { Box, Icon } from '@/components';
import { Doc } from '@/docs/doc-management';
import { useFocusStore } from '@/stores';
interface BoutonShareProps {
displayNbAccess: boolean;
@@ -24,7 +23,6 @@ export const BoutonShare = ({
open,
}: BoutonShareProps) => {
const { t } = useTranslation();
const addLastFocus = useFocusStore((state) => state.addLastFocus);
const treeContext = useTreeContext<Doc>();
/**
@@ -65,10 +63,7 @@ export const BoutonShare = ({
disabled={isDisabled}
/>
}
onClick={(e) => {
addLastFocus(e.currentTarget as HTMLElement);
open();
}}
onClick={open}
size="medium"
disabled={isDisabled}
>
@@ -82,10 +77,7 @@ export const BoutonShare = ({
<Button
color="brand"
variant="tertiary"
onClick={(e) => {
addLastFocus(e.currentTarget as HTMLElement);
open();
}}
onClick={open}
size="medium"
disabled={isDisabled}
>
@@ -44,7 +44,7 @@ import {
KEY_LIST_DOC_VERSIONS,
ModalSelectVersion,
} from '@/docs/doc-versioning';
import { useFocusStore, useResponsiveStore } from '@/stores';
import { useResponsiveStore } from '@/stores';
import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard';
@@ -70,7 +70,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const selectHistoryModal = useModal();
const modalShare = useModal();
const { addLastFocus, restoreFocus } = useFocusStore();
const { isSmallMobile, isMobile } = useResponsiveStore();
const copyDocLink = useCopyDocLink(doc.id);
const { mutate: duplicateDoc } = useDuplicateDoc({
@@ -225,8 +224,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
icon={
<Icon iconName="download" $color="inherit" aria-hidden={true} />
}
onClick={(e) => {
addLastFocus(e.currentTarget as HTMLElement);
onClick={() => {
setIsModalExportOpen(true);
}}
size={isSmallMobile ? 'small' : 'medium'}
@@ -251,29 +249,17 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
{modalShare.isOpen && (
<DocShareModal
onClose={() => {
modalShare.close();
restoreFocus();
}}
onClose={() => modalShare.close()}
doc={doc}
isRootDoc={treeContext?.root?.id === doc.id}
/>
)}
{isModalExportOpen && ModalExport && (
<ModalExport
onClose={() => {
setIsModalExportOpen(false);
restoreFocus();
}}
doc={doc}
/>
<ModalExport onClose={() => setIsModalExportOpen(false)} doc={doc} />
)}
{isModalRemoveOpen && (
<ModalRemoveDoc
onClose={() => {
setIsModalRemoveOpen(false);
restoreFocus();
}}
onClose={() => setIsModalRemoveOpen(false)}
doc={doc}
onSuccess={() => {
const isTopParent = doc.id === treeContext?.root?.id;
@@ -295,10 +281,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
)}
{selectHistoryModal.isOpen && (
<ModalSelectVersion
onClose={() => {
selectHistoryModal.close();
restoreFocus();
}}
onClose={() => selectHistoryModal.close()}
doc={doc}
/>
)}
@@ -31,7 +31,7 @@ export const FloatingBar = () => {
margin-left: calc(-${base});
margin-right: calc(-${base});
margin-top: calc(-${base});
z-index: 21; // Under editor select box but above other elements (e.g., doc title, suggestion menu)
z-index: 1000;
display: flex;
align-items: flex-start;
justify-content: flex-start;
@@ -13,7 +13,6 @@ import {
TextType,
emojidata,
} from '@/components';
import { useFocusStore } from '@/stores';
import { useDocTitleUpdate } from '../hooks/useDocTitleUpdate';
@@ -42,7 +41,6 @@ export const DocIcon = ({
}: DocIconProps) => {
const { updateDocEmoji } = useDocTitleUpdate();
const { t } = useTranslation();
const { addLastFocus, restoreFocus } = useFocusStore();
const iconRef = useRef<HTMLDivElement>(null);
@@ -92,16 +90,12 @@ export const DocIcon = ({
});
}
if (!openEmojiPicker) {
addLastFocus(iconRef.current);
}
setOpenEmojiPicker(!openEmojiPicker);
}
};
const handleEmojiSelect = ({ native }: { native: string }) => {
setOpenEmojiPicker(false);
restoreFocus();
// Update document emoji if docId is provided
if (docId && title !== undefined) {
@@ -114,7 +108,6 @@ export const DocIcon = ({
const handleClickOutside = () => {
setOpenEmojiPicker(false);
restoreFocus();
};
return (
@@ -38,7 +38,6 @@ export const ModalRemoveDoc = ({
const { push } = useRouter();
const { hasChildren } = useDocUtils(doc);
const cancelButtonRef = useRef<ButtonElement>(null);
const {
mutate: removeDoc,
isError,
@@ -61,14 +60,17 @@ export const ModalRemoveDoc = ({
},
},
});
// react-aria Popover restores focus to its trigger asynchronously
// when closing, which races with autoFocus when the modal is opened
// from a dropdown. This ensures focus wins after that restoration.
useEffect(() => {
const id = requestAnimationFrame(() => {
cancelButtonRef.current?.focus();
});
return () => cancelAnimationFrame(id);
const TIMEOUT_MODAL_MOUNTING = 100;
const timeoutId = setTimeout(() => {
const buttonElement = cancelButtonRef.current;
if (buttonElement) {
buttonElement.focus();
}
}, TIMEOUT_MODAL_MOUNTING);
return () => clearTimeout(timeoutId);
}, []);
const keyboardAction = useKeyboardAction();
@@ -98,7 +100,6 @@ export const ModalRemoveDoc = ({
aria-label={t('Cancel the deletion')}
variant="secondary"
fullWidth
autoFocus
onClick={handleClose}
onKeyDown={handleCloseKeyDown}
>
@@ -58,11 +58,7 @@ export const Heading = ({
});
}}
$radius="var(--c--globals--spacings--st)"
$background={
isActive
? 'var(--c--contextuals--background--semantic--neutral--secondary)'
: 'none'
}
$background={isActive ? `${colorsTokens['gray-100']}` : 'none'}
$css={css`
text-align: left;
&:focus-visible {
@@ -61,7 +61,7 @@ export const TableContent = () => {
$width={!isOpen ? '40px' : '200px'}
$height={!isOpen ? '40px' : 'auto'}
$maxHeight="calc(50vh - 60px)"
$zIndex={1000}
$zIndex={2000}
$align="center"
$padding={isOpen ? 'xs' : '0'}
$justify="center"
@@ -72,7 +72,7 @@ export const TableContent = () => {
border: 1px solid ${colorsTokens['brand-100']};
overflow: hidden;
border-radius: ${spacingsTokens['3xs']};
background: var(--c--contextuals--background--surface--primary);
background: ${colorsTokens['gray-000']};
${isOpen &&
css`
display: flex;
@@ -71,7 +71,7 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
allChildren as TreeViewDataType<Doc>[],
);
treeContext?.treeData.setSelectedNode(createdDoc);
togglePanel({ type: 'mobile' });
togglePanel();
})
.catch(console.error);
} else {
@@ -85,7 +85,7 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
node.open();
router.push(`/docs/${createdDoc.id}`);
treeContext?.treeData.setSelectedNode(newDoc);
togglePanel({ type: 'mobile' });
togglePanel();
}
};
@@ -99,16 +99,11 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
const buttonOptionRef = useRef<HTMLDivElement | null>(null);
const handleKeyDown = (e: React.KeyboardEvent) => {
const target = e.target as HTMLElement | null;
const isInActions = !!target?.closest('.light-doc-item-actions');
const isOnEmojiButton = !!target?.closest('.--docs--doc-icon');
const shouldOpenActions =
!menuOpen && !isInActions && (node.isFocused || isOnEmojiButton);
// F2: focus first action button
const shouldOpenActions = !menuOpen && node.isFocused;
if (e.key === 'F2' && shouldOpenActions) {
buttonOptionRef.current?.focus();
e.stopPropagation();
e.preventDefault();
return;
}
};
@@ -135,7 +130,7 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
aria-disabled={isDisabled}
onKeyDown={handleKeyDown}
$css={css`
background-color: var(--c--contextuals--background--surface--primary);
background-color: var(--c--globals--colors--gray-000);
.light-doc-item-actions {
display: ${menuOpen || !isDesktop ? 'flex' : 'none'};
right: var(--c--globals--spacings--0);
@@ -97,24 +97,16 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
// Handle keyboard navigation for root item
const handleRootKeyDown = useCallback(
(e: React.KeyboardEvent) => {
const target = e.target as HTMLElement | null;
const isInActions = !!target?.closest('.doc-tree-root-item-actions');
const isOnEmojiButton = !!target?.closest('.--docs--doc-icon');
const isOnRootItem = target === e.currentTarget;
if (e.key === 'F2' && !rootActionsOpen && !isInActions) {
if (
isOnEmojiButton ||
isOnRootItem ||
target?.classList.contains('c__tree-view--node')
) {
e.preventDefault();
rootButtonOptionRef.current?.focus();
}
// F2: focus first action button
if (e.key === 'F2' && !rootActionsOpen) {
e.preventDefault();
rootButtonOptionRef.current?.focus();
return;
}
if (isInActions) {
// Ignore if focus is in actions
const target = e.target as HTMLElement | null;
if (target?.closest('.doc-tree-root-item-actions')) {
return;
}
@@ -287,7 +279,6 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
padding: ${spacingsTokens['2xs']};
border-radius: var(--c--globals--spacings--st);
width: 100%;
min-width: 200px;
background-color: ${rootIsSelected || rootActionsOpen
? 'var(--c--contextuals--background--semantic--contextual--primary)'
: 'transparent'};
@@ -305,8 +296,8 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
}
.doc-tree-root-item-actions {
display: flex;
opacity: ${rootActionsOpen ? '1' : '0'};
display: ${rootActionsOpen ? 'flex' : 'none'};
&:has(.isOpen) {
opacity: 1;
@@ -76,7 +76,6 @@ export const ModalConfirmationVersion = ({
aria-label={`${t('Cancel')} - ${t('Warning')}`}
variant="secondary"
fullWidth
autoFocus
onClick={() => onClose()}
>
{t('Cancel')}
@@ -132,7 +132,6 @@ export const ModalSelectVersion = ({
</Text>
<ButtonCloseModal
aria-label={t('Close the version history modal')}
autoFocus
onClick={onClose}
size="nano"
/>
@@ -167,13 +167,9 @@ export const DocMoveModal = ({
$weight="normal"
$textAlign="left"
>
<Trans
i18nKey="Choose the new location for <strong>{{title}}</strong>."
values={{
title: docTitle,
}}
components={{ strong: <strong /> }}
/>
<Trans t={t}>
Choose the new location for <strong>{docTitle}</strong>.
</Trans>
</Text>
</Box>
<HorizontalSeparator />
@@ -20,8 +20,6 @@ import {
useTrans,
} from '@/docs/doc-management';
import { DocShareModal } from '@/docs/doc-share';
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
import { useFocusStore } from '@/stores';
import { DocMoveModal } from './DocMoveModal';
@@ -31,21 +29,13 @@ interface DocsGridActionsProps {
export const DocsGridActions = ({ doc }: DocsGridActionsProps) => {
const { t } = useTranslation();
const restoreFocus = useFocusStore((state) => state.restoreFocus);
const deleteModal = useModal();
const shareModal = useModal();
const importModal = useModal();
const { untitledDocument } = useTrans();
const { mutate: duplicateDoc } = useDuplicateDoc({
onSuccess: () => {
const mainContent = document.getElementById(MAIN_LAYOUT_ID);
if (mainContent) {
requestAnimationFrame(() => mainContent.focus());
}
},
});
const { mutate: duplicateDoc } = useDuplicateDoc();
const removeFavoriteDoc = useDeleteFavoriteDoc({
listInvalidQueries: [KEY_LIST_DOC, KEY_LIST_FAVORITE_DOC],
@@ -145,30 +135,15 @@ export const DocsGridActions = ({ doc }: DocsGridActionsProps) => {
</DropdownMenu>
{deleteModal.isOpen && (
<ModalRemoveDoc
onClose={() => {
deleteModal.onClose();
restoreFocus();
}}
doc={doc}
/>
<ModalRemoveDoc onClose={deleteModal.onClose} doc={doc} />
)}
{shareModal.isOpen && (
<DocShareModal
doc={doc}
onClose={() => {
shareModal.close();
restoreFocus();
}}
/>
<DocShareModal doc={doc} onClose={shareModal.close} />
)}
{importModal.isOpen && (
<DocMoveModal
doc={doc}
onClose={() => {
importModal.close();
restoreFocus();
}}
onClose={importModal.close}
isOpen={importModal.isOpen}
/>
)}
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next';
import { Box, Icon, Text } from '@/components';
import { Doc } from '@/docs/doc-management';
import { DocShareModal } from '@/docs/doc-share';
import { useFocusStore } from '@/stores';
type Props = {
doc: Doc;
@@ -15,7 +14,6 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
const sharedCount = doc.nb_accesses_direct;
const isShared = sharedCount - 1 > 0;
const shareModal = useModal();
const { addLastFocus, restoreFocus } = useFocusStore();
if (!isShared) {
return <Box $minWidth="50px">&nbsp;</Box>;
@@ -42,7 +40,6 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
addLastFocus(event.currentTarget as HTMLElement);
shareModal.open();
}}
color="brand"
@@ -63,13 +60,7 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
</Button>
</Tooltip>
{shareModal.isOpen && (
<DocShareModal
doc={doc}
onClose={() => {
shareModal.close();
restoreFocus();
}}
/>
<DocShareModal doc={doc} onClose={shareModal.close} />
)}
</>
);
@@ -4,14 +4,14 @@ import { useTranslation } from 'react-i18next';
import { Icon } from '@/components/';
import { useLeftPanelStore } from '@/features/left-panel';
export const LeftPanelToggleMobile = () => {
export const ButtonTogglePanel = () => {
const { t } = useTranslation();
const { isPanelOpenMobile, togglePanel } = useLeftPanelStore();
return (
<Button
size="medium"
onClick={() => togglePanel({ type: 'mobile' })}
onClick={() => togglePanel()}
aria-label={t(
isPanelOpenMobile ? 'Close the header menu' : 'Open the header menu',
)}
@@ -23,7 +23,7 @@ export const LeftPanelToggleMobile = () => {
iconName={isPanelOpenMobile ? 'close' : 'menu'}
/>
}
className="--docs--button-toggle-panel-mobile"
className="--docs--button-toggle-panel"
data-testid="header-menu-toggle"
/>
);
@@ -7,11 +7,11 @@ import { useConfig } from '@/core/config';
import { useCunninghamTheme } from '@/cunningham';
import { ButtonLogin } from '@/features/auth';
import { LanguagePicker } from '@/features/language';
import { LeftPanelToggleMobile } from '@/features/left-panel';
import { useResponsiveStore } from '@/stores';
import { HEADER_HEIGHT } from '../conf';
import { ButtonTogglePanel } from './ButtonTogglePanel';
import { Title } from './Title';
import { Waffle } from './Waffle';
@@ -46,7 +46,7 @@ export const Header = () => {
var(--c--contextuals--border--surface--primary);
`}
>
{!isDesktop && <LeftPanelToggleMobile />}
{!isDesktop && <ButtonTogglePanel />}
<StyledLink
href="/"
data-testid="header-logo-link"
@@ -1,3 +1,4 @@
export * from './ButtonTogglePanel';
export * from './Header';
export * from './Waffle';
export * from './Title';
@@ -3,9 +3,9 @@ import Image from 'next/image';
import { Box } from '@/components';
import { useConfig } from '@/core';
import { useCunninghamTheme } from '@/cunningham';
import { Title, Waffle } from '@/features/header';
import { ButtonTogglePanel, Title } from '@/features/header/';
import { Waffle } from '@/features/header/components/Waffle';
import { LanguagePicker } from '@/features/language';
import { LeftPanelToggleMobile } from '@/features/left-panel';
import { useResponsiveStore } from '@/stores';
export const HEADER_HEIGHT = 91;
@@ -42,7 +42,7 @@ export const HomeHeader = () => {
>
{isSmallMobile && (
<Box $position="absolute" $css="left: 1rem;">
<LeftPanelToggleMobile />
<ButtonTogglePanel />
</Box>
)}
{!isSmallMobile && logo?.src && (
@@ -7,12 +7,14 @@ import { Box, Icon, StyledLink, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { DocDefaultFilter } from '@/docs/doc-management';
import { useLeftPanelStore } from '@/features/left-panel';
import { useResponsiveStore } from '@/stores';
export const LeftPanelTargetFilters = () => {
const { t } = useTranslation();
const pathname = usePathname();
const searchParams = useSearchParams();
const { isDesktop } = useResponsiveStore();
const { closePanel } = useLeftPanelStore();
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
@@ -50,7 +52,9 @@ export const LeftPanelTargetFilters = () => {
};
const handleFilterClick = () => {
closePanel({ type: 'mobile' });
if (!isDesktop) {
closePanel();
}
};
return (
@@ -1,3 +1,5 @@
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { createGlobalStyle, css } from 'styled-components';
@@ -24,9 +26,15 @@ export const LeftPanel = () => {
const { t } = useTranslation();
const { spacingsTokens } = useCunninghamTheme();
const { isPanelOpen, isPanelOpenMobile } = useLeftPanelStore();
const { togglePanel, isPanelOpen, isPanelOpenMobile } = useLeftPanelStore();
const isPanelOpenState = isDesktop ? isPanelOpen : isPanelOpenMobile;
const pathname = usePathname();
useEffect(() => {
togglePanel(isDesktop);
}, [pathname, isDesktop, togglePanel]);
return (
<>
{isDesktop && (
@@ -36,9 +44,7 @@ export const LeftPanel = () => {
height: calc(100vh - ${HEADER_HEIGHT}px);
width: 100%;
overflow: hidden;
background-color: var(
--c--contextuals--background--surface--primary
);
background-color: var(--c--globals--colors--gray-000);
`}
className="--docs--left-panel-desktop"
as="nav"
@@ -67,9 +73,7 @@ export const LeftPanel = () => {
border-right: 1px solid var(--c--globals--colors--gray-200);
position: fixed;
transform: translateX(${isPanelOpenState ? '0' : '-100dvw'});
background-color: var(
--c--contextuals--background--surface--primary
);
background-color: var(--c--globals--colors--gray-000);
overflow-y: auto;
overflow-x: hidden;
`}
@@ -71,7 +71,7 @@ export const LeftPanelCollapseButton = () => {
>
<Button
size="small"
onClick={() => togglePanel({ type: 'desktop' })}
onClick={() => togglePanel()}
aria-label={ariaLabel}
aria-expanded={isPanelOpen}
color="neutral"
@@ -38,7 +38,7 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
const goToHome = () => {
void router.push('/');
togglePanel({ type: 'mobile' });
togglePanel();
};
return (
@@ -4,8 +4,9 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Icon } from '@/components';
import { useCreateDoc } from '@/docs/doc-management';
import { useCreateDoc } from '@/features/docs/doc-management';
import { useSkeletonStore } from '@/features/skeletons';
import { useResponsiveStore } from '@/stores';
import { useLeftPanelStore } from '../stores';
@@ -13,6 +14,7 @@ export const LeftPanelHeaderButton = () => {
const router = useRouter();
const { t } = useTranslation();
const { closePanel } = useLeftPanelStore();
const { isDesktop } = useResponsiveStore();
const { setIsSkeletonVisible } = useSkeletonStore();
const [isNavigating, setIsNavigating] = useState(false);
@@ -25,7 +27,9 @@ export const LeftPanelHeaderButton = () => {
.then(() => {
// The skeleton will be disabled by the [id] page once the data is loaded
setIsNavigating(false);
closePanel({ type: 'mobile' });
if (!isDesktop) {
closePanel();
}
})
.catch(() => {
// In case of navigation error, disable the skeleton
@@ -6,15 +6,17 @@ import {
PanelResizeHandle,
} from 'react-resizable-panels';
import { useLeftPanelStore } from '@/features/left-panel/stores';
import { useResponsiveStore } from '@/stores';
import { useLeftPanelStore } from '../stores';
// Convert a target pixel width to a percentage of the current viewport width.
const pxToPercent = (px: number) => {
return (px / window.innerWidth) * 100;
};
const PANEL_TOGGLE_TRANSITION =
'flex-grow 180ms var(--c--globals--transitions--ease-out), flex-basis 180ms var(--c--globals--transitions--ease-out)';
type ResizableLeftPanelProps = {
leftPanel: React.ReactNode;
children: React.ReactNode;
@@ -32,58 +34,70 @@ export const ResizableLeftPanel = ({
const { isPanelOpen } = useLeftPanelStore();
const ref = useRef<ImperativePanelHandle>(null);
const savedWidthPxRef = useRef<number>(minPanelSizePx);
const [isDragging, setIsDragging] = useState(false);
const previousPanelOpenRef = useRef<boolean>(isPanelOpen);
const [isToggleAnimating, setIsToggleAnimating] = useState(false);
const minPanelSizePercent = pxToPercent(minPanelSizePx);
const maxPanelSizePercent = Math.min(pxToPercent(maxPanelSizePx), 40);
const [panelSizePercent, setPanelSizePercent] = useState(() => {
const initialSize = pxToPercent(minPanelSizePx);
return Math.max(
minPanelSizePercent,
Math.min(initialSize, maxPanelSizePercent),
);
});
/**
* When the panel is toggled open/closed, we want
* to either expand/collapse
*/
useEffect(() => {
if (!ref.current || !isDesktop) {
return;
}
if (isPanelOpen) {
const syncPanelState = () => {
if (!ref.current || !isDesktop) {
return;
}
if (!isPanelOpen) {
ref.current.collapse();
return;
}
const restoredSizePercent = Math.max(
minPanelSizePercent,
Math.min(pxToPercent(savedWidthPxRef.current), maxPanelSizePercent),
);
ref.current.expand();
} else {
ref.current.collapse();
}
}, [isPanelOpen, isDesktop]);
ref.current.resize(restoredSizePercent);
};
// Keep pixel width constant on window resize
useEffect(() => {
if (!isDesktop) {
const hasPanelToggleChanged = previousPanelOpenRef.current !== isPanelOpen;
previousPanelOpenRef.current = isPanelOpen;
if (hasPanelToggleChanged) {
setIsToggleAnimating(true);
const animationFrameId = requestAnimationFrame(() => {
syncPanelState();
});
const timeoutId = setTimeout(() => {
setIsToggleAnimating(false);
}, 180);
return () => {
window.cancelAnimationFrame(animationFrameId);
window.clearTimeout(timeoutId);
};
}
syncPanelState();
if (!isDesktop || !isPanelOpen) {
return;
}
const handleResize = () => {
const newPercent = pxToPercent(savedWidthPxRef.current);
setPanelSizePercent(newPercent);
if (ref.current) {
ref.current.resize?.(newPercent - (ref.current.getSize() || 0));
}
syncPanelState();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [isDesktop]);
}, [isDesktop, isPanelOpen, minPanelSizePercent, maxPanelSizePercent]);
const handleResize = (sizePercent: number) => {
const widthPx = (sizePercent / 100) * window.innerWidth;
savedWidthPxRef.current = widthPx;
setPanelSizePercent(sizePercent);
if (isDesktop && sizePercent > 0) {
const widthPx = (sizePercent / 100) * window.innerWidth;
savedWidthPxRef.current = widthPx;
}
};
return (
@@ -91,19 +105,21 @@ export const ResizableLeftPanel = ({
<Panel
ref={ref}
className="--docs--resizable-left-panel"
collapsible={!isPanelOpen}
order={0}
collapsible
collapsedSize={0}
style={{
transition: isDragging
? 'none'
: 'flex var(--c--globals--transitions--duration) var(--c--globals--transitions--ease-out)',
overflow: 'hidden',
transition: isToggleAnimating ? PANEL_TOGGLE_TRANSITION : 'none',
}}
order={0}
defaultSize={
isDesktop
? Math.max(
minPanelSizePercent,
Math.min(panelSizePercent, maxPanelSizePercent),
Math.min(
pxToPercent(savedWidthPxRef.current),
maxPanelSizePercent,
),
)
: 0
}
@@ -113,20 +129,25 @@ export const ResizableLeftPanel = ({
>
{leftPanel}
</Panel>
{isPanelOpen && (
<PanelResizeHandle
style={{
borderRightWidth: '1px',
borderRightStyle: 'solid',
borderRightColor: 'var(--c--contextuals--border--surface--primary)',
width: '1px',
cursor: 'col-resize',
}}
onDragging={setIsDragging}
disabled={!isDesktop}
/>
)}
<Panel order={1}>{children}</Panel>
<PanelResizeHandle
style={{
borderRightWidth: '1px',
borderRightStyle: 'solid',
borderRightColor: 'var(--c--contextuals--border--surface--primary)',
width: '1px',
cursor: 'col-resize',
}}
disabled={!isDesktop || !isPanelOpen}
/>
<Panel
order={1}
style={{
transition: isToggleAnimating ? PANEL_TOGGLE_TRANSITION : 'none',
}}
>
{children}
</Panel>
</PanelGroup>
);
};
@@ -1,4 +1,3 @@
export * from './LeftPanel';
export * from './LeftPanelCollapseButton';
export * from './LeftPanelToggleMobile';
export * from './ResizableLeftPanel';
@@ -1,55 +1,31 @@
import { create } from 'zustand';
type TogglePanelType = { type: 'desktop' | 'mobile' };
type TogglePanelArgs = {
value?: boolean;
} & Partial<TogglePanelType>;
interface LeftPanelState {
isPanelOpen: boolean;
isPanelOpenMobile: boolean;
togglePanel: (args?: TogglePanelArgs) => void;
closePanel: (args?: TogglePanelType) => void;
togglePanel: (value?: boolean) => void;
closePanel: () => void;
}
export const useLeftPanelStore = create<LeftPanelState>((set, get) => ({
isPanelOpen: true,
isPanelOpenMobile: false,
togglePanel: ({ value, type }: TogglePanelArgs = {}) => {
if (typeof value === 'boolean') {
if (type === 'mobile') {
set({ isPanelOpenMobile: value });
return;
}
if (type === 'desktop') {
set({ isPanelOpen: value });
return;
}
set({ isPanelOpen: value, isPanelOpenMobile: value });
togglePanel: (value?: boolean) => {
if (value === true) {
set({ isPanelOpen: true });
return;
}
if (value === false) {
set({ isPanelOpen: false, isPanelOpenMobile: false });
return;
}
const { isPanelOpen, isPanelOpenMobile } = get();
if (type === 'mobile') {
set({ isPanelOpenMobile: !isPanelOpenMobile });
return;
}
if (type === 'desktop') {
set({ isPanelOpen: !isPanelOpen });
return;
}
set({ isPanelOpen: !isPanelOpen, isPanelOpenMobile: !isPanelOpenMobile });
set({
isPanelOpen: !isPanelOpen,
isPanelOpenMobile: !isPanelOpenMobile,
});
},
closePanel: ({ type }: Partial<TogglePanelType> = {}) => {
if (type === 'mobile') {
set({ isPanelOpenMobile: false });
return;
}
if (type === 'desktop') {
set({ isPanelOpen: false });
return;
}
closePanel: () => {
set({ isPanelOpen: false, isPanelOpenMobile: false });
},
}));
@@ -741,14 +741,11 @@
"Cancel": "Annuler",
"Cancel the deletion": "Annuler la suppression",
"Cancel the download": "Annuler le téléchargement",
"Cancel the move": "Annuler le déplacement",
"Change role for {{email}}": "Changer le rôle pour {{email}}",
"Change role for {{name}}": "Changer le rôle pour {{name}}",
"Choose the new location for <strong>{{title}}</strong>.": "Choisissez le nouvel emplacement pour <strong>{{title}}</strong>.",
"Close the access request modal": "Fermer la fenêtre modale de demande d'accès",
"Close the delete modal": "Fermer la fenêtre modale de suppression",
"Close the download modal": "Fermer la fenêtre modale de téléchargement",
"Close the move modal": "Fermer",
"Close the search modal": "Fermer la fenêtre modale de recherche",
"Close the share modal": "Fermer la fenêtre modale de partage",
"Close the version history modal": "Fermer la fenêtre modale de l'historique des versions",
@@ -861,22 +858,15 @@
"List request access card": "Carte de liste des demandes d'accès",
"List search user result card": "Liste des résultats de la recherche utilisateur",
"Load more": "Afficher plus",
"Loading documents...": "Chargement des documents...",
"Log in to access the document.": "Connectez-vous pour accéder au document.",
"Login": "Connexion",
"Logout": "Se déconnecter",
"Main content": "Contenu principal",
"Modal confirmation for moving a document": "Confirmation pour déplacer un document",
"Modal confirmation to download the attachment": "Modale de confirmation pour télécharger la pièce jointe",
"More docs": "Plus de documents",
"More options": "Plus d'options",
"Move": "Déplacer",
"Move Modal": "Déplacer",
"Move document": "Déplacer le document",
"Move here": "Déplacer ici",
"Move into a doc": "Déplacer dans un doc",
"Move modal": "Déplacer",
"Move the document to the selected location": "Déplacer le document vers l'emplacement sélectionné",
"Move to my docs": "Déplacer vers mes docs",
"My docs": "Mes documents",
"Name": "Nom",
@@ -926,14 +916,12 @@
"Rename": "Renommer",
"Rephrase": "Reformuler",
"Request access": "Demander l'accès",
"Request access modal": "Demande d'accès",
"Reset": "Réinitialiser",
"Restore": "Restaurer",
"Root document {{title}}": "Document racine pour le {{title}}",
"Search": "Rechercher",
"Search by title": "Recherchez par titre",
"Search docs": "Rechercher des docs",
"Search for a doc": "Rechercher un document",
"Search modal": "Modale de partage",
"Search results": "Résultats de la recherche",
"Search user result": "Résultat de la recherche utilisateur",
@@ -1001,8 +989,6 @@
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "Vous êtes le seul propriétaire de ce groupe, faites d'un autre membre le propriétaire du groupe, avant de pouvoir modifier votre propre rôle ou vous supprimer du document.",
"You can view this document but need additional access to see its members or modify settings.": "Vous pouvez voir ce document mais vous avez besoin d'un accès supplémentaire pour voir ses membres ou modifier les paramètres.",
"You cannot restrict access to a subpage relative to its parent page.": "Vous ne pouvez pas restreindre l'accès à une sous-page par rapport à sa page parente.",
"You don't have permission to move this document to <strong>{{targetDocumentTitle}}</strong>. You need edit access to the destination. Request access, then try again.": "Vous n'êtes pas autorisé à déplacer ce document vers <strong>{{targetDocumentTitle}}</strong>. Vous devez disposer d'un accès en modification au dossier de destination. Veuillez demander l'accès, puis réessayer.",
"You have already requested access to this document.": "Vous avez déjà demandé l'accès à ce document.",
"You must be at least the administrator of the target document": "Vous devez être au moins l'administrateur du document cible",
"You must be the owner to move the document": "Vous devez être le propriétaire pour déplacer le document",
"You're currently viewing a sub-document. To gain access, please request permission from the main document.": "Vous visualisez actuellement un sous-document. Pour obtenir un accès, veuillez demander la permission du document principal.",
@@ -1170,12 +1156,7 @@
"401 Unauthorized": "401 Ongeautoriseerd",
"A new way to organize knowledge.": "Een nieuwe manier om kennis te organiseren.",
"AI Actions": "AI Actie",
"AI is thinking": "AI denkt na",
"AI is writing": "AI schrijft",
"AI request failed": "AI verzoek mislukt",
"AI response ready for review": "AI reactie klaar voor beoordeling",
"AI seems busy! Please try again.": "AI is bezig! Probeer het later opnieuw.",
"Accept anyway": "Toch accepteren",
"Access Denied - Error 403": "Toegang geweigerd - Fout 403",
"Access Requests": "Toegangsverzoeken",
"Access request sent successfully.": "Toegangsverzoek is succesvol verzonden.",
@@ -1192,7 +1173,6 @@
"All docs": "Alle documenten",
"An error occurred during email validation.": "Er is een fout opgetreden tijdens de e-mailvalidatie.",
"An error occurred while restoring the document: {{error}}": "Er is een fout opgetreden tijdens het herstellen van het document: {{error}}",
"An error occurred...": "Er is een fout opgetreden...",
"An uncompromising writing experience.": "Een compromisloze schrijfervaring.",
"An unexpected error occurred.": "Er deed zich een onverwachte fout voor.",
"Analyzing file...": "Bestand analyseren...",
@@ -1203,8 +1183,6 @@
"Anyone with the link can view the document if they are logged in": "Iedereen met deze link kan het document zien, mits ze ingelogd zijn",
"Approve": "Goedkeuren",
"As this is a sub-document, please request access to the parent document to enable these features.": "Aangezien dit een subdocument is, vraag om toegang tot het bovenliggende document om deze functies in te schakelen.",
"Ask AI": "Vraag AI",
"Ask anything...": "Stel een vraag...",
"Available soon": "Binnenkort beschikbaar",
"Back to homepage": "Terug naar startpagina",
"Banner image": "Banner afbeelding",
@@ -1215,14 +1193,11 @@
"Cancel": "Annuleren",
"Cancel the deletion": "Verwijderen annuleren",
"Cancel the download": "Annuleer de download",
"Cancel the move": "Annuleer verplaatsen",
"Change role for {{email}}": "Rol wijzigen voor {{email}}",
"Change role for {{name}}": "Rol wijzigen voor {{name}}",
"Choose the new location for <strong>{{title}}</strong>.": "Kies de nieuwe locatie voor <strong>{{title}}</strong>.",
"Close the access request modal": "Sluit het toegangsverzoek venster",
"Close the delete modal": "Sluit het verwijder venster",
"Close the download modal": "Sluit het downloadvenster",
"Close the move modal": "Sluit het deelvenster",
"Close the search modal": "Sluit het zoekvenster",
"Close the share modal": "Sluit het deelvenster",
"Close the version history modal": "Sluit versie geschiedenis venster",
@@ -1335,22 +1310,15 @@
"List request access card": "Kaart voor de lijst van toegangsverzoeken",
"List search user result card": "Gebruikersresultaten weergeven op zoekkaart",
"Load more": "Laad meer",
"Loading documents...": "Document wordt geladen...",
"Log in to access the document.": "Log in om toegang tot het document te krijgen.",
"Login": "Inloggen",
"Logout": "Uitloggen",
"Main content": "Hoofdinhoud",
"Modal confirmation for moving a document": "Modal bevestiging voor het verplaatsen van een document",
"Modal confirmation to download the attachment": "Bevestiging om bijlage te downloaden",
"More docs": "Meer documenten",
"More options": "Meer opties",
"Move": "Verplaats",
"Move Modal": "Modal verplaatsen",
"Move document": "Document verplaatsen",
"Move here": "Hierheen verplaatsen",
"Move into a doc": "Verplaats naar een doc",
"Move modal": "Modal verplaatsen",
"Move the document to the selected location": "Verplaats het document naar de geselecteerde locatie",
"Move to my docs": "Verplaatsen naar mijn documenten",
"My docs": "Mijn documenten",
"Name": "Naam",
@@ -1400,14 +1368,12 @@
"Rename": "Hernoem",
"Rephrase": "Herschrijf",
"Request access": "Toegang aanvragen",
"Request access modal": "Verzoek toegang modal",
"Reset": "Herstellen",
"Restore": "Herstel",
"Root document {{title}}": "Hoofddocument {{title}}",
"Search": "Zoeken",
"Search by title": "Zoek op titel",
"Search docs": "Zoek documenten",
"Search for a doc": "Een doc zoeken",
"Search modal": "Zoekvenster",
"Search results": "Zoekresultaten",
"Search user result": "Zoekresultaten",
@@ -1432,7 +1398,6 @@
"Simple document icon": "Eenvoudig documentpictogram",
"Something bad happens, please retry.": "Een fout heeft plaatsgevonden, probeer het opnieuw.",
"Start Writing": "Begin met schrijven",
"Stop": "Stop",
"Summarize": "Vat samen",
"Summary": "Samenvatting",
"The antivirus has detected an anomaly in your file.": "Antivirus heeft een afwijking in uw bestand ontdekt.",
@@ -1446,7 +1411,6 @@
"The document visibility restored.": "De zichtbaarheid van het document is hersteld.",
"The export failed": "Het exporteren is mislukt",
"The link sharing rules differ from the parent document": "De regels voor het delen via een link verschillen van het bovenliggende document",
"Thinking...": "Denken...",
"This document and <strong>any sub-documents</strong> will be placed in the trashbin. You can restore it within {{days}} days.": "Dit document en <strong>alle sub-documenten</strong> zullen in de prullenbak worden geplaatst. Je kunt het binnen {{days}} dagen herstellen.",
"This document will be placed in the trashbin. You can restore it within {{days}} days.": "Dit document zal worden geplaatst in de prullenbak. U kunt het herstellen binnen {{days}} dagen.",
"This file is flagged as unsafe.": "Het bestand is gemarkeerd als onveilig.",
@@ -1471,12 +1435,9 @@
"Warning": "Waarschuwing",
"Why you can't edit the document?": "Waarom kunt u het document niet bewerken?",
"Write": "Schrijf",
"Writing...": "Schrijven...",
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "U bent de enige eigenaar van deze groep, maak een ander lid de groepseigenaar voordat u uw eigen rol kunt wijzigen of kan worden verwijderd van het document.",
"You can view this document but need additional access to see its members or modify settings.": "U kunt dit document bekijken, maar u heeft extra toegang nodig om de leden te kunnen bekijken of de instellingen aan te passen.",
"You cannot restrict access to a subpage relative to its parent page.": "U kunt de toegang niet beperken tot een subpagina met betrekking tot de bovenliggende pagina.",
"You don't have permission to move this document to <strong>{{targetDocumentTitle}}</strong>. You need edit access to the destination. Request access, then try again.": "U bent niet gemachtigd om dit document te verplaatsen naar <strong>{{targetDocumentTitle}}</strong>. U moet de toegang tot de bestemming bewerken. Vraag toegang aan en probeer het opnieuw.",
"You have already requested access to this document.": "U hebt al toegang gevraagd tot dit document.",
"You must be at least the administrator of the target document": "U moet tenminste beheerder zijn van het doeldocument",
"You must be the owner to move the document": "U moet de eigenaar zijn om het document te verplaatsen",
"You're currently viewing a sub-document. To gain access, please request permission from the main document.": "U bekijkt momenteel een subdocument. Om toegang te krijgen, vraagt u toestemming voor het hoofddocument.",
@@ -1552,14 +1513,11 @@
"Cancel": "Отмена",
"Cancel the deletion": "Отменить удаление",
"Cancel the download": "Отменить загрузку",
"Cancel the move": "Отмена перемещения",
"Change role for {{email}}": "Изменить роль для {{email}}",
"Change role for {{name}}": "Изменить роль для {{name}}",
"Choose the new location for <strong>{{title}}</strong>.": "Выберите новое расположение для <strong>{{title}}</strong>.",
"Close the access request modal": "Закрыть окно запроса доступа",
"Close the delete modal": "Закрыть окно удаления",
"Close the download modal": "Закрыть окно загрузки",
"Close the move modal": "Закрыть",
"Close the search modal": "Закрыть окно поиска",
"Close the share modal": "Закрыть окно общего доступа",
"Close the version history modal": "Закрыть историю версии",
@@ -1672,22 +1630,15 @@
"List request access card": "Карта списка запроса доступа",
"List search user result card": "Карта списка результатов поиска пользователей",
"Load more": "Загрузить ещё",
"Loading documents...": "Загрузка документов...",
"Log in to access the document.": "Войдите, чтобы получить доступ к документу.",
"Login": "Войти",
"Logout": "Выйти",
"Main content": "Основное содержимое",
"Modal confirmation for moving a document": "Подтверждение перемещения документа",
"Modal confirmation to download the attachment": "Подтверждение загрузки вложения",
"More docs": "Больше документов",
"More options": "Больше параметров",
"Move": "Переместить",
"Move Modal": "Перемещение",
"Move document": "Переместить документ",
"Move here": "Переместить сюда",
"Move into a doc": "Перенести в документ",
"Move modal": "Перемещение",
"Move the document to the selected location": "Переместить документ в выбранное место",
"Move to my docs": "Переместить в мои документы",
"My docs": "Мои документы",
"Name": "Название",
@@ -1737,14 +1688,12 @@
"Rename": "Переименовать",
"Rephrase": "Переформулировать",
"Request access": "Запрос доступа",
"Request access modal": "Запрос доступа",
"Reset": "Сброс",
"Restore": "Восстановить",
"Root document {{title}}": "Корневой документ {{title}}",
"Search": "Поиск",
"Search by title": "Поиск по названию",
"Search docs": "Поиск документов",
"Search for a doc": "Поиск документов",
"Search modal": "Поиск",
"Search results": "Результаты поиска",
"Search user result": "Результат поиска пользователя",
@@ -1812,8 +1761,6 @@
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "Вы единственный владелец этой группы. Прежде чем вы измените свою собственную роль или потеряете доступ к документу, назначьте другого владельца группы.",
"You can view this document but need additional access to see its members or modify settings.": "Вы можете просмотреть этот документ, но для получения списка участников или изменения настроек требуются дополнительные разрешения.",
"You cannot restrict access to a subpage relative to its parent page.": "Нельзя ограничить доступ ко вложенной странице относительно родительской страницы.",
"You don't have permission to move this document to <strong>{{targetDocumentTitle}}</strong>. You need edit access to the destination. Request access, then try again.": "У вас нет разрешения на перемещение этого документа в <strong>{{targetDocumentTitle}}</strong>. Вам необходимы права на редактирование в месте назначения. Запросите доступ, а затем попробуйте снова.",
"You have already requested access to this document.": "Вы уже запросили доступ к этому документу.",
"You must be at least the administrator of the target document": "Вы должны быть как минимум администратором целевого документа",
"You must be the owner to move the document": "Для перемещения документа вы должны быть его владельцем",
"You're currently viewing a sub-document. To gain access, please request permission from the main document.": "Сейчас вы просматриваете вложенный документ. Чтобы получить доступ, запросите разрешение для основного документа.",
@@ -1990,14 +1937,11 @@
"Cancel": "Скасувати",
"Cancel the deletion": "Скасувати видалення",
"Cancel the download": "Скасувати завантаження",
"Cancel the move": "Скасувати переміщення",
"Change role for {{email}}": "Змінити роль для {{email}}",
"Change role for {{name}}": "Змінити роль для {{name}}",
"Choose the new location for <strong>{{title}}</strong>.": "Виберіть нове розташування для <strong>{{title}}</strong>.",
"Close the access request modal": "Закрити вікно запиту доступу",
"Close the delete modal": "Закрити вікно видалення",
"Close the download modal": "Закрити вікно завантаження",
"Close the move modal": "Закрити",
"Close the search modal": "Закрити вікно пошуку",
"Close the share modal": "Закрити вікно загального доступу",
"Close the version history modal": "Закрити історію версій",
@@ -2110,22 +2054,15 @@
"List request access card": "Картка списку запитів доступу",
"List search user result card": "Картка списку результатів пошуку користувача",
"Load more": "Завантажити ще",
"Loading documents...": "Завантаження документів...",
"Log in to access the document.": "Увійдіть, щоб отримати доступ до документа.",
"Login": "Увійти",
"Logout": "Вийти",
"Main content": "Основний вміст",
"Modal confirmation for moving a document": "Підтвердження переміщення документа",
"Modal confirmation to download the attachment": "Підтвердження завантаження вкладень",
"More docs": "Більше документів",
"More options": "Більше параметрів",
"Move": "Переміщення",
"Move Modal": "Переміщення",
"Move document": "Переміщення документа",
"Move here": "Перемістити сюди",
"Move into a doc": "Перемістити документ",
"Move modal": "Переміщення",
"Move the document to the selected location": "Перемістити документ до вибраного розташування",
"Move to my docs": "Перемістити до моїх документів",
"My docs": "Мої документи",
"Name": "Назва",
@@ -2175,14 +2112,12 @@
"Rename": "Перейменувати",
"Rephrase": "Перефразувати",
"Request access": "Запит доступу",
"Request access modal": "Запит доступу",
"Reset": "Скинути",
"Restore": "Відновити",
"Root document {{title}}": "Корінь документа {{title}}",
"Search": "Пошук",
"Search by title": "Пошук за назвою",
"Search docs": "Пошук документів",
"Search for a doc": "Пошук документу",
"Search modal": "Підтвердження пошуку",
"Search results": "Результати пошуку",
"Search user result": "Результат пошуку користувача",
@@ -2250,8 +2185,6 @@
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "Ви єдиний власник цієї групи, призначте іншого учасника її власником. Інакше ви не зможете змінити власну роль або вийти з команди редакторів документу.",
"You can view this document but need additional access to see its members or modify settings.": "Ви можете переглянути цей документ, але для перегляду його учасників або зміни налаштувань потрібні додаткові дозволи.",
"You cannot restrict access to a subpage relative to its parent page.": "Не можна обмежити доступ до вкладеної сторінки відносно її батьківської сторінки.",
"You don't have permission to move this document to <strong>{{targetDocumentTitle}}</strong>. You need edit access to the destination. Request access, then try again.": "У вас немає дозволу на переміщення цього документа до <strong>{{targetDocumentTitle}}</strong>. Вам потрібен доступ для редагування у цьому місці призначення. Запросіть доступ і повторіть спробу.",
"You have already requested access to this document.": "Ви вже просили доступ до цього документа.",
"You must be at least the administrator of the target document": "Ви повинні бути принаймні адміністратором цільового документа",
"You must be the owner to move the document": "Для переміщення документа ви повинні бути його власником",
"You're currently viewing a sub-document. To gain access, please request permission from the main document.": "Зараз ви переглядаєте вкладений документ. Щоб отримати доступ, будь ласка, запитайте дозвіл від головного документа.",
@@ -56,18 +56,16 @@ export class Analytics {
/**
* Check if a feature flag is activated
*
* A feature flag is considered active only if ALL analytics agree it is.
* This ensures that if one analytic explicitly disables a flag,
* it takes precedence over analytics that do not manage flags.
* If no analytics are registered, default to true so features are not hidden
* when analytics are not configured.
* Feature flags are activated if at least one analytic is activated
* because we don't want to hide feature if the user does not
* use analytics (AB testing, etc)
*/
public static isFeatureFlagActivated(flagName: string): boolean {
if (!Analytics.analytics.length) {
return true;
}
return Analytics.analytics.every((analytic) =>
return Analytics.analytics.some((analytic) =>
analytic.isFeatureFlagActivated(flagName),
);
}
@@ -1,95 +0,0 @@
import { render, screen } from '@testing-library/react';
import React, { Fragment } from 'react';
import { AbstractAnalytic, Analytics, useAnalytics } from '@/libs';
import { AppWrapper } from '@/tests/utils';
class TestAnalytic1 extends AbstractAnalytic {
public constructor() {
super();
}
public Provider() {
return <Fragment />;
}
public trackEvent() {}
public isFeatureFlagActivated(flagName: string): boolean {
if (flagName === 'test-flag') {
return false;
}
if (flagName === 'test-flag2') {
return true;
}
return true;
}
}
class TestAnalytic2 extends AbstractAnalytic {
public constructor() {
super();
}
public Provider() {
return <Fragment />;
}
public trackEvent() {}
public isFeatureFlagActivated(): boolean {
return true;
}
}
const TestComponent = ({ flag }: { flag: string }) => {
const { isFeatureFlagActivated } = useAnalytics();
return (
<div>
{isFeatureFlagActivated(flag) ? (
<span>Feature is enabled</span>
) : (
<span>Feature is not enabled</span>
)}
</div>
);
};
describe('Analytics feature flag', () => {
beforeEach(() => {
Analytics.clearAnalytics();
});
test('renders feature when feature flag is not existing', async () => {
new TestAnalytic1();
new TestAnalytic2();
render(<TestComponent flag="unexisting-flag" />, {
wrapper: AppWrapper,
});
expect(await screen.findByText('Feature is enabled')).toBeInTheDocument();
});
test('renders feature when feature flag is not enabled', async () => {
new TestAnalytic1();
new TestAnalytic2();
render(<TestComponent flag="test-flag" />, {
wrapper: AppWrapper,
});
expect(screen.getByText('Feature is not enabled')).toBeInTheDocument();
});
test('renders feature when feature flag is enabled', async () => {
new TestAnalytic1();
new TestAnalytic2();
render(<TestComponent flag="test-flag2" />, {
wrapper: AppWrapper,
});
expect(screen.getByText('Feature is enabled')).toBeInTheDocument();
});
});
@@ -1,3 +1,2 @@
export * from './useBroadcastStore';
export * from './useFocusStore';
export * from './useResponsiveStore';
@@ -1,23 +0,0 @@
import { create } from 'zustand';
interface UseFocusStore {
lastFocusedElement: HTMLElement | null;
addLastFocus: (target: HTMLElement | null) => void;
restoreFocus: () => void;
}
export const useFocusStore = create<UseFocusStore>((set, get) => ({
lastFocusedElement: null,
addLastFocus: (target) => set({ lastFocusedElement: target }),
restoreFocus: () => {
const { lastFocusedElement } = get();
if (!lastFocusedElement) {
return;
}
requestAnimationFrame(() => {
lastFocusedElement.focus();
});
set({ lastFocusedElement: null });
},
}));
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "4.7.0",
"version": "4.6.0",
"private": true,
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-docs",
"version": "4.7.0",
"version": "4.6.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "4.7.0",
"version": "4.6.0",
"repository": "https://github.com/suitenumerique/docs",
"author": "DINUM",
"license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "4.7.0",
"version": "4.6.0",
"description": "Y.js provider for docs",
"repository": "https://github.com/suitenumerique/docs",
"license": "MIT",
@@ -16,7 +16,7 @@
"node": ">=22"
},
"dependencies": {
"@blocknote/server-util": "0.47.1",
"@blocknote/server-util": "0.47.0",
"@hocuspocus/server": "3.4.4",
"@sentry/node": "10.38.0",
"@sentry/profiling-node": "10.38.0",
@@ -30,7 +30,7 @@
"yjs": "*"
},
"devDependencies": {
"@blocknote/core": "0.47.1",
"@blocknote/core": "0.47.0",
"@hocuspocus/provider": "3.4.4",
"@types/cors": "2.8.19",
"@types/express": "5.0.6",
+70 -97
View File
@@ -1262,12 +1262,12 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@blocknote/code-block@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/code-block/-/code-block-0.47.1.tgz#6497f6d40b6756c7202e00d8d99974924c8b4967"
integrity sha512-gZmLkJupX0uKukTvEiNjcW9FRJW5iNoAaEYF1SGMbBw2cTi+eC+Nyqjgy+8MF7k5A87/TgoA8RQPqNqrRWyHlg==
"@blocknote/code-block@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/code-block/-/code-block-0.47.0.tgz#68954506e8ca75cb18746fd1eee34e29145718be"
integrity sha512-4FD3NoKWgfXPERKdOQYMYRvbzaFKZYYus+K248taUD47kuQSi8EiHsXHdJqMKJpKY+fGDlO8fC7wqdOYcIfgBQ==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/core" "0.47.0"
"@shikijs/core" "^3"
"@shikijs/engine-javascript" "^3"
"@shikijs/langs" "^3"
@@ -1275,10 +1275,10 @@
"@shikijs/themes" "^3"
"@shikijs/types" "^3"
"@blocknote/core@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/core/-/core-0.47.1.tgz#27db936db487f97a981827d6f72bbf3cccfa00b7"
integrity sha512-jRDi7yJPSHQ2Ks1inGQ41dATvaaWF1Y+XP7WlO0hcl45MNsGbUYCdyB1/CZS8nzYfT/Gkof6aR472gstj6ztVw==
"@blocknote/core@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/core/-/core-0.47.0.tgz#9f25c8ddddafe5677b97fe1e6a238f3303d8bd2d"
integrity sha512-i8QwAA8z/T97ZKPQy/8GTUicy3xBZy7xcR0fH/qA2xQq0xIaE7AZT92nS1sDvohxUsTJVNQBZ/zkpGKGj4Eq8g==
dependencies:
"@emoji-mart/data" "^1.2.1"
"@handlewithcare/prosemirror-inputrules" "^0.1.4"
@@ -1321,24 +1321,24 @@
y-protocols "^1.0.6"
yjs "^13.6.27"
"@blocknote/mantine@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/mantine/-/mantine-0.47.1.tgz#2cd638812fab8d6d9a0c7cb125fad03a438afc85"
integrity sha512-lcmHo/o35lVrCoY6qhTLn3Rs+tiZTktG2fYffe8LK4/qqs1hId4Xp2pbARLOGnDEREKvCL1PUZ9JKifgzA0DhQ==
"@blocknote/mantine@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/mantine/-/mantine-0.47.0.tgz#02855819255a45aca1b8362c817ab385a43484c7"
integrity sha512-qRntaUUNzueAa3bm0xRqpDq5HH4+JKI6TGZtsYQB/ENIiiIDGL2ow/GSqAViTo1PmQeCtDyQOPoag2p3bSR7kQ==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/react" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/react" "0.47.0"
react-icons "^5.5.0"
"@blocknote/react@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/react/-/react-0.47.1.tgz#5c55389f5d6b4a9ded658ebf1a4728b7364a623c"
integrity sha512-g+bfcjPuCSFmx+AMHJCBGFXPzaIzzXmidPXJGEMKZjvdjPNDr78+SnuYd3YU05+BleHQ+S2sBl6S8sBaYxFFNw==
"@blocknote/react@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/react/-/react-0.47.0.tgz#c57b60b5fb7afc0cff73f37f53ebab22ffb995bc"
integrity sha512-RgLCrccVCVySlnxSKoJfu1nHcJeahpgZQeQrLnVBqLMXicy4nB5vy5QYh5qQGFl7vaRGN3RVduDAZfOp96TF1g==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/core" "0.47.0"
"@emoji-mart/data" "^1.2.1"
"@floating-ui/react" "^0.27.18"
"@floating-ui/utils" "^0.2.10"
"@floating-ui/react" "^0.27.16"
"@floating-ui/utils" "0.2.10"
"@tanstack/react-store" "0.7.7"
"@tiptap/core" "^3.13.0"
"@tiptap/pm" "^3.13.0"
@@ -1350,13 +1350,13 @@
react-icons "^5.5.0"
use-sync-external-store "1.6.0"
"@blocknote/server-util@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/server-util/-/server-util-0.47.1.tgz#385d6e72ce5ea2078f78945bfcc327cd5cc01f42"
integrity sha512-WRNek+6mcqB7T+VJsElFTB1Fb4uVmxxRgJMwFF9LcxZDKP+GyyjASG+N0qaAi8qaSUh47FgZaWE2lkMYivUisg==
"@blocknote/server-util@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/server-util/-/server-util-0.47.0.tgz#6fd555c68eb3ba19969ac2836459470700e2a7dd"
integrity sha512-vPE9U6FZ3FIfREs0bpmv3CLZyHnC5N43aSjaomu5OiqvBoDIRnXgoMvKHksBT8T0ZMuBl7JKW5ibPfoGYbnNZg==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/react" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/react" "0.47.0"
"@tiptap/core" "^3.13.0"
"@tiptap/pm" "^3.13.0"
jsdom "^25.0.1"
@@ -1364,17 +1364,17 @@
y-protocols "^1.0.6"
yjs "^13.6.27"
"@blocknote/xl-ai@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/xl-ai/-/xl-ai-0.47.1.tgz#6e0057b502f4890600aa5252fa5622d139f905b3"
integrity sha512-eW5oB7QI9t7D5Ijl7+C+fHYjJIfR1cS1j12IjvBV4mkZvbDEiLT67cDOdCmqRDWsybN4rFSjs6o5J3GrP2AXxQ==
"@blocknote/xl-ai@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/xl-ai/-/xl-ai-0.47.0.tgz#fff198e65b05758ef1776bb24be4acc93917143a"
integrity sha512-czx1DyO5bMy2nBCQxesbMQyEZhFpqA6pRUyhnhUENHKievlUZgymRAH3DyO3Yl3STdZkwAiWQDkhVIQiecRNwA==
dependencies:
"@ai-sdk/provider-utils" "^4.0.2"
"@ai-sdk/react" "^3.0.5"
"@blocknote/core" "0.47.1"
"@blocknote/mantine" "0.47.1"
"@blocknote/react" "0.47.1"
"@floating-ui/react" "^0.27.18"
"@blocknote/core" "0.47.0"
"@blocknote/mantine" "0.47.0"
"@blocknote/react" "0.47.0"
"@floating-ui/react" "^0.26.28"
"@handlewithcare/prosemirror-suggest-changes" "^0.1.8"
"@tiptap/core" "^3.13.0"
ai "^6.0.5"
@@ -1394,24 +1394,24 @@
unified "^11.0.5"
y-prosemirror "^1.3.7"
"@blocknote/xl-docx-exporter@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/xl-docx-exporter/-/xl-docx-exporter-0.47.1.tgz#7b1b39d9793b1078849f77d508f4c9969b416cff"
integrity sha512-MD5kTlH/EQXfLkpQD7h3D2ht4b+FSPI20w8ogd9p768BlDO84rWaw4ne1RUTWKZXcZqVR9KLXe2XgkoPTt7dTg==
"@blocknote/xl-docx-exporter@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/xl-docx-exporter/-/xl-docx-exporter-0.47.0.tgz#b78ee9082538410efc5867cf276ec325a632ef23"
integrity sha512-qYldOglAi37ggiNaOA1avTG59AewvB0fGfHpVsQlrIYl4iVkm7CaqIco2OQg0OSMDd//M3i8SsOzgX5eRriXsg==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/xl-multi-column" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/xl-multi-column" "0.47.0"
buffer "^6.0.3"
docx "^9.5.1"
image-meta "^0.2.2"
"@blocknote/xl-multi-column@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/xl-multi-column/-/xl-multi-column-0.47.1.tgz#6d9de986c1ffa0db31ae9e19876a27e09afd1774"
integrity sha512-Ksxrt+002/c+DJqCLJuk/xeTEJ8iZSTxzBeDsO5kuUp2h5B2uMvMJ+Fj43SCtcaJYEML7fdXrQmxh6BTYs4dEg==
"@blocknote/xl-multi-column@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/xl-multi-column/-/xl-multi-column-0.47.0.tgz#cd71dbacd6d7b2ff9d10440f5c7730779c280c03"
integrity sha512-UkVx3y5M/y9S2p3jijylh+aXJIrh6babBkQvGMd95El/qwjZs5pJKUg/wApOtyrhaP0genWdWLNka6ag0cD9aA==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/react" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/react" "0.47.0"
"@tiptap/core" "^3.13.0"
prosemirror-model "^1.25.4"
prosemirror-state "^1.4.4"
@@ -1420,25 +1420,25 @@
prosemirror-view "^1.41.4"
react-icons "^5.5.0"
"@blocknote/xl-odt-exporter@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/xl-odt-exporter/-/xl-odt-exporter-0.47.1.tgz#13cb5463b4dd7c608e5c9a41177e7b6995b81580"
integrity sha512-wyz+UGL7QpapmPKIU8WAPRKu9pUkZJrOaEm6vaBRFt2WaAzs2x7lWVU4TFy0gLfcVQEhgz/cqzofDxXZFKCZfw==
"@blocknote/xl-odt-exporter@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/xl-odt-exporter/-/xl-odt-exporter-0.47.0.tgz#d8a510d2b7ca7eebc14d8638d69ca456761748fe"
integrity sha512-rfZkG3ajgc1v2Wtb0iqnqlEQJ7oQwauaq7WR+f9BJRwocYS4jzX0sa7iMDuuKfvehbgG09HXM7FkcNZKbj+yoQ==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/xl-multi-column" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/xl-multi-column" "0.47.0"
"@zip.js/zip.js" "^2.8.8"
buffer "^6.0.3"
image-meta "^0.2.2"
"@blocknote/xl-pdf-exporter@0.47.1":
version "0.47.1"
resolved "https://registry.yarnpkg.com/@blocknote/xl-pdf-exporter/-/xl-pdf-exporter-0.47.1.tgz#1bd556a1b3844c5341bbd1da755bf50bbb631013"
integrity sha512-pltqLWhHsoOXqwsh75Ivk2q3/OJZwERfvbROmmJtYgKyAGcb5I9IvAkScoF1V5BpIhVl178ACA1ghEYYbyXdIA==
"@blocknote/xl-pdf-exporter@0.47.0":
version "0.47.0"
resolved "https://registry.yarnpkg.com/@blocknote/xl-pdf-exporter/-/xl-pdf-exporter-0.47.0.tgz#793c4b678f3ef276633bf56d9e385f12adb18fbc"
integrity sha512-HXtZSx5Xe6ZEhd78G+fYyvTOOl3XdLuFkzf+0StgbOx8PcU8Ts579Ysh3rVnftVI4EJhjytYyUeHuFS/iJFdmw==
dependencies:
"@blocknote/core" "0.47.1"
"@blocknote/react" "0.47.1"
"@blocknote/xl-multi-column" "0.47.1"
"@blocknote/core" "0.47.0"
"@blocknote/react" "0.47.0"
"@blocknote/xl-multi-column" "0.47.0"
"@react-pdf/renderer" "^4.3.0"
buffer "^6.0.3"
docx "^9.5.1"
@@ -1929,13 +1929,6 @@
dependencies:
"@floating-ui/utils" "^0.2.10"
"@floating-ui/core@^1.7.5":
version "1.7.5"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.5.tgz#d4af157a03330af5a60e69da7a4692507ada0622"
integrity sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==
dependencies:
"@floating-ui/utils" "^0.2.11"
"@floating-ui/dom@^1.0.0", "@floating-ui/dom@^1.0.1":
version "1.7.4"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.4.tgz#ee667549998745c9c3e3e84683b909c31d6c9a77"
@@ -1952,27 +1945,21 @@
"@floating-ui/core" "^1.7.4"
"@floating-ui/utils" "^0.2.10"
"@floating-ui/dom@^1.7.6":
version "1.7.6"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.6.tgz#f915bba5abbb177e1f227cacee1b4d0634b187bf"
integrity sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==
dependencies:
"@floating-ui/core" "^1.7.5"
"@floating-ui/utils" "^0.2.11"
"@floating-ui/react-dom@^2.1.7":
"@floating-ui/react-dom@^2.1.2", "@floating-ui/react-dom@^2.1.7":
version "2.1.7"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.7.tgz#529475cc16ee4976ba3387968117e773d9aa703e"
integrity sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==
dependencies:
"@floating-ui/dom" "^1.7.5"
"@floating-ui/react-dom@^2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.8.tgz#5fb5a20d10aafb9505f38c24f38d00c8e1598893"
integrity sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==
"@floating-ui/react@^0.26.28":
version "0.26.28"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.28.tgz#93f44ebaeb02409312e9df9507e83aab4a8c0dc7"
integrity sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==
dependencies:
"@floating-ui/dom" "^1.7.6"
"@floating-ui/react-dom" "^2.1.2"
"@floating-ui/utils" "^0.2.8"
tabbable "^6.0.0"
"@floating-ui/react@^0.27.16":
version "0.27.18"
@@ -1983,25 +1970,11 @@
"@floating-ui/utils" "^0.2.10"
tabbable "^6.0.0"
"@floating-ui/react@^0.27.18":
version "0.27.19"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.19.tgz#d8d5d895b7cb97dac370bfbf55f3e630878fdf1f"
integrity sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==
dependencies:
"@floating-ui/react-dom" "^2.1.8"
"@floating-ui/utils" "^0.2.11"
tabbable "^6.0.0"
"@floating-ui/utils@^0.2.10":
"@floating-ui/utils@0.2.10", "@floating-ui/utils@^0.2.10", "@floating-ui/utils@^0.2.8":
version "0.2.10"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c"
integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==
"@floating-ui/utils@^0.2.11":
version "0.2.11"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.11.tgz#a269e055e40e2f45873bae9d1a2fdccbd314ea3f"
integrity sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==
"@fontsource-variable/inter@5.2.8":
version "5.2.8"
resolved "https://registry.yarnpkg.com/@fontsource-variable/inter/-/inter-5.2.8.tgz#29b11476f5149f6a443b4df6516e26002d87941a"
+2 -2
View File
@@ -1,10 +1,10 @@
environments:
dev:
values:
- version: 4.7.0
- version: 4.6.0
feature:
values:
- version: 4.7.0
- version: 4.6.0
feature: ci
domain: example.com
imageTag: demo
+1 -1
View File
@@ -1,5 +1,5 @@
apiVersion: v2
type: application
name: docs
version: 4.7.0
version: 4.6.0
appVersion: latest
@@ -38,14 +38,10 @@ items:
imagePullPolicy: {{ ($.Values.backend.image | default dict).pullPolicy | default $.Values.image.pullPolicy }}
args:
{{- toYaml .command | nindent 22 }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 22 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 22 }}
{{- end }}
{{- end }}
{{- with $.Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 22 }}
@@ -109,4 +105,4 @@ items:
{{- end }}
{{- end }}
{{- end }}
{{- end }}
@@ -49,14 +49,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -139,9 +135,6 @@ spec:
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
+2 -6
View File
@@ -44,14 +44,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -48,14 +48,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -48,14 +48,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -49,14 +49,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.backend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -139,9 +135,6 @@ spec:
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
@@ -39,10 +39,6 @@ spec:
env:
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
{{- toYaml .Values.backend.envFrom | nindent 12 }}
{{- end }}
{{- with .Values.docSpec.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -49,14 +49,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.frontend.envFrom }}
envFrom:
{{- toYaml .Values.frontend.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.frontend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -129,9 +125,6 @@ spec:
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
@@ -49,14 +49,10 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.yProvider.envFrom }}
envFrom:
{{- toYaml .Values.yProvider.envFrom | nindent 12 }}
{{- end }}
{{- end }}
{{- with .Values.yProvider.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
@@ -129,9 +125,6 @@ spec:
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}

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