mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-07 15:43:01 +02:00
Compare commits
16 Commits
v4.2.0
...
sbl-public
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
270b374a17 | ||
|
|
ea3a4a6da3 | ||
|
|
b78ad27a71 | ||
|
|
e4b8ffb304 | ||
|
|
78c7ab247b | ||
|
|
b0bd6e2c01 | ||
|
|
37527416f2 | ||
|
|
30bc959340 | ||
|
|
a73d9c1c78 | ||
|
|
a920daf05b | ||
|
|
ff88465398 | ||
|
|
3617e4f7b8 | ||
|
|
efaec45bfd | ||
|
|
715d88ba3c | ||
|
|
7d64d79eeb | ||
|
|
2e66b87dab |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -6,6 +6,24 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(helm) redirecting system #1697
|
||||
- 📱(frontend) add comments for smaller device #1737
|
||||
- ✨(project) add custom js support via config #1759
|
||||
|
||||
### Changed
|
||||
|
||||
- 🥅(frontend) intercept 401 error on GET threads #1754
|
||||
- 🦺(frontend) check content type pdf on PdfBlock #1756
|
||||
- ✈️(frontend) pause Posthog when offline #1755
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🐛(frontend) fix tables deletion #1752
|
||||
- 🐛(frontend) fix children not display when first resize #1753
|
||||
- 📝(doc) fix publiccode.yml syntax #1770
|
||||
|
||||
## [4.2.0] - 2025-12-17
|
||||
|
||||
### Added
|
||||
|
||||
@@ -60,6 +60,7 @@ These are the environment variables you can set for the `impress-backend` contai
|
||||
| DJANGO_SERVER_TO_SERVER_API_TOKENS | | [] |
|
||||
| DOCUMENT_IMAGE_MAX_SIZE | Maximum size of document in bytes | 10485760 |
|
||||
| FRONTEND_CSS_URL | To add a external css file to the app | |
|
||||
| FRONTEND_JS_URL | To add a external js file to the app | |
|
||||
| FRONTEND_HOMEPAGE_FEATURE_ENABLED | Frontend feature flag to display the homepage | false |
|
||||
| FRONTEND_THEME | Frontend theme to use | |
|
||||
| LANGUAGE_CODE | Default language | en-us |
|
||||
|
||||
@@ -8,7 +8,7 @@ To use this feature, simply set the `FRONTEND_CSS_URL` environment variable to t
|
||||
FRONTEND_CSS_URL=http://anything/custom-style.css
|
||||
```
|
||||
|
||||
Once you've set this variable, our application will load your custom CSS file and apply the styles to our frontend application.
|
||||
Once you've set this variable, Docs will load your custom CSS file and apply the styles to our frontend application.
|
||||
|
||||
### Benefits
|
||||
|
||||
@@ -32,6 +32,61 @@ Then, set the `FRONTEND_CSS_URL` environment variable to the URL of your custom
|
||||
|
||||
----
|
||||
|
||||
# Runtime JavaScript Injection 🚀
|
||||
|
||||
### How to Use
|
||||
|
||||
To use this feature, simply set the `FRONTEND_JS_URL` environment variable to the URL of your custom JavaScript file. For example:
|
||||
|
||||
```javascript
|
||||
FRONTEND_JS_URL=http://anything/custom-script.js
|
||||
```
|
||||
|
||||
Once you've set this variable, Docs will load your custom JavaScript file and execute it in the browser, allowing you to modify the application's behavior at runtime.
|
||||
|
||||
### Benefits
|
||||
|
||||
This feature provides several benefits, including:
|
||||
|
||||
* **Dynamic customization** 🔄: With this feature, you can dynamically modify the behavior and appearance of our application without requiring any code changes.
|
||||
* **Flexibility** 🌈: You can add custom functionality, modify existing features, or integrate third-party services.
|
||||
* **Runtime injection** ⏱️: This feature allows you to inject JavaScript into the application at runtime, without requiring a restart or recompilation.
|
||||
|
||||
### Example Use Case
|
||||
|
||||
Let's say you want to add a custom menu to the application header. You can create a custom JavaScript file with the following contents:
|
||||
|
||||
```javascript
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function initCustomMenu() {
|
||||
// Wait for the page to be fully loaded
|
||||
const header = document.querySelector('header');
|
||||
if (!header) return false;
|
||||
|
||||
// Create and inject your custom menu
|
||||
const customMenu = document.createElement('div');
|
||||
customMenu.innerHTML = '<button>Custom Menu</button>';
|
||||
header.appendChild(customMenu);
|
||||
|
||||
console.log('Custom menu added successfully');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initCustomMenu);
|
||||
} else {
|
||||
initCustomMenu();
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
Then, set the `FRONTEND_JS_URL` environment variable to the URL of your custom JavaScript file. Once you've done this, our application will load your custom JavaScript file and execute it, adding your custom menu to the header.
|
||||
|
||||
----
|
||||
|
||||
# **Your Docs icon** 📝
|
||||
|
||||
You can add your own Docs icon in the header from the theme customization file.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
publiccodeYmlVersion: "2.4.0"
|
||||
publiccodeYmlVersion: "0.5.0"
|
||||
name: Docs
|
||||
url: https://github.com/suitenumerique/docs
|
||||
landingURL: https://github.com/suitenumerique/docs
|
||||
creationDate: 2023-12-10
|
||||
logo: https://raw.githubusercontent.com/suitenumerique/docs/main/docs/assets/docs-logo.png
|
||||
usedBy:
|
||||
- Direction interministériel du numérique (DINUM)
|
||||
- Direction interministérielle du numérique (DINUM)
|
||||
fundedBy:
|
||||
- name: Direction interministériel du numérique (DINUM)
|
||||
url: https://www.numerique.gouv.fr
|
||||
- name: Direction interministérielle du numérique (DINUM)
|
||||
uri: https://www.numerique.gouv.fr
|
||||
roadmap: "https://github.com/orgs/suitenumerique/projects/2/views/1"
|
||||
softwareType: "standalone/other"
|
||||
platforms:
|
||||
- "web"
|
||||
developmentStatus: "stable"
|
||||
description:
|
||||
en:
|
||||
shortDescription: "The open source document editor where your notes can become knowledge through live collaboration"
|
||||
@@ -18,10 +20,18 @@ description:
|
||||
shortDescription: "L'éditeur de documents open source où vos notes peuvent devenir des connaissances grâce à la collaboration en direct."
|
||||
legal:
|
||||
license: MIT
|
||||
localisation:
|
||||
localisationReady: true
|
||||
availableLanguages:
|
||||
- de
|
||||
- en
|
||||
- es
|
||||
- fr
|
||||
- nl
|
||||
maintenance:
|
||||
type: internal
|
||||
contacts:
|
||||
- name: "Virgile Deville"
|
||||
email: "virgile.deville@numerique.gouv.fr"
|
||||
- name: "samuel.paccoud"
|
||||
- name: "Samuel Paccoud"
|
||||
email: "samuel.paccoud@numerique.gouv.fr"
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
"groupName": "ignored js dependencies",
|
||||
"matchManagers": ["npm"],
|
||||
"matchPackageNames": [
|
||||
"@next/eslint-plugin-next",
|
||||
"docx",
|
||||
"eslint-config-next",
|
||||
"fetch-mock",
|
||||
"next",
|
||||
"node",
|
||||
"node-fetch",
|
||||
"workbox-webpack-plugin"
|
||||
|
||||
@@ -2197,6 +2197,7 @@ class ConfigView(drf.views.APIView):
|
||||
"ENVIRONMENT",
|
||||
"FRONTEND_CSS_URL",
|
||||
"FRONTEND_HOMEPAGE_FEATURE_ENABLED",
|
||||
"FRONTEND_JS_URL",
|
||||
"FRONTEND_THEME",
|
||||
"MEDIA_BASE_URL",
|
||||
"POSTHOG_KEY",
|
||||
|
||||
@@ -24,6 +24,7 @@ pytestmark = pytest.mark.django_db
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=True,
|
||||
CRISP_WEBSITE_ID="123",
|
||||
FRONTEND_CSS_URL="http://testcss/",
|
||||
FRONTEND_JS_URL="http://testjs/",
|
||||
FRONTEND_THEME="test-theme",
|
||||
MEDIA_BASE_URL="http://testserver/",
|
||||
POSTHOG_KEY={"id": "132456", "host": "https://eu.i.posthog-test.com"},
|
||||
@@ -49,6 +50,7 @@ def test_api_config(is_authenticated):
|
||||
"ENVIRONMENT": "test",
|
||||
"FRONTEND_CSS_URL": "http://testcss/",
|
||||
"FRONTEND_HOMEPAGE_FEATURE_ENABLED": True,
|
||||
"FRONTEND_JS_URL": "http://testjs/",
|
||||
"FRONTEND_THEME": "test-theme",
|
||||
"LANGUAGES": [
|
||||
["en-us", "English"],
|
||||
|
||||
@@ -509,6 +509,9 @@ class Base(Configuration):
|
||||
FRONTEND_CSS_URL = values.Value(
|
||||
None, environ_name="FRONTEND_CSS_URL", environ_prefix=None
|
||||
)
|
||||
FRONTEND_JS_URL = values.Value(
|
||||
None, environ_name="FRONTEND_JS_URL", environ_prefix=None
|
||||
)
|
||||
|
||||
THEME_CUSTOMIZATION_FILE_PATH = values.Value(
|
||||
os.path.join(BASE_DIR, "impress/configuration/theme/default.json"),
|
||||
|
||||
@@ -126,6 +126,20 @@ test.describe('Config', () => {
|
||||
).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks FRONTEND_JS_URL config', async ({ page }) => {
|
||||
await overrideConfig(page, {
|
||||
FRONTEND_JS_URL: 'http://localhost:123465/js/script.js',
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator('script[src="http://localhost:123465/js/script.js"]')
|
||||
.first(),
|
||||
).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks theme_customization.translations config', async ({
|
||||
page,
|
||||
}) => {
|
||||
@@ -145,10 +159,6 @@ test.describe('Config', () => {
|
||||
|
||||
await expect(page.getByText('MyCustomDocs')).toBeAttached();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Config: Not logged', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
test('it checks the config api is called', async ({ page }) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
@@ -168,6 +178,10 @@ test.describe('Config: Not logged', () => {
|
||||
|
||||
expect(configApi).toStrictEqual(CONFIG_LEFT);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Config: Not logged', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
test('it checks that theme is configured from config endpoint', async ({
|
||||
page,
|
||||
|
||||
@@ -153,7 +153,7 @@ test.describe('Doc Comments', () => {
|
||||
await thread.getByRole('menuitem', { name: 'Edit comment' }).click();
|
||||
const commentEditor = thread.getByText('This is a comment').first();
|
||||
await commentEditor.fill('This is an edited comment');
|
||||
const saveBtn = thread.getByRole('button', { name: 'Save' });
|
||||
const saveBtn = thread.locator('button[data-test="save"]').first();
|
||||
await saveBtn.click();
|
||||
await expect(saveBtn).toBeHidden();
|
||||
await expect(
|
||||
@@ -163,7 +163,8 @@ test.describe('Doc Comments', () => {
|
||||
|
||||
// Add second comment
|
||||
await thread.getByRole('paragraph').last().fill('This is a second comment');
|
||||
await thread.getByRole('button', { name: 'Save' }).click();
|
||||
await saveBtn.click();
|
||||
await expect(saveBtn).toBeHidden();
|
||||
await expect(
|
||||
thread.getByText('This is an edited comment').first(),
|
||||
).toBeVisible();
|
||||
@@ -371,7 +372,7 @@ test.describe('Doc Comments', () => {
|
||||
test.describe('Doc Comments mobile', () => {
|
||||
test.use({ viewport: { width: 500, height: 1200 } });
|
||||
|
||||
test('Comments are not visible on mobile', async ({ page, browserName }) => {
|
||||
test('Can comments on mobile', async ({ page, browserName }) => {
|
||||
const [title] = await createDoc(
|
||||
page,
|
||||
'comment-mobile',
|
||||
@@ -387,7 +388,16 @@ test.describe('Doc Comments mobile', () => {
|
||||
// Checks add react reaction
|
||||
const editor = await writeInEditor({ page, text: 'Hello' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await expect(page.getByRole('button', { name: 'Comment' })).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: 'Paragraph' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
const thread = page.locator('.bn-thread');
|
||||
await thread.getByRole('paragraph').first().fill('This is a comment');
|
||||
await thread.locator('[data-test="save"]').click();
|
||||
await expect(thread.getByText('This is a comment').first()).toBeHidden();
|
||||
|
||||
await editor.first().click();
|
||||
await editor.getByText('Hello').click();
|
||||
|
||||
await expect(thread.getByText('This is a comment').first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -880,14 +880,15 @@ test.describe('Doc Editor', () => {
|
||||
// Wait for the interlink to be created and rendered
|
||||
const editor = await getEditor({ page });
|
||||
|
||||
const interlinkChild2 = editor.getByRole('button', {
|
||||
name: docChild2,
|
||||
});
|
||||
const interlinkChild = editor
|
||||
.locator('.--docs--interlinking-link-inline-content')
|
||||
.first();
|
||||
|
||||
await expect(interlinkChild2).toBeVisible({ timeout: 10000 });
|
||||
await expect(interlinkChild2).toContainText('😀');
|
||||
await expect(interlinkChild2.locator('svg').first()).toBeHidden();
|
||||
await interlinkChild2.click();
|
||||
await expect(interlinkChild).toBeVisible({ timeout: 10000 });
|
||||
await expect(interlinkChild).toContainText('😀');
|
||||
await expect(interlinkChild).toContainText(docChild2);
|
||||
await expect(interlinkChild.locator('svg').first()).toBeHidden();
|
||||
await interlinkChild.click();
|
||||
|
||||
await verifyDocName(page, docChild2);
|
||||
|
||||
@@ -897,11 +898,9 @@ test.describe('Doc Editor', () => {
|
||||
await input.fill(docChild1);
|
||||
await searchContainer.getByText(docChild1).click();
|
||||
|
||||
const interlinkChild1 = editor.getByRole('button', {
|
||||
name: docChild1,
|
||||
});
|
||||
await expect(interlinkChild1).toBeVisible({ timeout: 10000 });
|
||||
await expect(interlinkChild1.locator('svg').first()).toBeVisible();
|
||||
await expect(interlinkChild).toContainText(docChild1);
|
||||
await expect(interlinkChild).toBeVisible({ timeout: 10000 });
|
||||
await expect(interlinkChild.locator('svg').first()).toBeVisible();
|
||||
|
||||
await page.keyboard.press('@');
|
||||
|
||||
@@ -961,13 +960,35 @@ test.describe('Doc Editor', () => {
|
||||
test('it embeds PDF', async ({ page, browserName }) => {
|
||||
await createDoc(page, 'doc-toolbar', browserName, 1);
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await updateShareLink(page, 'Public', 'Reading');
|
||||
|
||||
await page.getByRole('button', { name: 'Close the share modal' }).click();
|
||||
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Embed a PDF file').click();
|
||||
|
||||
const pdfBlock = page.locator('div[data-content-type="pdf"]').first();
|
||||
const pdfBlock = page.locator('div[data-content-type="pdf"]').last();
|
||||
|
||||
await expect(pdfBlock).toBeVisible();
|
||||
|
||||
// Try with invalid PDF first
|
||||
await page.getByText(/Add (PDF|file)/).click();
|
||||
|
||||
await page.locator('[data-test="embed-tab"]').click();
|
||||
|
||||
await page
|
||||
.locator('[data-test="embed-input"]')
|
||||
.fill('https://example.test/test.test');
|
||||
|
||||
await page.locator('[data-test="embed-input-button"]').click();
|
||||
|
||||
await expect(page.getByText('Invalid or missing PDF file')).toBeVisible();
|
||||
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Embed a PDF file').click();
|
||||
|
||||
// Now with a valid PDF
|
||||
await page.getByText(/Add (PDF|file)/).click();
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
@@ -992,7 +1013,7 @@ test.describe('Doc Editor', () => {
|
||||
await expect(pdfEmbed).toHaveAttribute('role', 'presentation');
|
||||
|
||||
// Check download with original filename
|
||||
await page.locator('.bn-block-content[data-content-type="pdf"]').click();
|
||||
await pdfBlock.click();
|
||||
await page.locator('[data-test="downloadfile"]').click();
|
||||
|
||||
const download = await downloadPromise;
|
||||
|
||||
@@ -527,7 +527,7 @@ test.describe('Doc Export', () => {
|
||||
|
||||
await verifyDocName(page, docChild);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
const editor = await openSuggestionMenu({ page });
|
||||
await page.getByText('Link a doc').first().click();
|
||||
|
||||
const input = page.locator(
|
||||
@@ -544,12 +544,11 @@ test.describe('Doc Export', () => {
|
||||
await searchContainer.getByText(randomDoc).click();
|
||||
|
||||
// Search the interlinking link in the editor (not in the document tree)
|
||||
const editor = page.locator('.ProseMirror.bn-editor');
|
||||
const interlink = editor.getByRole('button', {
|
||||
name: randomDoc,
|
||||
});
|
||||
const interlink = editor
|
||||
.locator('.--docs--interlinking-link-inline-content')
|
||||
.first();
|
||||
|
||||
await expect(interlink).toBeVisible();
|
||||
await expect(interlink).toContainText(randomDoc);
|
||||
|
||||
const downloadPromise = page.waitForEvent('download', (download) => {
|
||||
return download.suggestedFilename().includes(`${docChild}.pdf`);
|
||||
@@ -593,7 +592,7 @@ test.describe('Doc Export', () => {
|
||||
|
||||
await verifyDocName(page, docChild);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
const editor = await openSuggestionMenu({ page });
|
||||
await page.getByText('Link a doc').first().click();
|
||||
|
||||
const input = page.locator(
|
||||
@@ -610,12 +609,11 @@ test.describe('Doc Export', () => {
|
||||
await searchContainer.getByText(randomDoc).click();
|
||||
|
||||
// Search the interlinking link in the editor (not in the document tree)
|
||||
const editor = page.locator('.ProseMirror.bn-editor');
|
||||
const interlink = editor.getByRole('button', {
|
||||
name: randomDoc,
|
||||
});
|
||||
const interlink = editor
|
||||
.locator('.--docs--interlinking-link-inline-content')
|
||||
.first();
|
||||
|
||||
await expect(interlink).toBeVisible();
|
||||
await expect(interlink).toContainText(randomDoc);
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
|
||||
@@ -10,6 +10,7 @@ export const CONFIG = {
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
|
||||
ENVIRONMENT: 'development',
|
||||
FRONTEND_CSS_URL: null,
|
||||
FRONTEND_JS_URL: null,
|
||||
FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,
|
||||
FRONTEND_THEME: null,
|
||||
MEDIA_BASE_URL: 'http://localhost:8083',
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"test:ui::chromium": "yarn test:ui --project=chromium"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.56.1",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@types/node": "*",
|
||||
"@types/pdf-parse": "1.1.5",
|
||||
"eslint-plugin-docs": "*",
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-media/react-pdf-table": "2.0.3",
|
||||
"@blocknote/code-block": "0.44.2",
|
||||
"@blocknote/core": "0.44.2",
|
||||
"@blocknote/mantine": "0.44.2",
|
||||
"@blocknote/react": "0.44.2",
|
||||
"@blocknote/xl-docx-exporter": "0.44.2",
|
||||
"@blocknote/xl-multi-column": "0.44.2",
|
||||
"@blocknote/xl-odt-exporter": "0.44.2",
|
||||
"@blocknote/xl-pdf-exporter": "0.44.2",
|
||||
"@blocknote/code-block": "0.45.0",
|
||||
"@blocknote/core": "0.45.0",
|
||||
"@blocknote/mantine": "0.45.0",
|
||||
"@blocknote/react": "0.45.0",
|
||||
"@blocknote/xl-docx-exporter": "0.45.0",
|
||||
"@blocknote/xl-multi-column": "0.45.0",
|
||||
"@blocknote/xl-odt-exporter": "0.45.0",
|
||||
"@blocknote/xl-pdf-exporter": "0.45.0",
|
||||
"@dnd-kit/core": "6.3.1",
|
||||
"@dnd-kit/modifiers": "9.0.0",
|
||||
"@emoji-mart/data": "1.2.1",
|
||||
@@ -35,14 +35,14 @@
|
||||
"@fontsource-variable/material-symbols-outlined": "5.2.30",
|
||||
"@fontsource/material-icons": "5.2.7",
|
||||
"@gouvfr-lasuite/integration": "1.0.3",
|
||||
"@gouvfr-lasuite/ui-kit": "0.18.0",
|
||||
"@hocuspocus/provider": "3.4.0",
|
||||
"@mantine/core": "8.3.9",
|
||||
"@mantine/hooks": "8.3.9",
|
||||
"@gouvfr-lasuite/ui-kit": "0.18.4",
|
||||
"@hocuspocus/provider": "3.4.3",
|
||||
"@mantine/core": "8.3.10",
|
||||
"@mantine/hooks": "8.3.10",
|
||||
"@openfun/cunningham-react": "4.0.0",
|
||||
"@react-pdf/renderer": "4.3.1",
|
||||
"@sentry/nextjs": "10.27.0",
|
||||
"@tanstack/react-query": "5.90.10",
|
||||
"@sentry/nextjs": "10.30.0",
|
||||
"@tanstack/react-query": "5.90.12",
|
||||
"@tiptap/extensions": "*",
|
||||
"canvg": "4.0.3",
|
||||
"clsx": "2.1.1",
|
||||
@@ -52,17 +52,17 @@
|
||||
"emoji-datasource-apple": "16.0.0",
|
||||
"emoji-mart": "5.6.0",
|
||||
"emoji-regex": "10.6.0",
|
||||
"i18next": "25.6.3",
|
||||
"i18next": "25.7.2",
|
||||
"i18next-browser-languagedetector": "8.2.0",
|
||||
"idb": "8.0.3",
|
||||
"lodash": "4.17.21",
|
||||
"luxon": "3.7.2",
|
||||
"next": "15.5.9",
|
||||
"posthog-js": "1.298.0",
|
||||
"posthog-js": "1.306.1",
|
||||
"react": "*",
|
||||
"react-aria-components": "1.13.0",
|
||||
"react-dom": "*",
|
||||
"react-i18next": "16.3.5",
|
||||
"react-i18next": "16.5.0",
|
||||
"react-intersection-observer": "10.0.0",
|
||||
"react-resizable-panels": "3.0.6",
|
||||
"react-select": "5.10.2",
|
||||
@@ -70,11 +70,11 @@
|
||||
"use-debounce": "10.0.6",
|
||||
"y-protocols": "1.0.6",
|
||||
"yjs": "*",
|
||||
"zustand": "5.0.8"
|
||||
"zustand": "5.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@tanstack/react-query-devtools": "5.91.0",
|
||||
"@tanstack/react-query-devtools": "5.91.1",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.0",
|
||||
@@ -84,21 +84,21 @@
|
||||
"@types/node": "*",
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"@vitejs/plugin-react": "5.1.1",
|
||||
"@vitejs/plugin-react": "5.1.2",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"cross-env": "10.1.0",
|
||||
"dotenv": "17.2.3",
|
||||
"eslint-plugin-docs": "*",
|
||||
"fetch-mock": "9.11.0",
|
||||
"jsdom": "27.2.0",
|
||||
"jsdom": "27.3.0",
|
||||
"node-fetch": "2.7.0",
|
||||
"prettier": "3.6.2",
|
||||
"stylelint": "16.26.0",
|
||||
"prettier": "3.7.4",
|
||||
"stylelint": "16.26.1",
|
||||
"stylelint-config-standard": "39.0.1",
|
||||
"stylelint-prettier": "5.0.3",
|
||||
"typescript": "*",
|
||||
"vite-tsconfig-paths": "5.1.4",
|
||||
"vitest": "4.0.13",
|
||||
"vite-tsconfig-paths": "6.0.1",
|
||||
"vitest": "4.0.15",
|
||||
"webpack": "5.103.0",
|
||||
"workbox-webpack-plugin": "7.1.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import Head from 'next/head';
|
||||
import Script from 'next/script';
|
||||
import { PropsWithChildren, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -87,6 +88,9 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
|
||||
<link rel="stylesheet" href={conf?.FRONTEND_CSS_URL} />
|
||||
</Head>
|
||||
)}
|
||||
{conf?.FRONTEND_JS_URL && (
|
||||
<Script src={conf?.FRONTEND_JS_URL} strategy="afterInteractive" />
|
||||
)}
|
||||
<AnalyticsProvider>
|
||||
<CrispProvider websiteId={conf?.CRISP_WEBSITE_ID}>
|
||||
{children}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface ConfigResponse {
|
||||
ENVIRONMENT: string;
|
||||
FRONTEND_CSS_URL?: string;
|
||||
FRONTEND_HOMEPAGE_FEATURE_ENABLED?: boolean;
|
||||
FRONTEND_JS_URL?: string;
|
||||
FRONTEND_THEME?: Theme;
|
||||
LANGUAGES: [string, string][];
|
||||
LANGUAGE_CODE: string;
|
||||
|
||||
@@ -44,7 +44,7 @@ export const Auth = ({ children }: PropsWithChildren) => {
|
||||
if (config?.FRONTEND_HOMEPAGE_FEATURE_ENABLED) {
|
||||
if (pathname !== HOME_URL) {
|
||||
setIsRedirecting(true);
|
||||
void replace(HOME_URL).then(() => setIsRedirecting(false));
|
||||
window.location.replace(HOME_URL);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { baseApiUrl } from '@/api';
|
||||
|
||||
export const HOME_URL = '/home';
|
||||
export const HOME_URL = '/home/';
|
||||
export const LOGIN_URL = `${baseApiUrl()}authenticate/`;
|
||||
export const LOGOUT_URL = `${baseApiUrl()}logout/`;
|
||||
export const PATH_AUTH_LOCAL_STORAGE = 'docs-path-auth';
|
||||
|
||||
@@ -16,13 +16,13 @@ import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
import type { Awareness } from 'y-protocols/awareness';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { Box, TextErrors } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Doc, useProviderStore } from '@/docs/doc-management';
|
||||
import { avatarUrlFromName, useAuth } from '@/features/auth';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import {
|
||||
useHeadings,
|
||||
@@ -85,12 +85,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
const { setEditor } = useEditorStore();
|
||||
const { t } = useTranslation();
|
||||
const { themeTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isSynced: isConnectedToCollabServer } = useProviderStore();
|
||||
const refEditorContainer = useRef<HTMLDivElement>(null);
|
||||
const canSeeComment = doc.abilities.comment;
|
||||
// Determine if comments should be visible in the UI
|
||||
const showComments = canSeeComment && isDesktop;
|
||||
const showComments = canSeeComment;
|
||||
|
||||
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
|
||||
const { i18n } = useTranslation();
|
||||
@@ -117,7 +116,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
const editor: DocsBlockNoteEditor = useCreateBlockNote(
|
||||
{
|
||||
collaboration: {
|
||||
provider: provider,
|
||||
provider: provider as { awareness?: Awareness | undefined },
|
||||
fragment: provider.document.getXmlFragment('document-store'),
|
||||
user: {
|
||||
name: cursorName,
|
||||
@@ -163,8 +162,10 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
},
|
||||
dictionary: {
|
||||
...locales[lang as keyof typeof locales],
|
||||
multi_column:
|
||||
multiColumnLocales?.[lang as keyof typeof multiColumnLocales],
|
||||
...(multiColumnLocales && {
|
||||
multi_column:
|
||||
multiColumnLocales[lang as keyof typeof multiColumnLocales],
|
||||
}),
|
||||
},
|
||||
pasteHandler: ({ event, defaultPasteHandler }) => {
|
||||
// Get clipboard data
|
||||
|
||||
@@ -18,7 +18,6 @@ import { css } from 'styled-components';
|
||||
import { Box, Icon } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { useDocStore } from '@/features/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import {
|
||||
DocsBlockSchema,
|
||||
@@ -31,7 +30,6 @@ export const CommentToolbarButton = () => {
|
||||
const { currentDoc } = useDocStore();
|
||||
const { t } = useTranslation();
|
||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const comments = useExtension('comments') as unknown as ReturnType<
|
||||
ReturnType<typeof CommentsExtension>
|
||||
>;
|
||||
@@ -61,7 +59,6 @@ export const CommentToolbarButton = () => {
|
||||
|
||||
if (
|
||||
!comments ||
|
||||
!isDesktop ||
|
||||
!show ||
|
||||
!editor.isEditable ||
|
||||
!Components ||
|
||||
|
||||
@@ -37,7 +37,8 @@ export class DocsThreadStore extends ThreadStore {
|
||||
if (docAuth.canSee) {
|
||||
this.awareness = awareness;
|
||||
this.awareness?.on('update', this.onAwarenessUpdate);
|
||||
void this.refreshThreads();
|
||||
|
||||
this.refreshThreads();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +99,9 @@ export class DocsThreadStore extends ThreadStore {
|
||||
|
||||
// If we know the threadId, schedule a targeted refresh. Otherwise, fall back to full refresh.
|
||||
if (ping.threadId) {
|
||||
await this.refreshThread(ping.threadId);
|
||||
void this.refreshThread(ping.threadId);
|
||||
} else {
|
||||
await this.refreshThreads();
|
||||
this.refreshThreads();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -280,7 +281,7 @@ export class DocsThreadStore extends ThreadStore {
|
||||
}),
|
||||
);
|
||||
|
||||
await this.refreshThreads();
|
||||
this.refreshThreads();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -298,7 +299,17 @@ export class DocsThreadStore extends ThreadStore {
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
public async refreshThreads(): Promise<void> {
|
||||
public refreshThreads() {
|
||||
this.initThreads().catch((e) => {
|
||||
if (!(e instanceof APIError) || e.status !== 401) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public async initThreads(): Promise<void> {
|
||||
const response = await fetchAPI(`documents/${this.docId}/threads/`, {
|
||||
method: 'GET',
|
||||
});
|
||||
@@ -484,7 +495,7 @@ export class DocsThreadStore extends ThreadStore {
|
||||
);
|
||||
}
|
||||
|
||||
await this.refreshThreads();
|
||||
this.refreshThreads();
|
||||
this.ping(threadId);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,13 +26,16 @@ export const cssComments = (
|
||||
|
||||
// Thread modal
|
||||
.bn-thread {
|
||||
width: 400px;
|
||||
width: 100%;
|
||||
min-width: calc(min(400px, 90vw));
|
||||
max-width: calc(min(400px, 90vw));
|
||||
max-height: calc(min(500px, 60vh));
|
||||
padding: 8px;
|
||||
box-shadow: 0px 6px 18px 0px #00001229;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
gap: 0;
|
||||
overflow: auto;
|
||||
max-height: 500px;
|
||||
|
||||
.bn-default-styles {
|
||||
font-family: var(--c--globals--font--families--base);
|
||||
|
||||
@@ -13,12 +13,13 @@ import {
|
||||
createReactBlockSpec,
|
||||
} from '@blocknote/react';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { createGlobalStyle, css } from 'styled-components';
|
||||
|
||||
import { Box, Icon } from '@/components';
|
||||
import { Box, Icon, Loading } from '@/components';
|
||||
|
||||
import { ANALYZE_URL } from '../../conf';
|
||||
import { DocsBlockNoteEditor } from '../../types';
|
||||
|
||||
const PDFBlockStyle = createGlobalStyle`
|
||||
@@ -66,6 +67,9 @@ const PdfBlockComponent = ({
|
||||
const pdfUrl = block.props.url;
|
||||
const { i18n, t } = useTranslation();
|
||||
const lang = i18n.resolvedLanguage;
|
||||
const [isPDFContent, setIsPDFContent] = useState<boolean | null>(null);
|
||||
const [isPDFContentLoading, setIsPDFContentLoading] =
|
||||
useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (lang && locales[lang as keyof typeof locales]) {
|
||||
@@ -82,9 +86,55 @@ const PdfBlockComponent = ({
|
||||
}
|
||||
}, [lang, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pdfUrl || pdfUrl.includes(ANALYZE_URL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validatePDFContent = async () => {
|
||||
setIsPDFContentLoading(true);
|
||||
try {
|
||||
const response = await fetch(pdfUrl, {
|
||||
credentials: 'include',
|
||||
});
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (response.ok && contentType?.includes('application/pdf')) {
|
||||
setIsPDFContent(true);
|
||||
} else {
|
||||
setIsPDFContent(false);
|
||||
}
|
||||
} catch {
|
||||
setIsPDFContent(false);
|
||||
} finally {
|
||||
setIsPDFContentLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
void validatePDFContent();
|
||||
}, [pdfUrl]);
|
||||
|
||||
return (
|
||||
<Box ref={contentRef} className="bn-file-block-content-wrapper">
|
||||
<PDFBlockStyle />
|
||||
{isPDFContentLoading && <Loading />}
|
||||
{!isPDFContentLoading && isPDFContent !== null && !isPDFContent && (
|
||||
<Box
|
||||
$align="center"
|
||||
$justify="center"
|
||||
$color="#666"
|
||||
$background="#f5f5f5"
|
||||
$border="1px solid #ddd"
|
||||
$height="300px"
|
||||
$css={css`
|
||||
text-align: center;
|
||||
`}
|
||||
contentEditable={false}
|
||||
onClick={() => editor.setTextCursorPosition(block)}
|
||||
>
|
||||
{t('Invalid or missing PDF file.')}
|
||||
</Box>
|
||||
)}
|
||||
<ResizableFileBlockWrapper
|
||||
buttonIcon={
|
||||
<Icon iconName="upload" $size="24px" $css="line-height: normal;" />
|
||||
@@ -92,18 +142,21 @@ const PdfBlockComponent = ({
|
||||
block={block as unknown as FileBlockBlock}
|
||||
editor={editor as unknown as FileBlockEditor}
|
||||
>
|
||||
<Box
|
||||
className="bn-visual-media"
|
||||
role="presentation"
|
||||
as="embed"
|
||||
$width="100%"
|
||||
$height="450px"
|
||||
type="application/pdf"
|
||||
src={pdfUrl}
|
||||
contentEditable={false}
|
||||
draggable={false}
|
||||
onClick={() => editor.setTextCursorPosition(block)}
|
||||
/>
|
||||
{!isPDFContentLoading && isPDFContent && (
|
||||
<Box
|
||||
as="embed"
|
||||
className="bn-visual-media"
|
||||
role="presentation"
|
||||
$width="100%"
|
||||
$height="450px"
|
||||
type="application/pdf"
|
||||
src={pdfUrl}
|
||||
aria-label={block.props.name || t('PDF document')}
|
||||
contentEditable={false}
|
||||
draggable={false}
|
||||
onClick={() => editor.setTextCursorPosition(block)}
|
||||
/>
|
||||
)}
|
||||
</ResizableFileBlockWrapper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -79,10 +79,12 @@ const LinkSelected = ({ url, title }: LinkSelectedProps) => {
|
||||
|
||||
return (
|
||||
<BoxButton
|
||||
as="span"
|
||||
className="--docs--interlinking-link-inline-content"
|
||||
onClick={handleClick}
|
||||
draggable="false"
|
||||
$css={css`
|
||||
display: contents;
|
||||
display: inline;
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
& svg {
|
||||
@@ -91,7 +93,9 @@ const LinkSelected = ({ url, title }: LinkSelectedProps) => {
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${colorsTokens['gray-100']};
|
||||
background-color: var(
|
||||
--c--contextuals--background--semantic--contextual--primary
|
||||
);
|
||||
}
|
||||
transition: background-color var(--c--globals--transitions--duration)
|
||||
var(--c--globals--transitions--ease-out);
|
||||
|
||||
@@ -171,6 +171,7 @@ export const SearchPage = ({
|
||||
{trigger}
|
||||
<Box
|
||||
as="input"
|
||||
name="doc-search-input"
|
||||
$padding={{ left: '3px' }}
|
||||
$css={inputStyle}
|
||||
ref={inputRef}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const ANALYZE_URL = 'media-check';
|
||||
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { backendUrl } from '@/api';
|
||||
|
||||
import { useCreateDocAttachment } from '../api';
|
||||
import { ANALYZE_URL } from '../conf';
|
||||
import { DocsBlockNoteEditor } from '../types';
|
||||
|
||||
export const useUploadFile = (docId: string) => {
|
||||
@@ -46,7 +47,6 @@ export const useUploadFile = (docId: string) => {
|
||||
* @param editor
|
||||
*/
|
||||
export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
const ANALYZE_URL = 'media-check';
|
||||
const { t } = useTranslation();
|
||||
|
||||
/**
|
||||
|
||||
@@ -172,7 +172,10 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
|
||||
emoji={emoji}
|
||||
withEmojiPicker={doc.abilities.partial_update}
|
||||
defaultIcon={
|
||||
<SubPageIcon color="var(--c--contextuals--content--semantic--info--tertiary)" />
|
||||
<SubPageIcon
|
||||
color="var(--c--contextuals--content--semantic--info--tertiary)"
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
}
|
||||
$size="sm"
|
||||
docId={doc.id}
|
||||
|
||||
@@ -63,13 +63,11 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
||||
});
|
||||
treeContext?.treeData.handleMove(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* This function resets the tree states.
|
||||
*/
|
||||
const resetStateTree = useCallback(() => {
|
||||
if (!treeContext?.root?.id) {
|
||||
return;
|
||||
}
|
||||
treeContext?.setRoot(null);
|
||||
setInitialOpenState(undefined);
|
||||
}, [treeContext]);
|
||||
|
||||
@@ -29,6 +29,7 @@ export const Header = () => {
|
||||
<SkipToContent />
|
||||
<Box
|
||||
as="header"
|
||||
className="--docs--header"
|
||||
role="banner"
|
||||
$css={css`
|
||||
position: fixed;
|
||||
@@ -45,7 +46,6 @@ export const Header = () => {
|
||||
border-bottom: 1px solid
|
||||
var(--c--contextuals--border--surface--primary);
|
||||
`}
|
||||
className="--docs--header"
|
||||
>
|
||||
{!isDesktop && <ButtonTogglePanel />}
|
||||
<StyledLink
|
||||
@@ -88,7 +88,12 @@ export const Header = () => {
|
||||
<LaGaufre />
|
||||
</Box>
|
||||
) : (
|
||||
<Box $align="center" $gap={spacingsTokens['sm']} $direction="row">
|
||||
<Box
|
||||
className="--docs--header-block-right"
|
||||
$align="center"
|
||||
$gap={spacingsTokens['sm']}
|
||||
$direction="row"
|
||||
>
|
||||
<ButtonLogin />
|
||||
<LanguagePicker />
|
||||
<LaGaufre />
|
||||
|
||||
@@ -10,6 +10,12 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-id-pattern */
|
||||
body > #__next > .c__app > div:has(> .c__loader) {
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { HOME_URL } from '@/features/auth';
|
||||
|
||||
const Page = () => {
|
||||
const { replace } = useRouter();
|
||||
void replace(HOME_URL);
|
||||
window.location.replace(HOME_URL);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -3,6 +3,7 @@ import posthog from 'posthog-js';
|
||||
import { PostHogProvider as PHProvider } from 'posthog-js/react';
|
||||
import { JSX, PropsWithChildren, ReactNode, useEffect } from 'react';
|
||||
|
||||
import { useIsOffline } from '@/features/service-worker/hooks/useOffline';
|
||||
import { AbstractAnalytic, AnalyticEvent } from '@/libs/';
|
||||
|
||||
export class PostHogAnalytic extends AbstractAnalytic {
|
||||
@@ -45,6 +46,8 @@ export function PostHogProvider({
|
||||
children,
|
||||
conf,
|
||||
}: PropsWithChildren<PostHogProviderProps>) {
|
||||
const isOffline = useIsOffline((state) => state.isOffline);
|
||||
|
||||
useEffect(() => {
|
||||
if (!conf?.id || !conf?.host || posthog.__loaded) {
|
||||
return;
|
||||
@@ -53,9 +56,9 @@ export function PostHogProvider({
|
||||
posthog.init(conf.id, {
|
||||
api_host: conf.host,
|
||||
person_profiles: 'always',
|
||||
loaded: (posthog) => {
|
||||
loaded: (posthogInstance) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
posthog.debug();
|
||||
posthogInstance.debug();
|
||||
}
|
||||
},
|
||||
capture_pageview: false,
|
||||
@@ -71,5 +74,14 @@ export function PostHogProvider({
|
||||
};
|
||||
}, [conf?.host, conf?.id]);
|
||||
|
||||
// Disable PostHog when offline to prevent retry requests
|
||||
useEffect(() => {
|
||||
if (isOffline) {
|
||||
posthog.opt_out_capturing();
|
||||
} else {
|
||||
posthog.opt_in_capturing();
|
||||
}
|
||||
}, [isOffline]);
|
||||
|
||||
return <PHProvider client={posthog}>{children}</PHProvider>;
|
||||
}
|
||||
|
||||
@@ -31,14 +31,15 @@
|
||||
"server:test": "yarn COLLABORATION_SERVER run test"
|
||||
},
|
||||
"resolutions": {
|
||||
"@tiptap/extensions": "3.11.0",
|
||||
"@types/node": "24.10.1",
|
||||
"@types/react": "19.2.6",
|
||||
"@tiptap/extensions": "3.13.0",
|
||||
"@types/node": "24.10.4",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"docx": "9.5.0",
|
||||
"eslint": "9.39.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"eslint": "9.39.2",
|
||||
"prosemirror-view": "1.41.3",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"typescript": "5.9.3",
|
||||
"wrap-ansi": "9.0.2",
|
||||
"yjs": "13.6.27"
|
||||
|
||||
@@ -17,21 +17,21 @@
|
||||
"eslint": ">=9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "16.0.3",
|
||||
"@next/eslint-plugin-next": "15.5.9",
|
||||
"@tanstack/eslint-plugin-query": "5.91.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.47.0",
|
||||
"@typescript-eslint/parser": "8.47.0",
|
||||
"@vitest/eslint-plugin": "1.4.3",
|
||||
"eslint-config-next": "16.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.49.0",
|
||||
"@typescript-eslint/parser": "8.49.0",
|
||||
"@vitest/eslint-plugin": "1.5.2",
|
||||
"eslint-config-next": "15.5.9",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-jest": "29.2.1",
|
||||
"eslint-plugin-jest": "29.5.0",
|
||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||
"eslint-plugin-playwright": "2.3.0",
|
||||
"eslint-plugin-playwright": "2.4.0",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-react": "7.37.5",
|
||||
"eslint-plugin-testing-library": "7.13.5",
|
||||
"prettier": "3.6.2"
|
||||
"eslint-plugin-testing-library": "7.13.6",
|
||||
"prettier": "3.7.4"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"i18next-parser": "9.3.0",
|
||||
"jest": "30.2.0",
|
||||
"ts-jest": "29.4.5",
|
||||
"ts-jest": "29.4.6",
|
||||
"typescript": "*",
|
||||
"yargs": "18.0.0"
|
||||
},
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
"node": ">=22"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blocknote/server-util": "0.44.2",
|
||||
"@hocuspocus/server": "3.4.0",
|
||||
"@sentry/node": "10.26.0",
|
||||
"@sentry/profiling-node": "10.26.0",
|
||||
"@blocknote/server-util": "0.45.0",
|
||||
"@hocuspocus/server": "3.4.3",
|
||||
"@sentry/node": "10.30.0",
|
||||
"@sentry/profiling-node": "10.30.0",
|
||||
"@tiptap/extensions": "*",
|
||||
"axios": "1.13.2",
|
||||
"cors": "2.8.5",
|
||||
"express": "5.1.0",
|
||||
"express": "5.2.1",
|
||||
"express-ws": "5.0.2",
|
||||
"uuid": "13.0.0",
|
||||
"y-protocols": "1.0.6",
|
||||
"yjs": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blocknote/core": "0.44.2",
|
||||
"@hocuspocus/provider": "3.4.0",
|
||||
"@blocknote/core": "0.45.0",
|
||||
"@hocuspocus/provider": "3.4.3",
|
||||
"@types/cors": "2.8.19",
|
||||
"@types/express": "5.0.5",
|
||||
"@types/express": "5.0.6",
|
||||
"@types/express-ws": "3.0.6",
|
||||
"@types/node": "*",
|
||||
"@types/supertest": "6.0.3",
|
||||
@@ -45,7 +45,7 @@
|
||||
"ts-node": "10.9.2",
|
||||
"tsc-alias": "1.8.16",
|
||||
"typescript": "*",
|
||||
"vitest": "4.0.13",
|
||||
"vitest": "4.0.15",
|
||||
"vitest-mock-extended": "3.1.0",
|
||||
"ws": "8.18.3"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,17 +31,18 @@ backend:
|
||||
LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
|
||||
LOGGING_LEVEL_LOGGERS_ROOT: INFO
|
||||
LOGGING_LEVEL_LOGGERS_APP: INFO
|
||||
OIDC_USERINFO_SHORTNAME_FIELD: "given_name"
|
||||
OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name"
|
||||
OIDC_USERINFO_SHORTNAME_FIELD: "first_name"
|
||||
OIDC_USERINFO_FULLNAME_FIELDS: "name"
|
||||
OIDC_OP_JWKS_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/certs
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/auth
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/token
|
||||
OIDC_OP_USER_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/logout
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: "docs.127.0.0.1.nip.io"
|
||||
OIDC_RP_CLIENT_ID: docs
|
||||
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email given_name usual_name"
|
||||
OIDC_RP_SCOPES: "openid email profile"
|
||||
LOGIN_REDIRECT_URL: https://docs.127.0.0.1.nip.io
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://docs.127.0.0.1.nip.io
|
||||
LOGOUT_REDIRECT_URL: https://docs.127.0.0.1.nip.io
|
||||
|
||||
@@ -32,17 +32,18 @@ backend:
|
||||
LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
|
||||
LOGGING_LEVEL_LOGGERS_ROOT: INFO
|
||||
LOGGING_LEVEL_LOGGERS_APP: INFO
|
||||
OIDC_USERINFO_SHORTNAME_FIELD: "given_name"
|
||||
OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name"
|
||||
OIDC_USERINFO_SHORTNAME_FIELD: "first_name"
|
||||
OIDC_USERINFO_FULLNAME_FIELDS: "name"
|
||||
OIDC_OP_JWKS_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/certs
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/auth
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/token
|
||||
OIDC_OP_USER_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/logout
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: "{{ .Values.feature }}-docs.{{ .Values.domain }}"
|
||||
OIDC_RP_CLIENT_ID: docs
|
||||
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email given_name usual_name"
|
||||
OIDC_RP_SCOPES: "openid email profile"
|
||||
LOGIN_REDIRECT_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
|
||||
LOGOUT_REDIRECT_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
|
||||
|
||||
@@ -39,6 +39,14 @@
|
||||
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout` | | `86400` |
|
||||
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout` | | `86400` |
|
||||
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/upstream-hash-by` | | `$arg_room` |
|
||||
| `ingressRedirects.enabled` | whether to enable the Ingress Redirects or not | `false` |
|
||||
| `ingressRedirects.className` | IngressClass to use for the Ingress Redirects | `nil` |
|
||||
| `ingressRedirects.host` | Host for the Ingress Redirects | `impress.example.com` |
|
||||
| `ingressRedirects.tls.enabled` | Weather to enable TLS for the Ingress Redirects | `true` |
|
||||
| `ingressRedirects.tls.secretName` | Secret name for TLS config | `nil` |
|
||||
| `ingressRedirects.tls.additional[].secretName` | Secret name for additional TLS config | |
|
||||
| `ingressRedirects.tls.additional[].hosts[]` | Hosts for additional TLS config | |
|
||||
| `ingressRedirects.rules` | Rules for the Ingress Redirects | `[]` |
|
||||
| `ingressCollaborationApi.enabled` | whether to enable the Ingress or not | `false` |
|
||||
| `ingressCollaborationApi.className` | IngressClass to use for the Ingress | `nil` |
|
||||
| `ingressCollaborationApi.host` | Host for the Ingress | `impress.example.com` |
|
||||
@@ -113,15 +121,15 @@
|
||||
| `backend.job.annotations` | Annotations to add to the job [default: argocd.argoproj.io/hook: PostSync] | |
|
||||
| `backend.cronjobs` | Cronjob name, schedule, command | `[]` |
|
||||
| `backend.probes.liveness.path` | Configure path for backend HTTP liveness probe | `/__heartbeat__` |
|
||||
| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `undefined` |
|
||||
| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `nil` |
|
||||
| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `10` |
|
||||
| `backend.probes.liveness.initialDelaySeconds` | Configure timeout for backend liveness probe | `10` |
|
||||
| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | `undefined` |
|
||||
| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | `undefined` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | `undefined` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure timeout for backend startup probe | `undefined` |
|
||||
| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | `nil` |
|
||||
| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | `nil` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | `nil` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure timeout for backend startup probe | `nil` |
|
||||
| `backend.probes.readiness.path` | Configure path for backend HTTP readiness probe | `/__lbheartbeat__` |
|
||||
| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `undefined` |
|
||||
| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `nil` |
|
||||
| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `10` |
|
||||
| `backend.probes.readiness.initialDelaySeconds` | Configure timeout for backend readiness probe | `10` |
|
||||
| `backend.resources` | Resource requirements for the backend container | `{}` |
|
||||
|
||||
63
src/helm/impress/templates/ingress-redirects.yaml
Normal file
63
src/helm/impress/templates/ingress-redirects.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
{{- if .Values.ingressRedirects.enabled }}
|
||||
{{- $fullName := include "impress.fullname" . -}}
|
||||
{{- $ns := .Release.Namespace -}}
|
||||
|
||||
{{- range $i, $r := .Values.ingressRedirects.rules }}
|
||||
{{- $host := $r.host | default $.Values.ingressRedirects.host -}}
|
||||
{{- $from := $r.from | default "/home" -}}
|
||||
{{- $to := required (printf "ingressRedirects.rules[%d].to is required" $i) $r.to -}}
|
||||
{{- $name := printf "%s-redirect-%s" $fullName (replace "/" "-" (trimAll "/" $from)) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- if $i }}
|
||||
---
|
||||
{{- end }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
annotations:
|
||||
{{- if or (not $r.code) (eq (toString $r.code) "301") }}
|
||||
nginx.ingress.kubernetes.io/permanent-redirect: "{{ $to }}"
|
||||
{{- else }}
|
||||
nginx.ingress.kubernetes.io/temporal-redirect: "{{ $to }}"
|
||||
nginx.ingress.kubernetes.io/temporal-redirect-code: "{{ $r.code }}"
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if $.Values.ingressRedirects.className }}
|
||||
ingressClassName: {{ $.Values.ingressRedirects.className }}
|
||||
{{- end }}
|
||||
{{- if $.Values.ingressRedirects.tls.enabled }}
|
||||
tls:
|
||||
{{- if $host }}
|
||||
- secretName: {{ $.Values.ingressRedirects.tls.secretName | default (printf "%s-tls" $fullName) | quote }}
|
||||
hosts:
|
||||
- {{ $host | quote }}
|
||||
{{- end }}
|
||||
{{- range $.Values.ingressRedirects.tls.additional }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ $host }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ $from }}
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "impress.frontend.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.frontend.service.port }}
|
||||
- path: {{ printf "%s/" (trimSuffix "/" $from) }}
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "impress.frontend.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.frontend.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -85,6 +85,25 @@ ingressCollaborationWS:
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
|
||||
nginx.ingress.kubernetes.io/upstream-hash-by: $arg_room
|
||||
|
||||
## @param ingressRedirects.enabled whether to enable the Ingress Redirects or not
|
||||
## @param ingressRedirects.className IngressClass to use for the Ingress Redirects
|
||||
## @param ingressRedirects.host Host for the Ingress Redirects
|
||||
ingressRedirects:
|
||||
enabled: false
|
||||
className: null
|
||||
host: impress.example.com
|
||||
## @param ingressRedirects.tls.enabled Weather to enable TLS for the Ingress Redirects
|
||||
## @param ingressRedirects.tls.secretName Secret name for TLS config
|
||||
## @skip ingressRedirects.tls.additional
|
||||
## @extra ingressRedirects.tls.additional[].secretName Secret name for additional TLS config
|
||||
## @extra ingressRedirects.tls.additional[].hosts[] Hosts for additional TLS config
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: null
|
||||
additional: []
|
||||
## @param ingressRedirects.rules Rules for the Ingress Redirects
|
||||
rules: []
|
||||
|
||||
## @param ingressCollaborationApi.enabled whether to enable the Ingress or not
|
||||
## @param ingressCollaborationApi.className IngressClass to use for the Ingress
|
||||
## @param ingressCollaborationApi.host Host for the Ingress
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.5.4",
|
||||
"mjml": "4.17.1"
|
||||
"mjml": "4.18.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -581,46 +581,46 @@ minimatch@^9.0.3, minimatch@^9.0.4:
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
mjml-accordion@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.17.1.tgz#6ee3c016ea78a5a0ed29f3ec28c17d181028994f"
|
||||
integrity sha512-xl9oUbMp8aju6b1OZYqv3orE287ofGNEv09h2mFmzRTJxug7nJBFXs2I9v7dUVuWIBRk940PjnIVSuW+9bPvCA==
|
||||
mjml-accordion@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.18.0.tgz#4695289161fc631bd3f4c9546fedb84b42712ef8"
|
||||
integrity sha512-9PUmy2JxIOGgAaVHvgVYX21nVAo3o/+wJckTTF/YTLGAqB+nm+44buxRzaXxVk7qXRwbCNfE8c8mlGVNh7vB1g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-body@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.17.1.tgz#a4e4d2ee34abfbb45b74999ee49356b35830a0dc"
|
||||
integrity sha512-EfvVVfutARRjJ1jsOxxf2DY/ufqWswv9JKjbwu/Fu8h4havAcdmw2BDmX3vwXEzatqpL1l//YWOKlqUe9ZEs+A==
|
||||
mjml-body@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.18.0.tgz#20731dbbc4a92813b4c4f6ce417fc79e7a0b307f"
|
||||
integrity sha512-34AwX70/7NkRIajPsa5j6NySRiNrlLatTKhiLwTVFiVtrEFlfCcbeMNmdVixI3Ldvs8209ZC6euaAnXDRyR1zw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-button@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.17.1.tgz#1cbdf444802690329ea59ea524d088607666fa5f"
|
||||
integrity sha512-A9xQwgccPzrwr11izorBsA92THpkrviWkCwlYMxL9V3wgt5YJYDrt4r023HCveqN7b6iTkvqkXeDoIPX/kEDDQ==
|
||||
mjml-button@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.18.0.tgz#654a1fb6ade98c32f8c7c8e90c729edde81eeefb"
|
||||
integrity sha512-ZsWMI0j7EcFCMqbqdVwMWhmsVc03FhmypWXokKopGhwySn4IAB4AOURonRmFrO7k6sDeQ+iJ9QtTu7jA+S8wmg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-carousel@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.17.1.tgz#c395842741f55420dd7a3c08a76cd3d3a73e49ba"
|
||||
integrity sha512-pWo/aIgRL3XduckOBVEvbpph3vR4f9maRrbJ8Jfu4NVI6Ws3PQ6wt7HPXHmJlzJlK0gTiAF9f4+I076RVHPG7A==
|
||||
mjml-carousel@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.18.0.tgz#56e61a7873b91d9549d9992f517a471cdb433030"
|
||||
integrity sha512-wY4g1CHCOoVSZuar7CLFon/qkPbICu71IT+6pa4BDwkAiaAMAemZPyy+a+iIUgdc8kHgSuHGsGf6PQzBSMWRZA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-cli@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.17.1.tgz#0bc278762bc2391b6c61d16784156ee429cd104f"
|
||||
integrity sha512-1cMWP+yDDBUIjDYnjiKhIPW3NYJrt/W5rqOiB3zOTZQBT722Uh055S3BoLUikKxc+1sQPww4d9dH371zX2HaXA==
|
||||
mjml-cli@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.18.0.tgz#779902569f609ca6c1f26fadced10770a286b6fd"
|
||||
integrity sha512-N6CnA4o/q/VRnGPxTzvVnjAEcF7WUVVQGYfS9SPAp0qwyf7RysMmewdS9yN8GwXwZV6L2sKdn+3ANNi2FNsJ7w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
chokidar "^3.0.0"
|
||||
@@ -629,25 +629,25 @@ mjml-cli@4.17.1:
|
||||
js-beautify "^1.6.14"
|
||||
lodash "^4.17.21"
|
||||
minimatch "^9.0.3"
|
||||
mjml-core "4.17.1"
|
||||
mjml-migrate "4.17.1"
|
||||
mjml-parser-xml "4.17.1"
|
||||
mjml-validator "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
mjml-migrate "4.18.0"
|
||||
mjml-parser-xml "4.18.0"
|
||||
mjml-validator "4.18.0"
|
||||
yargs "^17.7.2"
|
||||
|
||||
mjml-column@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.17.1.tgz#3aa64972bb827fd96b596c71ce583e9ef720aa41"
|
||||
integrity sha512-S+oNZaWFP1/TCEgVwVcwqYIyHwwVZWSKLKj4fcWIMUCaHWKuojYrexOGfULDAwTjYEDhZaRDrrq96ulF12wJeQ==
|
||||
mjml-column@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.18.0.tgz#d8357449af5804e6eb506b8cf87691787e959cf5"
|
||||
integrity sha512-0QZ1whxbHUmJaRT8tW+wmr3fWZ/kpsHKAd24c7Z/N1Otm/U2G0T/FFEFJ6cB25X6ZN0K40QZ8L9gdLfiSVuRbA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-core@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.17.1.tgz#a303e8e4f94f5124284843bebd70c1d91a9085a6"
|
||||
integrity sha512-u2aHbBxFA2uJdS6T1A1ZTGYryPNebHMByRrMPCbe5W8Os+sGiC5gKLhZgjavZteKiMS+09swkvfneNLGYwzBKg==
|
||||
mjml-core@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.18.0.tgz#36d9ef9ff26cb010f81bef5342cf643b4fdf8043"
|
||||
integrity sha512-yey72LszXvIo5p0R6DB+YU8er/nP2wPsqpLKQCB0H8vG0WRT1sbSUvnCUOkKGn7subuyWDTdzHKbQO3XYIOmvg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
cheerio "1.0.0-rc.12"
|
||||
@@ -656,263 +656,263 @@ mjml-core@4.17.1:
|
||||
js-beautify "^1.6.14"
|
||||
juice "^10.0.0"
|
||||
lodash "^4.17.21"
|
||||
mjml-migrate "4.17.1"
|
||||
mjml-parser-xml "4.17.1"
|
||||
mjml-validator "4.17.1"
|
||||
mjml-migrate "4.18.0"
|
||||
mjml-parser-xml "4.18.0"
|
||||
mjml-validator "4.18.0"
|
||||
|
||||
mjml-divider@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.17.1.tgz#1219c4f25d9e6f963de9438a5adf85064c7e9f01"
|
||||
integrity sha512-KUWvcx1cIDwkN/gDuY37e9Vv+0U5U+xOVOfXRGloSnapYcP0IvmLtLTJeBwvGhwoN30wBiHDGwO8p/7B6CyxqQ==
|
||||
mjml-divider@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.18.0.tgz#36877b43836d7d05b190c109df5af73a781b6a80"
|
||||
integrity sha512-FmGUVJqi4RYroh7y85vDx0aUKZgECkxHtMQ4pkLGQbZ2g93/Qt0Ek88DVCNJ5XwUAQQkE/TvrGMLHp3CIqpQ9Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-group@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.17.1.tgz#38b4da7e67151c2c8c84378ef176432a135e11e2"
|
||||
integrity sha512-0vOcLm7l4ptLM5rqC6DhCafxIw5+WUrSYLcdUSJxO3AZMGJMxU7fkWeWGowE+PQdgqh6ee1/4RYc2qJDWtHW5A==
|
||||
mjml-group@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.18.0.tgz#09ec6c78e36c5c074260d888505efde645bc3da9"
|
||||
integrity sha512-28ABkXsKljBqj7XCC8GkQ94xz8HEU2XTyD+9LTlkDafzGp/MGJb8DcLh/7IkxCwqkQWyeMiDNLf1djsQ909Vxw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-attributes@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.17.1.tgz#198ca06a6a9b9148af1b6508aab38a41c2cf9b30"
|
||||
integrity sha512-p+g33eI4xyHb9Yv28zIXnNdsXQYvoGex/GvoGumwyxu6O8OOvPk1mIV87SjDISQHosJJMcTiZVd/RfBlwnZpGA==
|
||||
mjml-head-attributes@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.18.0.tgz#bf311924af6d2e0784df9bb95629bdc02583c0a7"
|
||||
integrity sha512-nLzix1wrMnojE0RPGhk4iKqSRwHKjie2EPzgKT7CDzfqN+Ref03E5Q19x3cQTLgxvq3C3CnvCQBfnhoS3Eakug==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-breakpoint@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.17.1.tgz#4fafa9cc176c052a2796d904ff84172290826adb"
|
||||
integrity sha512-vjsNAgdLnwqmkVlIENbH6odK6ZARiNQvsm+1o8CLo9ymw82WhIEbOnAeCfoddumZ6h2ywbZuBZzS23jJi13MUQ==
|
||||
mjml-head-breakpoint@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.18.0.tgz#4d9d2e35ec0bdb2fd2c530241c38df9c19106dfb"
|
||||
integrity sha512-k6rwff+7i+vTQYJ/CjBfE20qNqPaW60IRH2x2oEPuCzmwDmoVWOcplJIuotSqIAdfwF9hLkICknisp1BpczVlQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-font@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.17.1.tgz#0984862ebae07fbf2427e61c3d997ac155d56bd2"
|
||||
integrity sha512-Xeih/vqocR1BoBLbh8Sn67kNkfLsyHeZ7Z/3nyNz7TriZ//TGAR/PGTFFghQlXyX1BCtSx/eFoxMkKKswLYReA==
|
||||
mjml-head-font@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.18.0.tgz#f153a8b81275a3f25fa28142e3536e3dd4606c51"
|
||||
integrity sha512-ao8HB5nf+Dmxw4GO6lMMOlnj1lNZONai0GC9RobrZgPlghZw6hpURWGpkON7pQcy6XnOHwYwkV7Go/npzA2i7w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-html-attributes@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.17.1.tgz#cb4b74210257d9bb7ba9b23ca5cc409516456b8b"
|
||||
integrity sha512-O7YzEAFtSELB7wVYV808g6JcxXrzHk5glDdzzCEhDR4bjPHewSUpkrYOqvt0BdfdFsvqH4zm4vsJImUMW692HQ==
|
||||
mjml-head-html-attributes@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.18.0.tgz#167527f926919ff0cffdc8456e5db17953c81a11"
|
||||
integrity sha512-xaQE1rthe0RrNotwEr71X1tE+QQ489Yc0ynMm3oNMrohDI/TaCeazx8GAHPMM7VLduDA8D4A5wkZ6PuEvlJu4w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-preview@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.17.1.tgz#63e52bae35b43bdc43da838a0c3e85f3131439ef"
|
||||
integrity sha512-XL+8N9yrADJSw4gX9lvDcp31ghGy8WavenVO8UhxPyhLu/sMJ9lFXLbTB4z5JU1z4t/HPEp/GtgMGxAbr+QrQQ==
|
||||
mjml-head-preview@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.18.0.tgz#6469215ee85c15ffb58801d5aea3a9f1db337d2c"
|
||||
integrity sha512-2JvYqhbLyU/+Te6/1AXxzTNoHYCDYhXOVZP7wMvU4t7K34pXqyRUNO405atyHUY1MRafrl6RJ8cIx0x5vUX7PA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-style@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.17.1.tgz#29a1cb440ae36e0029af6cf1adc9eeb181ada09f"
|
||||
integrity sha512-YTjtqZAG0hD0aYwk02/8hS1W+T4nDUhVCBFmcxL/aTSrRbJQew0dSVtCvqNpAsbIJCUg/mUxx6pKKzRPdN+FtA==
|
||||
mjml-head-style@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.18.0.tgz#21746637547d8a2b9b1f9337349be4f1f95b3b20"
|
||||
integrity sha512-nEwDHkAqY3Fm7QWeAZc/a7MakZpXh6THfrE8/AWrfpgzTHrD/wihNUc09ztNpr6z/K1+JWgQfSF2BRc+X3P46g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head-title@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.17.1.tgz#def969e9ac58e975bc686fc95273ddc2d23925f1"
|
||||
integrity sha512-cUO4b7tDuX1BLu6XYnPgG40o3pBUCkT+Yzu5DGsvRxvCWougJFN68ocF6zcc7OOanmLgBYlJevQKUyT6W5Rp0g==
|
||||
mjml-head-title@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.18.0.tgz#82f43cf4ca30f31756ce6fc4c7b71aba14d59041"
|
||||
integrity sha512-0Hm8o50rPMUQLSCOOa4D4pz9NajmCDccLvBYE4fwKdeUXjSJ6bwAYeMpveel8oNZMDUVJ4Hx+PskisEGHMHM2w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-head@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.17.1.tgz#516d6039e103424d05ec5b55202b79a2b9a440f4"
|
||||
integrity sha512-+DBJ6UvkpYkKJGJKqo8luucDGbg9+rQZKytl/4VOGTE8bmbrKFixY3lkfmBrSkQ7/t6L4dDVSXywl6H91JsL+g==
|
||||
mjml-head@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.18.0.tgz#93dd1194fea0f51f23454f24835e7b3428b40872"
|
||||
integrity sha512-DS0adpIAsVMDIk2DOsHzjg+RNjQU0fF8jiVP9BmdRHVGrLPmpL9wIHZk2KvsKvZe7VaXXBijFt3DZ5/CQ/+D7Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-hero@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.17.1.tgz#d4f7ad9e29cb11107843f68a906f9389acb6a230"
|
||||
integrity sha512-WDmNVJ4+xHLrkYOrGrq23hUYDVG3iFSyk/vIC/KlcG5Kebu5vVWbe6n3ZEucatPuYn/EUVV1ofIJM6dnXXfkGQ==
|
||||
mjml-hero@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.18.0.tgz#13f6cb1763c3cdbafbbf5b7b1cbd6e4d76e25f61"
|
||||
integrity sha512-rujm0ROM4QGWw77vnl3NaVaCKXrT4xTSHeAnkHKiY5AuRf6HPTgEtutq5pdel/y6Q9GrmxvN3HRESum7tpJCJw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-image@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.17.1.tgz#9a427d719caf664b3a60b8f6cfb10e91dabdcb5d"
|
||||
integrity sha512-ZIFXmP2Fb77vvX8SBQYbrAPPvkqx5GqJ7AqVWteQk4iz6nJf8GspZiotWyL4LvgZzf0B81aQCB11y7+RvAfVvw==
|
||||
mjml-image@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.18.0.tgz#c909279b7742c8e75e2b146b8f1c565e1a0ad503"
|
||||
integrity sha512-e09NkoYwvzMcTv7V6H5doWD6Te2E1y2EvOLQJoXKVdQpDwyBWGdfnZke0scJGdA58HLAB+0mLYogpLwmfLaP5Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-migrate@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.17.1.tgz#d50dd85f5f964d2e860741e657da03078209eeb4"
|
||||
integrity sha512-Rb66BdvuV8fGYdQJzvLK0naWGI8G9smzm1OJDjdhcCrQU3BfTW/BiTS9FP5G0W73kFJe//vlHCDZ3uBIr6REAA==
|
||||
mjml-migrate@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.18.0.tgz#1b08c93a9358b45f5b607043872fad216ef0ff9e"
|
||||
integrity sha512-qfNCgW9zhJIsbPyXFA5RT/WY4mlje3N0WhHHOsHc0nY89Q01DenyslUy9nLLGXwi4K5FHS58oCjwWbMhwDcj1w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
js-beautify "^1.6.14"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-parser-xml "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
mjml-parser-xml "4.18.0"
|
||||
yargs "^17.7.2"
|
||||
|
||||
mjml-navbar@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.17.1.tgz#215e1dc8546dc9658af59770113ac0f9b3eae47e"
|
||||
integrity sha512-SWtovALlb+tM2lu2stlsKItrM/Tc/YxWiCm+UtLuOvkBmouBX/vASufaFab3VPAq/pGJKF9nFGX2eWoJCGA4rA==
|
||||
mjml-navbar@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.18.0.tgz#b367eac34f22aa05e5506c07db6a0273b6255c67"
|
||||
integrity sha512-uho/MS2tfNAe+V9u2X7NoCco34MDbdp30ETA8009Qo1VCP/D8lZ+s69WGRPu6hvN/Y2pzBgZly++CMg3qFZqBQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-parser-xml@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.17.1.tgz#7b497c20bf1bb343fe49e2c79b24aa5ae926a4a8"
|
||||
integrity sha512-8cc1+cI1+ymeKmiaioZMaIzg8K9SmCErr0WOdS0n90pnt5eLqGQEh3RQJv7VoucO5aoJXgAnCSGeCstVXvZykg==
|
||||
mjml-parser-xml@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.18.0.tgz#53f78f6d5710eee9fcf4bf110d19cd4b300d66bf"
|
||||
integrity sha512-sHSsZg4afY1heThuJzxa1Kvfh/QzB7/9P5fFUHeVnnxb07ZTXnhXWA6YbobdND5/l9+5yjN5/UgqDZm3tIT4Uw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
detect-node "2.1.0"
|
||||
htmlparser2 "^9.1.0"
|
||||
lodash "^4.17.21"
|
||||
|
||||
mjml-preset-core@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-preset-core/-/mjml-preset-core-4.17.1.tgz#7826184b7ca57383e47597c1593e492a9a5b4102"
|
||||
integrity sha512-cFfelKeRJNG+WZv+kGWjjHrQam5PiHH8JaC3vvjl1eEwLcR2nbaYArlnLTIzgG+M3+cBlIl0Ru3Say5ZqWAcxw==
|
||||
mjml-preset-core@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-preset-core/-/mjml-preset-core-4.18.0.tgz#183b79cf51847e898c6e0c70601328665e85e2ef"
|
||||
integrity sha512-x3l8vMVtsaqM/jauMeZIN7HFD2t5A28J4U0o4849yIlRxiWguLFV5l3BL8Byol+YLkoLuT9PjaZs9RYv+FGfeg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
mjml-accordion "4.17.1"
|
||||
mjml-body "4.17.1"
|
||||
mjml-button "4.17.1"
|
||||
mjml-carousel "4.17.1"
|
||||
mjml-column "4.17.1"
|
||||
mjml-divider "4.17.1"
|
||||
mjml-group "4.17.1"
|
||||
mjml-head "4.17.1"
|
||||
mjml-head-attributes "4.17.1"
|
||||
mjml-head-breakpoint "4.17.1"
|
||||
mjml-head-font "4.17.1"
|
||||
mjml-head-html-attributes "4.17.1"
|
||||
mjml-head-preview "4.17.1"
|
||||
mjml-head-style "4.17.1"
|
||||
mjml-head-title "4.17.1"
|
||||
mjml-hero "4.17.1"
|
||||
mjml-image "4.17.1"
|
||||
mjml-navbar "4.17.1"
|
||||
mjml-raw "4.17.1"
|
||||
mjml-section "4.17.1"
|
||||
mjml-social "4.17.1"
|
||||
mjml-spacer "4.17.1"
|
||||
mjml-table "4.17.1"
|
||||
mjml-text "4.17.1"
|
||||
mjml-wrapper "4.17.1"
|
||||
mjml-accordion "4.18.0"
|
||||
mjml-body "4.18.0"
|
||||
mjml-button "4.18.0"
|
||||
mjml-carousel "4.18.0"
|
||||
mjml-column "4.18.0"
|
||||
mjml-divider "4.18.0"
|
||||
mjml-group "4.18.0"
|
||||
mjml-head "4.18.0"
|
||||
mjml-head-attributes "4.18.0"
|
||||
mjml-head-breakpoint "4.18.0"
|
||||
mjml-head-font "4.18.0"
|
||||
mjml-head-html-attributes "4.18.0"
|
||||
mjml-head-preview "4.18.0"
|
||||
mjml-head-style "4.18.0"
|
||||
mjml-head-title "4.18.0"
|
||||
mjml-hero "4.18.0"
|
||||
mjml-image "4.18.0"
|
||||
mjml-navbar "4.18.0"
|
||||
mjml-raw "4.18.0"
|
||||
mjml-section "4.18.0"
|
||||
mjml-social "4.18.0"
|
||||
mjml-spacer "4.18.0"
|
||||
mjml-table "4.18.0"
|
||||
mjml-text "4.18.0"
|
||||
mjml-wrapper "4.18.0"
|
||||
|
||||
mjml-raw@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.17.1.tgz#0422013a4b8c6f35afdc624e56b47039c6c174f2"
|
||||
integrity sha512-CnfgXh+c8u/jOuVjmv9N6Hxal5U4PPJFVY1JFRRJr/7Tcxl8aJUF03mBjqW9zAzoYO1bRcgyG3clchyEwwXQ8g==
|
||||
mjml-raw@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.18.0.tgz#8683b716c592a12560175697407e5130373cf8ac"
|
||||
integrity sha512-F/kViAwXm3ccPP52kw++/mHQbcYbYYxC8JH15TZxH8GLVZkX5CGKgcBrHhDK7WoIlfEIsVRZ6IZdlHjH8vgyxw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-section@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.17.1.tgz#10339354719e7e2c02911e56510811fb5bf9fa5b"
|
||||
integrity sha512-YrkvcBgJw2NBnPirjuVU4AoqwySZzOovm5sfryID9I59EmmG+lbBJOnv/v/5wXQSlw2a4n1+VX2sCUcH5/O5sA==
|
||||
mjml-section@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.18.0.tgz#ca4257bdd83c6202b03ace84f7a6246f453f9669"
|
||||
integrity sha512-bB8My9zvIEkTOxej+TrjEeaeRT0lsypGeRADtdrRZXeqUClkkuCnCXlsNKSLGT8ZRqjUqWRc5z8ubDOvGk2+Gg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-social@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.17.1.tgz#52e9f28799a1992ae291b1b7000b7c2b58cb23be"
|
||||
integrity sha512-Agp6CHJn7SwD+cckCxibZ/32luTzAiDJDlKH0SjQ+9NvSoGskkhii3yOqtYnJ+t3NmQkxpRkXOnUN4GEbupghA==
|
||||
mjml-social@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.18.0.tgz#c9eeb1116f77120431fa27e583e3513852e0c487"
|
||||
integrity sha512-iAQc9g59L6L3VHDd55BxeIvk/zHkxflxmvuyYyOOvpmmKAvUBC//ULfpxiiM4yupofsThqFfrO+wc8d4kTRkbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-spacer@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.17.1.tgz#5a81281872f3f2556c1828bf24e8df475ac71463"
|
||||
integrity sha512-TxXDosuRzuoQNdceG47TKy+NWbwIGZmVDV/4XRtkcPHEvlsHpIIzn2+zzj+xrA6qh5Z+zlXL+x8ZpWMqrUoKfQ==
|
||||
mjml-spacer@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.18.0.tgz#ab55416f943ca1b3e515bba6203d81b88eab8337"
|
||||
integrity sha512-FK/0f5IBiONgaRpwNBs7G8EbLdAbmYqcIfHR8O8tP4LipAChLQKHO9vX3vrRMGLBZZNTESLObcFSVWmA40Mfpw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-table@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.17.1.tgz#0955b75ff86eb80a511cbcd7a37befca8c41101c"
|
||||
integrity sha512-AcAcsNrpzTOsNc0X0i0+5+iNNGEnYjwn9qodF/413yuWDSH9p7SL8vFuI3Snmgv9s1dR+BKDiF8uPt4XTOMlzA==
|
||||
mjml-table@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.18.0.tgz#3c57741f3aff14428921157d63912709aa005adc"
|
||||
integrity sha512-vJysCPUL3CHcsQDAFpW+skzBtY0RYsmMBYswI4WX0B05GLKlOjXqpYOwcmAupWeGoBVL5r/t28ynu2PqnOlN3w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-text@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.17.1.tgz#77e1598c1e4d98c10490d242c9928ec3aa6e3663"
|
||||
integrity sha512-pOrz8tRU3hReKd+K69dJmiVndC0+gB5IfVKIK3fdvYMb9laZBAstkXW0j5wn/0Af4FZSlJkDRLM7Ylxbh1+fqQ==
|
||||
mjml-text@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.18.0.tgz#f4855611975643cef33bf1b334e1734956994d8e"
|
||||
integrity sha512-hBLmF3JgveUKktKQFWHqHAr7qr92j1CxAvq7mtpDUgiWgyPFzqRX8mUsFYgZ7DmRxG4UE+Kzpt8/YFd9+E98lw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
|
||||
mjml-validator@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.17.1.tgz#d73fb08bc368763f6bf0898a88b6b8452573b2d2"
|
||||
integrity sha512-0Au5L5fIfAzOJpQG4PkpFeV0mbzCgjCTu5XbG7pJX4Wup72TGYwrA6Aq2yAdlx17kFPWThSZxeB3Xpd3/kwqOg==
|
||||
mjml-validator@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.18.0.tgz#6f5fb9804a6bb5910aa9cf0b64896fb6f9ae0132"
|
||||
integrity sha512-JmpWAsNTUlAxJOz2zHYfF8Vod8OzM3Qp5JXtrVw5tivZQzq88ZfqVGuqsas51z0pp1/ilfD4lC17YGfGwKGyhA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
|
||||
mjml-wrapper@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.17.1.tgz#8832cd7da08a32478189041b8b1c6538f204022a"
|
||||
integrity sha512-c0bCgXCwffI4krnQYU0Zp8ifGkYMgE7a65NAWXlV3AWEfVmjDlhCcD8LBfZ8UfY8zR3Che8pnunowPZfwh0Nxg==
|
||||
mjml-wrapper@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.18.0.tgz#73e62fa05f307c6b763233284e8f27a16c3c03cd"
|
||||
integrity sha512-TZeOvLjIhXEK60rjWNiYhEYNlv5GKYahE+96ifcT5OGkWkRA0DsQDfp+6VI32OS5VxsfKq2h/UdERPlQijjpAQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
lodash "^4.17.21"
|
||||
mjml-core "4.17.1"
|
||||
mjml-section "4.17.1"
|
||||
mjml-core "4.18.0"
|
||||
mjml-section "4.18.0"
|
||||
|
||||
mjml@4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.17.1.tgz#fe77de5258f31b42532f601ccb10058a2d95bdee"
|
||||
integrity sha512-aqy5EVZuwXIINl+d7vC1Fn+MzMfIU4qxCx2TUHnGJxYONrtNIgSQEDlgB2ns2oK8a8WgPuEJCZBYwRE+5ZFcng==
|
||||
mjml@4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.18.0.tgz#9e4b37d26c1ad689ca26f889048b884e47e26fff"
|
||||
integrity sha512-rQM4aqFRrNvV1k733e8hJSopBjZvoSdBpRYzNTMAN+As0jqJsO5eN0wTT2IFtfe4PREzzu5b06RkPiUQdd0IIg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
mjml-cli "4.17.1"
|
||||
mjml-core "4.17.1"
|
||||
mjml-migrate "4.17.1"
|
||||
mjml-preset-core "4.17.1"
|
||||
mjml-validator "4.17.1"
|
||||
mjml-cli "4.18.0"
|
||||
mjml-core "4.18.0"
|
||||
mjml-migrate "4.18.0"
|
||||
mjml-preset-core "4.18.0"
|
||||
mjml-validator "4.18.0"
|
||||
|
||||
no-case@^2.2.0:
|
||||
version "2.3.2"
|
||||
|
||||
Reference in New Issue
Block a user