diff --git a/apps/client/index.html b/apps/client/index.html index c96058cb..28679e40 100644 --- a/apps/client/index.html +++ b/apps/client/index.html @@ -2,10 +2,18 @@ - - - + + + Docmost + + + + + + + + diff --git a/apps/client/package.json b/apps/client/package.json index 78306af7..20a8539c 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,7 +1,7 @@ { "name": "client", "private": true, - "version": "0.22.2", + "version": "0.23.0", "scripts": { "dev": "vite", "build": "tsc && vite build", @@ -40,7 +40,7 @@ "katex": "0.16.22", "lowlight": "^3.3.0", "mantine-form-zod-resolver": "^1.3.0", - "mermaid": "^11.6.0", + "mermaid": "^11.11.0", "mitt": "^3.0.1", "posthog-js": "^1.255.1", "react": "^18.3.1", diff --git a/apps/client/public/favicon-16x16.png b/apps/client/public/favicon-16x16.png deleted file mode 100644 index 6298fe8a..00000000 Binary files a/apps/client/public/favicon-16x16.png and /dev/null differ diff --git a/apps/client/public/favicon-32x32.png b/apps/client/public/favicon-32x32.png deleted file mode 100644 index 40d6a30e..00000000 Binary files a/apps/client/public/favicon-32x32.png and /dev/null differ diff --git a/apps/client/public/icons/app-icon-192x192.png b/apps/client/public/icons/app-icon-192x192.png new file mode 100644 index 00000000..46bce9e5 Binary files /dev/null and b/apps/client/public/icons/app-icon-192x192.png differ diff --git a/apps/client/public/icons/app-icon-512x512.png b/apps/client/public/icons/app-icon-512x512.png new file mode 100644 index 00000000..65b91ed0 Binary files /dev/null and b/apps/client/public/icons/app-icon-512x512.png differ diff --git a/apps/client/public/icons/favicon-16x16.png b/apps/client/public/icons/favicon-16x16.png new file mode 100644 index 00000000..c8d2d56f Binary files /dev/null and b/apps/client/public/icons/favicon-16x16.png differ diff --git a/apps/client/public/icons/favicon-32x32.png b/apps/client/public/icons/favicon-32x32.png new file mode 100644 index 00000000..3ccc0fb0 Binary files /dev/null and b/apps/client/public/icons/favicon-32x32.png differ diff --git a/apps/client/public/locales/de-DE/translation.json b/apps/client/public/locales/de-DE/translation.json index 0199a502..bc1bd1f2 100644 --- a/apps/client/public/locales/de-DE/translation.json +++ b/apps/client/public/locales/de-DE/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "z.B. Bereich für das Produktteam", "e.g Space for sales team to collaborate": "z.B. Bereich für das Vertriebsteam zur Zusammenarbeit", "Edit": "Bearbeiten", + "Read": "Lesen", "Edit group": "Gruppe bearbeiten", "Email": "E-Mail", "Enter a strong password": "Geben Sie ein starkes Passwort ein", @@ -495,5 +496,36 @@ "Page restored successfully": "Seite erfolgreich wiederhergestellt", "Deleted by": "Gelöscht von", "Deleted at": "Gelöscht am", - "Preview": "Vorschau" + "Preview": "Vorschau", + "Subpages": "Unterseiten", + "Failed to load subpages": "Fehler beim Laden von Unterseiten", + "No subpages": "Keine Unterseiten", + "Subpages (Child pages)": "Unterseiten (Untergeordnete Seiten)", + "List all subpages of the current page": "Alle Unterseiten der aktuellen Seite auflisten", + "Attachments": "Anhänge", + "All spaces": "Alle Bereiche", + "Unknown": "Unbekannt", + "Find a space": "Einen Bereich finden", + "Search in all your spaces": "In all deinen Bereichen suchen", + "Type": "Art", + "Enterprise": "Unternehmen", + "Download attachment": "Anhang herunterladen", + "Allowed email domains": "Erlaubte E-Mail-Domains", + "Only users with email addresses from these domains can signup via SSO.": "Nur Benutzer mit E-Mail-Adressen aus diesen Domains können sich über SSO registrieren.", + "Enter valid domain names separated by comma or space": "Geben Sie gültige Domainnamen ein, durch Kommas oder Leerzeichen getrennt", + "Enforce two-factor authentication": "Erzwingen der Zwei-Faktor-Authentifizierung", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Sobald es erzwungen wird, müssen alle Mitglieder die Zwei-Faktor-Authentifizierung aktivieren, um auf den Arbeitsbereich zugreifen zu können.", + "Toggle MFA enforcement": "Umschalten der MFA-Erzwingung", + "Display name": "Anzeigename", + "Allow signup": "Registrierung erlauben", + "Enabled": "Aktiviert", + "Advanced Settings": "Erweiterte Einstellungen", + "Enable TLS/SSL": "TLS/SSL aktivieren", + "Use secure connection to LDAP server": "Sichere Verbindung zum LDAP-Server verwenden", + "Group sync": "Gruppensynchronisation", + "No SSO providers found.": "Keine SSO-Anbieter gefunden.", + "Delete SSO provider": "SSO-Anbieter löschen", + "Are you sure you want to delete this SSO provider?": "Sind Sie sicher, dass Sie diesen SSO-Anbieter löschen möchten?", + "Action": "Aktion", + "{{ssoProviderType}} configuration": "{{ssoProviderType}}-Konfiguration" } diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 3efcdfec..1dd6ef58 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "e.g Space for product team", "e.g Space for sales team to collaborate": "e.g Space for sales team to collaborate", "Edit": "Edit", + "Read": "Read", "Edit group": "Edit group", "Email": "Email", "Enter a strong password": "Enter a strong password", @@ -500,5 +501,31 @@ "Failed to load subpages": "Failed to load subpages", "No subpages": "No subpages", "Subpages (Child pages)": "Subpages (Child pages)", - "List all subpages of the current page": "List all subpages of the current page" + "List all subpages of the current page": "List all subpages of the current page", + "Attachments": "Attachments", + "All spaces": "All spaces", + "Unknown": "Unknown", + "Find a space": "Find a space", + "Search in all your spaces": "Search in all your spaces", + "Type": "Type", + "Enterprise": "Enterprise", + "Download attachment": "Download attachment", + "Allowed email domains": "Allowed email domains", + "Only users with email addresses from these domains can signup via SSO.": "Only users with email addresses from these domains can signup via SSO.", + "Enter valid domain names separated by comma or space": "Enter valid domain names separated by comma or space", + "Enforce two-factor authentication": "Enforce two-factor authentication", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Once enforced, all members must enable two-factor authentication to access the workspace.", + "Toggle MFA enforcement": "Toggle MFA enforcement", + "Display name": "Display name", + "Allow signup": "Allow signup", + "Enabled": "Enabled", + "Advanced Settings": "Advanced Settings", + "Enable TLS/SSL": "Enable TLS/SSL", + "Use secure connection to LDAP server": "Use secure connection to LDAP server", + "Group sync": "Group sync", + "No SSO providers found.": "No SSO providers found.", + "Delete SSO provider": "Delete SSO provider", + "Are you sure you want to delete this SSO provider?": "Are you sure you want to delete this SSO provider?", + "Action": "Action", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuration" } diff --git a/apps/client/public/locales/es-ES/translation.json b/apps/client/public/locales/es-ES/translation.json index 407b9f14..3450f27d 100644 --- a/apps/client/public/locales/es-ES/translation.json +++ b/apps/client/public/locales/es-ES/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "ej: Espacio para el equipo de producto", "e.g Space for sales team to collaborate": "ej: Espacio para que el equipo de ventas colabore", "Edit": "Editar", + "Read": "Leer", "Edit group": "Editar grupo", "Email": "Correo electrónico", "Enter a strong password": "Introduce una contraseña fuerte", @@ -495,5 +496,36 @@ "Page restored successfully": "Página restaurada con éxito", "Deleted by": "Eliminado por", "Deleted at": "Eliminado en", - "Preview": "Vista previa" + "Preview": "Vista previa", + "Subpages": "Subpáginas", + "Failed to load subpages": "Error al cargar subpáginas", + "No subpages": "Sin subpáginas", + "Subpages (Child pages)": "Subpáginas (Páginas hijas)", + "List all subpages of the current page": "Listar todas las subpáginas de la página actual", + "Attachments": "Adjuntos", + "All spaces": "Todos los espacios", + "Unknown": "Desconocido", + "Find a space": "Encontrar un espacio", + "Search in all your spaces": "Buscar en todos tus espacios", + "Type": "Tipo", + "Enterprise": "Empresa", + "Download attachment": "Descargar adjunto", + "Allowed email domains": "Dominios de correo electrónico permitidos", + "Only users with email addresses from these domains can signup via SSO.": "Solo los usuarios con direcciones de correo electrónico de estos dominios pueden registrarse a través de SSO.", + "Enter valid domain names separated by comma or space": "Introduce nombres de dominio válidos separados por coma o espacio", + "Enforce two-factor authentication": "Aplicar autenticación de dos factores", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Una vez aplicada, todos los miembros deben habilitar la autenticación de dos factores para acceder al espacio de trabajo.", + "Toggle MFA enforcement": "Alternar la aplicación de MFA", + "Display name": "Nombre para mostrar", + "Allow signup": "Permitir registro", + "Enabled": "Habilitado", + "Advanced Settings": "Configuración avanzada", + "Enable TLS/SSL": "Habilitar TLS/SSL", + "Use secure connection to LDAP server": "Usar conexión segura al servidor LDAP", + "Group sync": "Sincronización de grupos", + "No SSO providers found.": "No se encontraron proveedores de SSO.", + "Delete SSO provider": "Eliminar proveedor de SSO", + "Are you sure you want to delete this SSO provider?": "¿Está seguro de que desea eliminar este proveedor de SSO?", + "Action": "Acción", + "{{ssoProviderType}} configuration": "Configuración de {{ssoProviderType}}" } diff --git a/apps/client/public/locales/fr-FR/translation.json b/apps/client/public/locales/fr-FR/translation.json index a502af11..0dbd62ac 100644 --- a/apps/client/public/locales/fr-FR/translation.json +++ b/apps/client/public/locales/fr-FR/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "par ex. Espace pour l'équipe produit", "e.g Space for sales team to collaborate": "par ex. Espace pour l'équipe de vente pour collaborer", "Edit": "Modifier", + "Read": "Lire", "Edit group": "Modifier groupe", "Email": "Email", "Enter a strong password": "Entrez un mot de passe fort", @@ -495,5 +496,36 @@ "Page restored successfully": "Page restaurée avec succès", "Deleted by": "Supprimé par", "Deleted at": "Supprimé à", - "Preview": "Aperçu" + "Preview": "Aperçu", + "Subpages": "Sous-pages", + "Failed to load subpages": "Échec du chargement des sous-pages", + "No subpages": "Pas de sous-pages", + "Subpages (Child pages)": "Sous-pages (Pages enfants)", + "List all subpages of the current page": "Lister toutes les sous-pages de la page actuelle", + "Attachments": "Pièces jointes", + "All spaces": "Tous les espaces", + "Unknown": "Inconnu", + "Find a space": "Trouver un espace", + "Search in all your spaces": "Rechercher dans tous vos espaces", + "Type": "Type", + "Enterprise": "Entreprise", + "Download attachment": "Télécharger la pièce jointe", + "Allowed email domains": "Domaines de messagerie autorisés", + "Only users with email addresses from these domains can signup via SSO.": "Seuls les utilisateurs possédant des adresses e-mail provenant de ces domaines peuvent s'inscrire via SSO.", + "Enter valid domain names separated by comma or space": "Entrez des noms de domaine valides séparés par une virgule ou un espace", + "Enforce two-factor authentication": "Imposer l'authentification à deux facteurs", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Une fois appliquée, tous les membres doivent activer l'authentification à deux facteurs pour accéder à l'espace de travail.", + "Toggle MFA enforcement": "Basculer l'application de l'AMF", + "Display name": "Nom d'affichage", + "Allow signup": "Autoriser l'inscription", + "Enabled": "Activé", + "Advanced Settings": "Paramètres avancés", + "Enable TLS/SSL": "Activer TLS/SSL", + "Use secure connection to LDAP server": "Utiliser une connexion sécurisée au serveur LDAP", + "Group sync": "Synchronisation de groupe", + "No SSO providers found.": "Aucun fournisseur SSO trouvé.", + "Delete SSO provider": "Supprimer le fournisseur SSO", + "Are you sure you want to delete this SSO provider?": "Êtes-vous sûr de vouloir supprimer ce fournisseur SSO ?", + "Action": "Action", + "{{ssoProviderType}} configuration": "Configuration {{ssoProviderType}}" } diff --git a/apps/client/public/locales/it-IT/translation.json b/apps/client/public/locales/it-IT/translation.json index f72c58c7..8ed1f2c8 100644 --- a/apps/client/public/locales/it-IT/translation.json +++ b/apps/client/public/locales/it-IT/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "es. Spazio per il team di prodotto", "e.g Space for sales team to collaborate": "es. Spazio per la collaborazione del team di vendita", "Edit": "Modifica", + "Read": "Leggi", "Edit group": "Modifica gruppo", "Email": "Email", "Enter a strong password": "Inserisci una password sicura", @@ -495,5 +496,36 @@ "Page restored successfully": "Pagina ripristinata con successo", "Deleted by": "Eliminato da", "Deleted at": "Eliminato il", - "Preview": "Anteprima" + "Preview": "Anteprima", + "Subpages": "Sottopagine", + "Failed to load subpages": "Caricamento delle sottopagine non riuscito", + "No subpages": "Nessuna sottopagina", + "Subpages (Child pages)": "Sottopagine (Pagine figlie)", + "List all subpages of the current page": "Elenca tutte le sottopagine della pagina corrente", + "Attachments": "Allegati", + "All spaces": "Tutti gli spazi", + "Unknown": "Sconosciuto", + "Find a space": "Trova uno spazio", + "Search in all your spaces": "Cerca in tutti i tuoi spazi", + "Type": "Tipo", + "Enterprise": "Impresa", + "Download attachment": "Scarica allegato", + "Allowed email domains": "Domini email consentiti", + "Only users with email addresses from these domains can signup via SSO.": "Solo gli utenti con indirizzi email provenienti da questi domini possono registrarsi tramite SSO.", + "Enter valid domain names separated by comma or space": "Inserisci nomi di dominio validi separati da virgole o spazi", + "Enforce two-factor authentication": "Imponi l'autenticazione a due fattori", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Una volta impostata, tutti i membri devono abilitare l'autenticazione a due fattori per accedere all'area di lavoro.", + "Toggle MFA enforcement": "Attiva disattiva l'applicazione MFA", + "Display name": "Nome visualizzato", + "Allow signup": "Consenti iscrizione", + "Enabled": "Abilitato", + "Advanced Settings": "Impostazioni avanzate", + "Enable TLS/SSL": "Abilita TLS/SSL", + "Use secure connection to LDAP server": "Usa connessione sicura al server LDAP", + "Group sync": "Sincronizzazione gruppi", + "No SSO providers found.": "Nessun provider SSO trovato.", + "Delete SSO provider": "Elimina provider SSO", + "Are you sure you want to delete this SSO provider?": "Sei sicuro di voler eliminare questo provider SSO?", + "Action": "Azione", + "{{ssoProviderType}} configuration": "Configurazione {{ssoProviderType}}" } diff --git a/apps/client/public/locales/ja-JP/translation.json b/apps/client/public/locales/ja-JP/translation.json index 3e7950db..4e1811f3 100644 --- a/apps/client/public/locales/ja-JP/translation.json +++ b/apps/client/public/locales/ja-JP/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "例: 製品チームのスペース", "e.g Space for sales team to collaborate": "例: 営業チーム連携用スペース", "Edit": "編集", + "Read": "読む", "Edit group": "グループを編集", "Email": "メールアドレス", "Enter a strong password": "強力なパスワードを入力してください", @@ -495,5 +496,36 @@ "Page restored successfully": "ページが正常に復元されました", "Deleted by": "削除者", "Deleted at": "削除日時", - "Preview": "プレビュー" + "Preview": "プレビュー", + "Subpages": "サブページ", + "Failed to load subpages": "サブページの読み込みに失敗しました", + "No subpages": "サブページがありません", + "Subpages (Child pages)": "サブページ(子ページ)", + "List all subpages of the current page": "現在のページのすべてのサブページをリスト", + "Attachments": "添付ファイル", + "All spaces": "すべてのスペース", + "Unknown": "不明", + "Find a space": "スペースを探す", + "Search in all your spaces": "あなたのすべてのスペースで検索", + "Type": "タイプ", + "Enterprise": "エンタープライズ", + "Download attachment": "添付ファイルをダウンロード", + "Allowed email domains": "許可されたメールドメイン", + "Only users with email addresses from these domains can signup via SSO.": "これらのドメインからのメールアドレスを持つユーザーのみがSSOで登録できます。", + "Enter valid domain names separated by comma or space": "コンマまたはスペースで区切って有効なドメイン名を入力してください", + "Enforce two-factor authentication": "二要素認証を強制する", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "一度強制されると、すべてのメンバーはワークスペースにアクセスするために二要素認証を有効にする必要があります。", + "Toggle MFA enforcement": "MFAの強制を切り替える", + "Display name": "表示名", + "Allow signup": "登録を許可する", + "Enabled": "有効", + "Advanced Settings": "詳細設定", + "Enable TLS/SSL": "TLS/SSLを有効にする", + "Use secure connection to LDAP server": "LDAPサーバーへの安全な接続を使用する", + "Group sync": "グループ同期", + "No SSO providers found.": "SSOプロバイダーが見つかりませんでした。", + "Delete SSO provider": "SSOプロバイダーを削除する", + "Are you sure you want to delete this SSO provider?": "このSSOプロバイダーを削除してもよろしいですか?", + "Action": "アクション", + "{{ssoProviderType}} configuration": "{{ssoProviderType}}の構成" } diff --git a/apps/client/public/locales/ko-KR/translation.json b/apps/client/public/locales/ko-KR/translation.json index fdf35b72..c6e3dc88 100644 --- a/apps/client/public/locales/ko-KR/translation.json +++ b/apps/client/public/locales/ko-KR/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "예: 제품 팀을 위한 Space", "e.g Space for sales team to collaborate": "예: 영업 팀의 Space", "Edit": "편집", + "Read": "읽기", "Edit group": "팀 편집", "Email": "이메일", "Enter a strong password": "강력한 비밀번호를 입력하세요", @@ -495,5 +496,36 @@ "Page restored successfully": "페이지가 성공적으로 복구되었습니다", "Deleted by": "삭제자", "Deleted at": "삭제 시간", - "Preview": "미리보기" + "Preview": "미리보기", + "Subpages": "하위 페이지", + "Failed to load subpages": "하위 페이지 로드 실패", + "No subpages": "하위 페이지 없음", + "Subpages (Child pages)": "하위 페이지 (자식 페이지)", + "List all subpages of the current page": "현재 페이지의 모든 하위 페이지 목록", + "Attachments": "첨부 파일", + "All spaces": "전체 공간", + "Unknown": "알 수 없음", + "Find a space": "공간 찾기", + "Search in all your spaces": "모든 공간에서 검색", + "Type": "유형", + "Enterprise": "기업", + "Download attachment": "첨부 파일 다운로드", + "Allowed email domains": "허용된 이메일 도메인", + "Only users with email addresses from these domains can signup via SSO.": "이 도메인의 이메일 주소를 가진 사용자만 SSO를 통해 가입할 수 있습니다.", + "Enter valid domain names separated by comma or space": "콤마 또는 공백으로 구분하여 유효한 도메인 이름 입력", + "Enforce two-factor authentication": "이중 인증 시행", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "시행되면 모든 멤버가 작업 공간에 액세스하기 위해 이중 인증을 활성화해야 합니다.", + "Toggle MFA enforcement": "MFA 시행 전환", + "Display name": "표시 이름", + "Allow signup": "가입 허용", + "Enabled": "활성화됨", + "Advanced Settings": "고급 설정", + "Enable TLS/SSL": "TLS\\/SSL 활성화", + "Use secure connection to LDAP server": "LDAP 서버에 안전한 연결 사용", + "Group sync": "그룹 동기화", + "No SSO providers found.": "SSO 제공자를 찾을 수 없습니다.", + "Delete SSO provider": "SSO 제공자 삭제", + "Are you sure you want to delete this SSO provider?": "이 SSO 제공자를 삭제하시겠습니까?", + "Action": "작업", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} 구성" } diff --git a/apps/client/public/locales/nl-NL/translation.json b/apps/client/public/locales/nl-NL/translation.json index 1757f92e..7429bfe1 100644 --- a/apps/client/public/locales/nl-NL/translation.json +++ b/apps/client/public/locales/nl-NL/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "bijv. Ruimte voor productteam", "e.g Space for sales team to collaborate": "bijv. Ruimte voor verkoopteam om samen te werken", "Edit": "Bewerken", + "Read": "Lezen", "Edit group": "Groep bewerken", "Email": "E-mailadres", "Enter a strong password": "Voer een sterk wachtwoord in", @@ -495,5 +496,36 @@ "Page restored successfully": "Pagina succesvol hersteld", "Deleted by": "Verwijderd door", "Deleted at": "Verwijderd op", - "Preview": "Voorbeeld" + "Preview": "Voorbeeld", + "Subpages": "Subpagina's", + "Failed to load subpages": "Laden van subpagina's mislukt", + "No subpages": "Geen subpagina's", + "Subpages (Child pages)": "Subpagina's (Kindpagina's)", + "List all subpages of the current page": "Lijst van alle subpagina's van de huidige pagina", + "Attachments": "Bijlagen", + "All spaces": "Alle ruimtes", + "Unknown": "Onbekend", + "Find a space": "Vind een ruimte", + "Search in all your spaces": "Zoek in al je ruimtes", + "Type": "Type", + "Enterprise": "Onderneming", + "Download attachment": "Bijlage downloaden", + "Allowed email domains": "Toegestane e-maildomeinen", + "Only users with email addresses from these domains can signup via SSO.": "Alleen gebruikers met e-mailadressen van deze domeinen kunnen zich aanmelden via SSO.", + "Enter valid domain names separated by comma or space": "Voer geldige domeinnamen in, gescheiden door komma of spatie", + "Enforce two-factor authentication": "Handhaaf tweefactorauthenticatie", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Na handhaving moeten alle leden tweefactorauthenticatie inschakelen om toegang te krijgen tot de werkomgeving.", + "Toggle MFA enforcement": "Schakel MFA-handhaving in of uit", + "Display name": "Weergavenaam", + "Allow signup": "Aanmelden toestaan", + "Enabled": "Ingeschakeld", + "Advanced Settings": "Geavanceerde instellingen", + "Enable TLS/SSL": "TLS/SSL inschakelen", + "Use secure connection to LDAP server": "Gebruik een beveiligde verbinding met de LDAP-server", + "Group sync": "Groepssynchronisatie", + "No SSO providers found.": "Geen SSO-providers gevonden.", + "Delete SSO provider": "Verwijder SSO-provider", + "Are you sure you want to delete this SSO provider?": "Weet u zeker dat u deze SSO-provider wilt verwijderen?", + "Action": "Actie", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuratie" } diff --git a/apps/client/public/locales/pt-BR/translation.json b/apps/client/public/locales/pt-BR/translation.json index 1ed2c6e5..c4564830 100644 --- a/apps/client/public/locales/pt-BR/translation.json +++ b/apps/client/public/locales/pt-BR/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "ex.: Espaço para a equipe de produto", "e.g Space for sales team to collaborate": "ex.: Espaço para a equipe de vendas colaborar", "Edit": "Editar", + "Read": "Ler", "Edit group": "Editar grupo", "Email": "Email", "Enter a strong password": "Insira uma senha forte", @@ -495,5 +496,36 @@ "Page restored successfully": "Página restaurada com sucesso", "Deleted by": "Excluído por", "Deleted at": "Excluído em", - "Preview": "Visualização" + "Preview": "Visualização", + "Subpages": "Subpáginas", + "Failed to load subpages": "Falha ao carregar subpáginas", + "No subpages": "Sem subpáginas", + "Subpages (Child pages)": "Subpáginas (Páginas filhas)", + "List all subpages of the current page": "Listar todas as subpáginas da página atual", + "Attachments": "Anexos", + "All spaces": "Todos os espaços", + "Unknown": "Desconhecido", + "Find a space": "Encontrar um espaço", + "Search in all your spaces": "Pesquisar em todos os seus espaços", + "Type": "Tipo", + "Enterprise": "Empresa", + "Download attachment": "Baixar anexo", + "Allowed email domains": "Domínios de email permitidos", + "Only users with email addresses from these domains can signup via SSO.": "Apenas usuários com endereços de email desses domínios podem se inscrever via SSO.", + "Enter valid domain names separated by comma or space": "Insira nomes de domínio válidos separados por vírgula ou espaço", + "Enforce two-factor authentication": "Impor autenticação de dois fatores", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Uma vez imposto, todos os membros devem habilitar a autenticação de dois fatores para acessar o espaço de trabalho.", + "Toggle MFA enforcement": "Alternar imposição de MFA", + "Display name": "Nome de exibição", + "Allow signup": "Permitir inscrição", + "Enabled": "Habilitado", + "Advanced Settings": "Configurações Avançadas", + "Enable TLS/SSL": "Habilitar TLS/SSL", + "Use secure connection to LDAP server": "Usar conexão segura com o servidor LDAP", + "Group sync": "Sincronização de grupo", + "No SSO providers found.": "Nenhum provedor de SSO encontrado.", + "Delete SSO provider": "Excluir provedor de SSO", + "Are you sure you want to delete this SSO provider?": "Tem certeza de que deseja excluir este provedor de SSO?", + "Action": "Ação", + "{{ssoProviderType}} configuration": "Configuração de {{ssoProviderType}}" } diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index 3ad0607f..fab5389e 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "например, Пространство для продуктовой команды", "e.g Space for sales team to collaborate": "например, Пространство для совместной работы команды продаж", "Edit": "Редактировать", + "Read": "Читать", "Edit group": "Редактировать группу", "Email": "Электронная почта", "Enter a strong password": "Введите надёжный пароль", @@ -495,5 +496,36 @@ "Page restored successfully": "Страница успешно восстановлена", "Deleted by": "Удалено пользователем", "Deleted at": "Удалено в", - "Preview": "Предпросмотр" + "Preview": "Предпросмотр", + "Subpages": "Подстраницы", + "Failed to load subpages": "Не удалось загрузить подстраницы", + "No subpages": "Нет подстраниц", + "Subpages (Child pages)": "Подстраницы (вложенные страницы)", + "List all subpages of the current page": "Показать все подстраницы текущей страницы", + "Attachments": "Вложения", + "All spaces": "Все пространства", + "Unknown": "Неизвестно", + "Find a space": "Найти пространство", + "Search in all your spaces": "Поиск во всех ваших пространствах", + "Type": "Тип", + "Enterprise": "Предприятие", + "Download attachment": "Скачать вложение", + "Allowed email domains": "Разрешенные домены электронной почты", + "Only users with email addresses from these domains can signup via SSO.": "Только пользователи с электронными адресами из этих доменов могут зарегистрироваться через SSO.", + "Enter valid domain names separated by comma or space": "Введите допустимые доменные имена, разделённые запятыми или пробелами", + "Enforce two-factor authentication": "Обязательная двухфакторная аутентификация", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "После введения обязательности все участники должны будут включить двухфакторную аутентификацию для доступа к рабочему пространству.", + "Toggle MFA enforcement": "Переключить обязательность MFA", + "Display name": "Отображаемое имя", + "Allow signup": "Разрешить регистрацию", + "Enabled": "Включено", + "Advanced Settings": "Расширенные настройки", + "Enable TLS/SSL": "Включить TLS/SSL", + "Use secure connection to LDAP server": "Использовать защищённое соединение с сервером LDAP", + "Group sync": "Синхронизация группы", + "No SSO providers found.": "Поставщики SSO не найдены.", + "Delete SSO provider": "Удалить поставщика SSO", + "Are you sure you want to delete this SSO provider?": "Вы уверены, что хотите удалить этого поставщика SSO?", + "Action": "Действие", + "{{ssoProviderType}} configuration": "Настройка {{ssoProviderType}}" } diff --git a/apps/client/public/locales/uk-UA/translation.json b/apps/client/public/locales/uk-UA/translation.json index e3f359a3..e6d5427f 100644 --- a/apps/client/public/locales/uk-UA/translation.json +++ b/apps/client/public/locales/uk-UA/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "наприклад, Простір для продуктової команди", "e.g Space for sales team to collaborate": "наприклад, Простір для спільної роботи команди продажів", "Edit": "Редагувати", + "Read": "Читати", "Edit group": "Редагувати групу", "Email": "Електронна пошта", "Enter a strong password": "Введіть надійний пароль", @@ -495,5 +496,36 @@ "Page restored successfully": "Сторінку успішно відновлено", "Deleted by": "Видалено", "Deleted at": "Видалено о", - "Preview": "Попередній перегляд" + "Preview": "Попередній перегляд", + "Subpages": "Підсторінки", + "Failed to load subpages": "Не вдалося завантажити підсторінки", + "No subpages": "Немає підсторінок", + "Subpages (Child pages)": "Підсторінки (дочірні сторінки)", + "List all subpages of the current page": "Перелік всіх підсторінок поточної сторінки", + "Attachments": "Вкладення", + "All spaces": "Усі простори", + "Unknown": "Невідомо", + "Find a space": "Знайти простір", + "Search in all your spaces": "Шукати у всіх ваших просторах", + "Type": "Тип", + "Enterprise": "Підприємство", + "Download attachment": "Завантажити вкладення", + "Allowed email domains": "Дозволені домени електронної пошти", + "Only users with email addresses from these domains can signup via SSO.": "Лише користувачі з адресами електронної пошти з цих доменів можуть реєструватися через SSO.", + "Enter valid domain names separated by comma or space": "Введіть дійсні доменні імена, розділені комою або пробілом", + "Enforce two-factor authentication": "Вимагати двофакторну автентифікацію", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "Після увімкнення всі учасники повинні ввімкнути двофакторну автентифікацію для доступу до робочого простору.", + "Toggle MFA enforcement": "Перемикання вимоги MFA", + "Display name": "Відображуване ім'я", + "Allow signup": "Дозволити реєстрацію", + "Enabled": "Увімкнено", + "Advanced Settings": "Розширені налаштування", + "Enable TLS/SSL": "Увімкнути TLS/SSL", + "Use secure connection to LDAP server": "Використовувати захищене з'єднання з сервером LDAP", + "Group sync": "Синхронізація групи", + "No SSO providers found.": "Постачальників SSO не знайдено.", + "Delete SSO provider": "Видалити постачальника SSO", + "Are you sure you want to delete this SSO provider?": "Ви впевнені, що хочете видалити цього постачальника SSO?", + "Action": "Дія", + "{{ssoProviderType}} configuration": "Конфігурація {{ssoProviderType}}" } diff --git a/apps/client/public/locales/zh-CN/translation.json b/apps/client/public/locales/zh-CN/translation.json index 56c7ec85..33373155 100644 --- a/apps/client/public/locales/zh-CN/translation.json +++ b/apps/client/public/locales/zh-CN/translation.json @@ -53,6 +53,7 @@ "e.g Space for product team": "例如:产品团队的空间", "e.g Space for sales team to collaborate": "例如:销售团队协作的空间", "Edit": "编辑", + "Read": "阅读", "Edit group": "编辑群组", "Email": "电子邮箱", "Enter a strong password": "输入一个强密码", @@ -495,5 +496,36 @@ "Page restored successfully": "页面恢复成功", "Deleted by": "删除人", "Deleted at": "删除时间", - "Preview": "预览" + "Preview": "预览", + "Subpages": "子页面", + "Failed to load subpages": "加载子页面失败", + "No subpages": "没有子页面", + "Subpages (Child pages)": "子页面(子页面)", + "List all subpages of the current page": "列出当前页面的所有子页面", + "Attachments": "附件", + "All spaces": "所有空间", + "Unknown": "未知", + "Find a space": "查找空间", + "Search in all your spaces": "在您的所有空间中搜索", + "Type": "类型", + "Enterprise": "企业", + "Download attachment": "下载附件", + "Allowed email domains": "允许的电子邮件域", + "Only users with email addresses from these domains can signup via SSO.": "只有来自这些域的电子邮件地址的用户才能通过SSO注册。", + "Enter valid domain names separated by comma or space": "输入用逗号或空格分隔的有效域名", + "Enforce two-factor authentication": "强制实施双因素认证", + "Once enforced, all members must enable two-factor authentication to access the workspace.": "一旦实施,所有成员必须启用双因素认证才能访问工作区。", + "Toggle MFA enforcement": "切换多因素认证实施", + "Display name": "显示名称", + "Allow signup": "允许注册", + "Enabled": "已启用", + "Advanced Settings": "高级设置", + "Enable TLS/SSL": "启用TLS/SSL", + "Use secure connection to LDAP server": "使用安全连接到LDAP服务器", + "Group sync": "组同步", + "No SSO providers found.": "未找到SSO提供商。", + "Delete SSO provider": "删除SSO提供商", + "Are you sure you want to delete this SSO provider?": "您确定要删除此SSO提供商吗?", + "Action": "操作", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} 配置" } diff --git a/apps/client/public/manifest.json b/apps/client/public/manifest.json new file mode 100644 index 00000000..3e4b35dd --- /dev/null +++ b/apps/client/public/manifest.json @@ -0,0 +1,30 @@ +{ + "name": "Docmost", + "short_name": "Docmost", + "start_url": "/", + "display": "standalone", + "background_color": "#222", + "theme_color": "#222", + "icons": [ + { + "src": "icons/favicon-16x16.png", + "type": "image/png", + "sizes": "16x16" + }, + { + "src": "icons/favicon-32x32.png", + "type": "image/png", + "sizes": "32x32" + }, + { + "src": "icons/app-icon-192x192.png", + "type": "image/png", + "sizes": "180x180 192x192" + }, + { + "src": "icons/app-icon-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ] +} diff --git a/apps/client/src/components/settings/app-version.tsx b/apps/client/src/components/settings/app-version.tsx index cb332478..5ba9ce2a 100644 --- a/apps/client/src/components/settings/app-version.tsx +++ b/apps/client/src/components/settings/app-version.tsx @@ -50,7 +50,7 @@ export default function AppVersion() { href="https://github.com/docmost/docmost/releases" target="_blank" > - v{APP_VERSION} + {appVersion?.currentVersion && <>v{appVersion?.currentVersion}} diff --git a/apps/client/src/components/ui/responsive-settings-row.tsx b/apps/client/src/components/ui/responsive-settings-row.tsx new file mode 100644 index 00000000..ec3f65f7 --- /dev/null +++ b/apps/client/src/components/ui/responsive-settings-row.tsx @@ -0,0 +1,47 @@ +import { Box } from "@mantine/core"; +import React from "react"; + +interface ResponsiveSettingsRowProps { + children: React.ReactNode; +} + +export function ResponsiveSettingsRow({ children }: ResponsiveSettingsRowProps) { + return ( + + {children} + + ); +} + +interface ResponsiveSettingsContentProps { + children: React.ReactNode; +} + +export function ResponsiveSettingsContent({ children }: ResponsiveSettingsContentProps) { + return ( + + {children} + + ); +} + +interface ResponsiveSettingsControlProps { + children: React.ReactNode; +} + +export function ResponsiveSettingsControl({ children }: ResponsiveSettingsControlProps) { + return ( + + {children} + + ); +} diff --git a/apps/client/src/ee/mfa/components/mfa-settings.tsx b/apps/client/src/ee/mfa/components/mfa-settings.tsx index bf849f3e..73d9247d 100644 --- a/apps/client/src/ee/mfa/components/mfa-settings.tsx +++ b/apps/client/src/ee/mfa/components/mfa-settings.tsx @@ -9,6 +9,7 @@ import { MfaDisableModal } from "@/ee/mfa"; import { MfaBackupCodesModal } from "@/ee/mfa"; import { isCloud } from "@/lib/config.ts"; import useLicense from "@/ee/hooks/use-license.tsx"; +import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row"; export function MfaSettings() { const { t } = useTranslation(); @@ -53,8 +54,8 @@ export function MfaSettings() { return ( <> - -
+ + {t("2-step verification")} {!isMfaEnabled @@ -63,44 +64,46 @@ export function MfaSettings() { ) : t("Two-factor authentication is active on your account.")} -
+ - {!isMfaEnabled ? ( - - - - ) : ( - - - - - )} -
+ + + ) : ( + + + + + )} + +
- Allowed email domains + {t("Allowed email domains")} - Only users with email addresses from these domains can signup via SSO. + {t( + "Only users with email addresses from these domains can signup via SSO.", + )}
diff --git a/apps/client/src/ee/security/components/sso-google-form.tsx b/apps/client/src/ee/security/components/sso-google-form.tsx index 97a39a3b..ddd14b0f 100644 --- a/apps/client/src/ee/security/components/sso-google-form.tsx +++ b/apps/client/src/ee/security/components/sso-google-form.tsx @@ -1,6 +1,7 @@ import React from "react"; import { z } from "zod"; -import { useForm, zodResolver } from "@mantine/form"; +import { useForm } from "@mantine/form"; +import { zodResolver } from "mantine-form-zod-resolver"; import { Box, Button, Group, Stack, Switch, TextInput } from "@mantine/core"; import classes from "@/ee/security/components/sso.module.css"; import { IAuthProvider } from "@/ee/security/types/security.types.ts"; diff --git a/apps/client/src/ee/security/components/sso-ldap-form.tsx b/apps/client/src/ee/security/components/sso-ldap-form.tsx index c570a51c..662e8458 100644 --- a/apps/client/src/ee/security/components/sso-ldap-form.tsx +++ b/apps/client/src/ee/security/components/sso-ldap-form.tsx @@ -110,7 +110,7 @@ export function SsoLDAPForm({ provider, onClose }: SsoFormProps) { }> - Advanced Settings + {t("Advanced Settings")} diff --git a/apps/client/src/ee/security/components/sso-oidc-form.tsx b/apps/client/src/ee/security/components/sso-oidc-form.tsx index b1c19aa4..6f12e5d2 100644 --- a/apps/client/src/ee/security/components/sso-oidc-form.tsx +++ b/apps/client/src/ee/security/components/sso-oidc-form.tsx @@ -83,7 +83,7 @@ export function SsoOIDCForm({ provider, onClose }: SsoFormProps) { - + @@ -104,7 +104,7 @@ export default function SsoProviderList() { - + {provider.type.toUpperCase()} @@ -133,6 +133,7 @@ export default function SsoProviderList() { )} + + ))} diff --git a/apps/client/src/ee/security/components/sso-provider-modal.tsx b/apps/client/src/ee/security/components/sso-provider-modal.tsx index bf05dd54..6bc90017 100644 --- a/apps/client/src/ee/security/components/sso-provider-modal.tsx +++ b/apps/client/src/ee/security/components/sso-provider-modal.tsx @@ -6,6 +6,7 @@ import { SSO_PROVIDER } from "@/ee/security/contants.ts"; import { SsoOIDCForm } from "@/ee/security/components/sso-oidc-form.tsx"; import { SsoGoogleForm } from "@/ee/security/components/sso-google-form.tsx"; import { SsoLDAPForm } from "@/ee/security/components/sso-ldap-form.tsx"; +import { useTranslation } from "react-i18next"; interface SsoModalProps { opened: boolean; @@ -18,6 +19,8 @@ export default function SsoProviderModal({ onClose, provider, }: SsoModalProps) { + const { t } = useTranslation(); + if (!provider) { return null; } @@ -25,7 +28,9 @@ export default function SsoProviderModal({ return ( {provider.type === SSO_PROVIDER.SAML && ( diff --git a/apps/client/src/ee/security/components/sso-saml-form.tsx b/apps/client/src/ee/security/components/sso-saml-form.tsx index 696277bd..bf374697 100644 --- a/apps/client/src/ee/security/components/sso-saml-form.tsx +++ b/apps/client/src/ee/security/components/sso-saml-form.tsx @@ -1,6 +1,7 @@ import React from "react"; import { z } from "zod"; -import { useForm, zodResolver } from "@mantine/form"; +import { useForm } from "@mantine/form"; +import { zodResolver } from "mantine-form-zod-resolver"; import { Box, Button, @@ -91,7 +92,7 @@ export function SsoSamlForm({ provider, onClose }: SsoFormProps) { { if (!state) { @@ -164,19 +164,17 @@ export function CalloutMenu({ editor }: EditorMenuProps) { - - } - actionIconProps={{ - size: "lg", - variant: "default", - c: undefined - }} - /> - + } + actionIconProps={{ + size: "lg", + variant: "default", + c: undefined, + }} + /> ); diff --git a/apps/client/src/features/editor/components/drawio/drawio-view.tsx b/apps/client/src/features/editor/components/drawio/drawio-view.tsx index 468e26a0..bb901044 100644 --- a/apps/client/src/features/editor/components/drawio/drawio-view.tsx +++ b/apps/client/src/features/editor/components/drawio/drawio-view.tsx @@ -145,6 +145,7 @@ export default function DrawioView(props: NodeViewProps) { variant="default" color="gray" mx="xs" + className="print-hide" style={{ position: "absolute", top: 8, diff --git a/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx b/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx index d898e5a2..35d8de94 100644 --- a/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx +++ b/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx @@ -183,6 +183,7 @@ export default function ExcalidrawView(props: NodeViewProps) { variant="default" color="gray" mx="xs" + className="print-hide" style={{ position: "absolute", top: 8, diff --git a/apps/client/src/features/editor/styles/core.css b/apps/client/src/features/editor/styles/core.css index de60d710..051921de 100644 --- a/apps/client/src/features/editor/styles/core.css +++ b/apps/client/src/features/editor/styles/core.css @@ -33,8 +33,8 @@ } p { - margin-top: 0.65em; - margin-bottom: 0.65em; + margin-top: 0.5em; + margin-bottom: 0.5em; } ul, @@ -57,6 +57,7 @@ h5, h6 { line-height: 1.1; + margin-bottom: 10px; } code { @@ -136,7 +137,7 @@ .selection, *::selection { - background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-gray-7)); + background-color: Highlight; } .comment-mark { diff --git a/apps/client/src/features/editor/styles/print.css b/apps/client/src/features/editor/styles/print.css index ab105b40..c63e376b 100644 --- a/apps/client/src/features/editor/styles/print.css +++ b/apps/client/src/features/editor/styles/print.css @@ -1,11 +1,23 @@ @media print { - .mantine-AppShell-header, - .mantine-AppShell-navbar, - .mantine-AppShell-aside{ - display: none !important; - } + .mantine-AppShell-header, + .mantine-AppShell-navbar, + .mantine-AppShell-aside, + .print-hide, + .drag-handle { + display: none !important; + } - .mantine-AppShell-main { - padding-top: 0 !important; - } + .mantine-AppShell-main { + padding-top: 0 !important; + min-height: auto !important; + } + + .ProseMirror-selectednode { + outline: none !important; + box-shadow: none !important; + } + + .tableWrapper { + overflow: hidden !important; + } } diff --git a/apps/client/src/features/editor/styles/task-list.css b/apps/client/src/features/editor/styles/task-list.css index a2c5f6f4..b8b9fb89 100644 --- a/apps/client/src/features/editor/styles/task-list.css +++ b/apps/client/src/features/editor/styles/task-list.css @@ -10,6 +10,7 @@ ul[data-type="taskList"] { display: flex; > label { + padding-top: 0.2rem; flex: 0 0 auto; margin-right: 0.5rem; user-select: none; diff --git a/apps/client/src/features/search/components/search-result-item.tsx b/apps/client/src/features/search/components/search-result-item.tsx index 76b74f29..24d3472b 100644 --- a/apps/client/src/features/search/components/search-result-item.tsx +++ b/apps/client/src/features/search/components/search-result-item.tsx @@ -1,5 +1,13 @@ import React from "react"; -import { Group, Center, Text, Badge, ActionIcon } from "@mantine/core"; +import { + Group, + Center, + Text, + Badge, + ActionIcon, + Tooltip, + getDefaultZIndex, +} from "@mantine/core"; import { Spotlight } from "@mantine/spotlight"; import { Link } from "react-router-dom"; import { IconFile, IconDownload } from "@tabler/icons-react"; @@ -10,6 +18,7 @@ import { IPageSearch, } from "@/features/search/types/search.types"; import DOMPurify from "dompurify"; +import { useTranslation } from "react-i18next"; interface SearchResultItemProps { result: IPageSearch | IAttachmentSearch; @@ -22,6 +31,8 @@ export function SearchResultItem({ isAttachmentResult, showSpace, }: SearchResultItemProps) { + const { t } = useTranslation(); + if (isAttachmentResult) { const attachmentResult = result as IAttachmentSearch; @@ -45,7 +56,7 @@ export function SearchResultItem({ >
- +
@@ -61,21 +72,22 @@ export function SearchResultItem({ dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(attachmentResult.highlight, { ALLOWED_TAGS: ["mark", "em", "strong", "b"], - ALLOWED_ATTR: [] + ALLOWED_ATTR: [], }), }} /> )}
- - - + + + +
); @@ -111,7 +123,7 @@ export function SearchResultItem({ dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(pageResult.highlight, { ALLOWED_TAGS: ["mark", "em", "strong", "b"], - ALLOWED_ATTR: [] + ALLOWED_ATTR: [], }), }} /> diff --git a/apps/client/src/features/search/components/search-spotlight-filters.tsx b/apps/client/src/features/search/components/search-spotlight-filters.tsx index 6c3a2477..204d9570 100644 --- a/apps/client/src/features/search/components/search-spotlight-filters.tsx +++ b/apps/client/src/features/search/components/search-spotlight-filters.tsx @@ -25,6 +25,7 @@ import { useDebouncedValue } from "@mantine/hooks"; import { useGetSpacesQuery } from "@/features/space/queries/space-query"; import { useLicense } from "@/ee/hooks/use-license"; import classes from "./search-spotlight-filters.module.css"; +import { isCloud } from "@/lib/config.ts"; interface SearchSpotlightFiltersProps { onFiltersChange?: (filters: any) => void; @@ -81,11 +82,11 @@ export function SearchSpotlightFilters({ }, []); const contentTypeOptions = [ - { value: "page", label: "Pages" }, + { value: "page", label: t("Pages") }, { value: "attachment", - label: "Attachments", - disabled: !hasLicenseKey, + label: t("Attachments"), + disabled: !isCloud() && !hasLicenseKey, }, ]; @@ -165,13 +166,13 @@ export function SearchSpotlightFilters({ fw={500} > {selectedSpaceId - ? `Space: ${selectedSpaceData?.name || "Unknown"}` - : "Space: All spaces"} + ? `${t("Space")}: ${selectedSpaceData?.name || t("Unknown")}` + : `${t("Space")}: ${t("All spaces")}`} } @@ -189,15 +190,15 @@ export function SearchSpotlightFilters({
- All spaces + {t("All spaces")} - Search in all your spaces + {t("Search in all your spaces")}
{!selectedSpaceId && } @@ -246,8 +247,8 @@ export function SearchSpotlightFilters({ fw={500} > {contentType - ? `Type: ${contentTypeOptions.find((opt) => opt.value === contentType)?.label || contentType}` - : "Type"} + ? `${t("Type")}: ${contentTypeOptions.find((opt) => opt.value === contentType)?.label || t(contentType === "page" ? "Pages" : "Attachments")}` + : t("Type")} @@ -266,7 +267,7 @@ export function SearchSpotlightFilters({ {option.label} {option.disabled && ( - Enterprise + {t("Enterprise")} )} diff --git a/apps/client/src/features/search/hooks/use-unified-search.ts b/apps/client/src/features/search/hooks/use-unified-search.ts index c2c5f43c..06663efd 100644 --- a/apps/client/src/features/search/hooks/use-unified-search.ts +++ b/apps/client/src/features/search/hooks/use-unified-search.ts @@ -9,6 +9,7 @@ import { IPageSearchParams, } from "@/features/search/types/search.types"; import { useLicense } from "@/ee/hooks/use-license"; +import { isCloud } from "@/lib/config"; export type UnifiedSearchResult = IPageSearch | IAttachmentSearch; @@ -23,7 +24,7 @@ export function useUnifiedSearch( const { hasLicenseKey } = useLicense(); const isAttachmentSearch = - params.contentType === "attachment" && hasLicenseKey; + params.contentType === "attachment" && (isCloud() || hasLicenseKey); const searchType = isAttachmentSearch ? "attachment" : "page"; return useQuery({ diff --git a/apps/client/src/features/user/components/page-state-pref.tsx b/apps/client/src/features/user/components/page-state-pref.tsx index 12ba2d6f..283dc6bf 100644 --- a/apps/client/src/features/user/components/page-state-pref.tsx +++ b/apps/client/src/features/user/components/page-state-pref.tsx @@ -1,25 +1,28 @@ -import { Group, Text, MantineSize, SegmentedControl } from "@mantine/core"; +import { Text, MantineSize, SegmentedControl } from "@mantine/core"; import { useAtom } from "jotai"; import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; import { updateUser } from "@/features/user/services/user-service.ts"; import React, { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { PageEditMode } from "@/features/user/types/user.types.ts"; +import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row"; export default function PageStatePref() { const { t } = useTranslation(); return ( - -
+ + {t("Default page edit mode")} {t("Choose your preferred page edit mode. Avoid accidental edits.")} -
+ - -
+ + + + ); } diff --git a/apps/client/src/features/user/components/page-width-pref.tsx b/apps/client/src/features/user/components/page-width-pref.tsx index 6ad66062..c1ce4816 100644 --- a/apps/client/src/features/user/components/page-width-pref.tsx +++ b/apps/client/src/features/user/components/page-width-pref.tsx @@ -1,24 +1,27 @@ import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; import { updateUser } from "@/features/user/services/user-service.ts"; -import { Group, MantineSize, Switch, Text } from "@mantine/core"; +import { MantineSize, Switch, Text } from "@mantine/core"; import { useAtom } from "jotai/index"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; +import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row"; export default function PageWidthPref() { const { t } = useTranslation(); return ( - -
+ + {t("Full page width")} {t("Choose your preferred page width.")} -
+ - -
+ + + + ); } diff --git a/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx b/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx index bc7b9db5..81d5437e 100644 --- a/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx +++ b/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx @@ -26,7 +26,7 @@ export default function WorkspaceInvitesTable() { )} - +
diff --git a/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx index 427d78fe..49b9bf97 100644 --- a/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx +++ b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx @@ -4,7 +4,7 @@ import { useWorkspaceMembersQuery, } from "@/features/workspace/queries/workspace-query.ts"; import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; -import React, { useCallback, useRef, useState } from "react"; +import React from "react"; import RoleSelectMenu from "@/components/ui/role-select-menu.tsx"; import { getUserRoleLabel, @@ -54,7 +54,7 @@ export default function WorkspaceMembersTable() { return ( <> - +
diff --git a/apps/server/package.json b/apps/server/package.json index 0824b522..c1b2c41f 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.22.2", + "version": "0.23.0", "description": "", "author": "", "private": true, diff --git a/apps/server/src/core/attachment/processors/attachment.processor.ts b/apps/server/src/core/attachment/processors/attachment.processor.ts index 83323de5..bcf8a08a 100644 --- a/apps/server/src/core/attachment/processors/attachment.processor.ts +++ b/apps/server/src/core/attachment/processors/attachment.processor.ts @@ -37,7 +37,7 @@ export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy { // eslint-disable-next-line @typescript-eslint/no-require-imports AttachmentEeModule = require('./../../../ee/attachments-ee/attachment-ee.service'); } catch (err) { - this.logger.error( + this.logger.debug( 'Attachment enterprise module requested but EE module not bundled in this build', ); return; @@ -67,9 +67,15 @@ export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy { @OnWorkerEvent('failed') onError(job: Job) { - this.logger.error( - `Error processing ${job.name} job. Reason: ${job.failedReason}`, - ); + if (job.name === QueueJob.ATTACHMENT_INDEX_CONTENT) { + this.logger.debug( + `Error processing ${job.name} job for attachment ${job.data?.attachmentId}. Reason: ${job.failedReason}`, + ); + } else { + this.logger.error( + `Error processing ${job.name} job. Reason: ${job.failedReason}`, + ); + } } @OnWorkerEvent('completed') diff --git a/apps/server/src/ee b/apps/server/src/ee index 6ee9fafe..469eb56b 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 6ee9fafebd11fb3ccf35aa80ea25d33ded62e7de +Subproject commit 469eb56bb5fee0551c6c5efb17b7acf126af3240 diff --git a/apps/server/src/integrations/import/services/import-attachment.service.ts b/apps/server/src/integrations/import/services/import-attachment.service.ts index b9a488a9..660239eb 100644 --- a/apps/server/src/integrations/import/services/import-attachment.service.ts +++ b/apps/server/src/integrations/import/services/import-attachment.service.ts @@ -6,6 +6,7 @@ import { cleanUrlString } from '../utils/file.utils'; import { StorageService } from '../../storage/storage.service'; import { createReadStream } from 'node:fs'; import { promises as fs } from 'fs'; +import { Readable } from 'stream'; import { getMimeType, sanitizeFileName } from '../../../common/helpers'; import { v7 } from 'uuid'; import { FileTask } from '@docmost/db/types/entity.types'; @@ -15,6 +16,21 @@ import { unwrapFromParagraph } from '../utils/import-formatter'; import { resolveRelativeAttachmentPath } from '../utils/import.utils'; import { load } from 'cheerio'; import pLimit from 'p-limit'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue } from 'bullmq'; +import { QueueJob, QueueName } from '../../queue/constants'; + +interface AttachmentInfo { + href: string; + fileName: string; + mimeType: string; +} + +interface DrawioPair { + drawioFile?: AttachmentInfo; + pngFile?: AttachmentInfo; + baseName: string; +} @Injectable() export class ImportAttachmentService { @@ -26,6 +42,7 @@ export class ImportAttachmentService { constructor( private readonly storageService: StorageService, @InjectKysely() private readonly db: KyselyDB, + @InjectQueue(QueueName.ATTACHMENT_QUEUE) private attachmentQueue: Queue, ) {} async processAttachments(opts: { @@ -35,6 +52,7 @@ export class ImportAttachmentService { pageId: string; fileTask: FileTask; attachmentCandidates: Map; + pageAttachments?: AttachmentInfo[]; }): Promise { const { html, @@ -43,6 +61,7 @@ export class ImportAttachmentService { pageId, fileTask, attachmentCandidates, + pageAttachments = [], } = opts; const attachmentTasks: (() => Promise)[] = []; @@ -57,7 +76,7 @@ export class ImportAttachmentService { /** * Cache keyed by the *relative* path that appears in the HTML. * Ensures we upload (and DB-insert) each attachment at most once, - * even if it’s referenced multiple times on the page. + * even if it's referenced multiple times on the page. */ const processed = new Map< string, @@ -70,6 +89,99 @@ export class ImportAttachmentService { } >(); + // Analyze attachments to identify Draw.io pairs + const { drawioPairs, skipFiles } = this.analyzeAttachments(pageAttachments); + + // Map to store processed Draw.io SVGs + const drawioSvgMap = new Map< + string, + { + attachmentId: string; + apiFilePath: string; + fileName: string; + } + >(); + + //this.logger.debug(`Found ${drawioPairs.size} Draw.io pairs to process`); + + // Process Draw.io pairs and create combined SVG files + for (const [drawioHref, pair] of drawioPairs) { + if (!pair.drawioFile) continue; + + const drawioAbsPath = attachmentCandidates.get(drawioHref); + if (!drawioAbsPath) continue; + + const pngAbsPath = pair.pngFile + ? attachmentCandidates.get(pair.pngFile.href) + : undefined; + + try { + // Create combined SVG with Draw.io data and PNG image + const svgBuffer = await this.createDrawioSvg(drawioAbsPath, pngAbsPath); + + // Generate file details - always use "diagram.drawio.svg" as filename + const attachmentId = v7(); + const fileName = 'diagram.drawio.svg'; + const storageFilePath = `${getAttachmentFolderPath( + AttachmentType.File, + fileTask.workspaceId, + )}/${attachmentId}/${fileName}`; + const apiFilePath = `/api/files/${attachmentId}/${fileName}`; + + // Upload the SVG file + attachmentTasks.push(async () => { + try { + const stream = Readable.from(svgBuffer); + + // Upload to storage + await this.storageService.uploadStream(storageFilePath, stream); + + // Insert into database + await this.db + .insertInto('attachments') + .values({ + id: attachmentId, + filePath: storageFilePath, + fileName: fileName, + fileSize: svgBuffer.length, + mimeType: 'image/svg+xml', + type: 'file', + fileExt: '.svg', + creatorId: fileTask.creatorId, + workspaceId: fileTask.workspaceId, + pageId, + spaceId: fileTask.spaceId, + }) + .execute(); + + uploadStats.completed++; + } catch (error) { + uploadStats.failed++; + uploadStats.failedFiles.push(fileName); + this.logger.error( + `Failed to upload Draw.io SVG ${fileName}:`, + error, + ); + } + }); + + // Store the mapping for both Draw.io and PNG references + drawioSvgMap.set(drawioHref, { attachmentId, apiFilePath, fileName }); + if (pair.pngFile) { + drawioSvgMap.set(pair.pngFile.href, { + attachmentId, + apiFilePath, + fileName, + }); + } + } catch (error) { + this.logger.error( + `Failed to process Draw.io pair ${pair.baseName}:`, + error, + ); + } + } + const uploadOnce = (relPath: string) => { const abs = attachmentCandidates.get(relPath)!; const attachmentId = v7(); @@ -85,16 +197,18 @@ export class ImportAttachmentService { const apiFilePath = `/api/files/${attachmentId}/${fileNameWithExt}`; - attachmentTasks.push(() => this.uploadWithRetry({ - abs, - storageFilePath, - attachmentId, - fileNameWithExt, - ext, - pageId, - fileTask, - uploadStats, - })); + attachmentTasks.push(() => + this.uploadWithRetry({ + abs, + storageFilePath, + attachmentId, + fileNameWithExt, + ext, + pageId, + fileTask, + uploadStats, + }), + ); return { attachmentId, @@ -121,195 +235,301 @@ export class ImportAttachmentService { const pageDir = path.dirname(pageRelativePath); const $ = load(html); - // image - for (const imgEl of $('img').toArray()) { - const $img = $(imgEl); - const src = cleanUrlString($img.attr('src') ?? '')!; - if (!src || src.startsWith('http')) continue; + // Cache for resolved paths to avoid repeated lookups + const resolvedPathCache = new Map(); - const relPath = resolveRelativeAttachmentPath( - src, + const getCachedResolvedPath = (rawPath: string): string | null => { + if (resolvedPathCache.has(rawPath)) { + return resolvedPathCache.get(rawPath)!; + } + const resolved = resolveRelativeAttachmentPath( + rawPath, pageDir, attachmentCandidates, ); - if (!relPath) continue; + resolvedPathCache.set(rawPath, resolved); + return resolved; + }; - const { attachmentId, apiFilePath, abs } = processFile(relPath); - const stat = await fs.stat(abs); + // Cache for file stats to avoid repeated file system calls + const statCache = new Map(); - const width = $img.attr('width') ?? '100%'; - const align = $img.attr('data-align') ?? 'center'; + const getCachedStat = async (absPath: string) => { + if (statCache.has(absPath)) { + return statCache.get(absPath); + } + const stat = await fs.stat(absPath); + statCache.set(absPath, stat); + return stat; + }; - $img - .attr('src', apiFilePath) - .attr('data-attachment-id', attachmentId) - .attr('data-size', stat.size.toString()) - .attr('width', width) - .attr('data-align', align); + // Single DOM traversal for all attachment elements + const selector = + 'img, video, div[data-type="attachment"], a, div[data-type="excalidraw"], div[data-type="drawio"]'; + const elements = $(selector).toArray(); - unwrapFromParagraph($, $img); - } + for (const element of elements) { + const $el = $(element); + const tagName = element.tagName.toLowerCase(); - // video - for (const vidEl of $('video').toArray()) { - const $vid = $(vidEl); - const src = cleanUrlString($vid.attr('src') ?? '')!; - if (!src || src.startsWith('http')) continue; + // Process based on element type + if (tagName === 'img') { + const src = cleanUrlString($el.attr('src') ?? ''); + if (!src || src.startsWith('http')) continue; - const relPath = resolveRelativeAttachmentPath( - src, - pageDir, - attachmentCandidates, - ); - if (!relPath) continue; + const relPath = getCachedResolvedPath(src); + if (!relPath) continue; - const { attachmentId, apiFilePath, abs } = processFile(relPath); - const stat = await fs.stat(abs); + // Check if this image is part of a Draw.io pair + const drawioSvg = drawioSvgMap.get(relPath); + if (drawioSvg) { + const $drawio = $('
') + .attr('data-type', 'drawio') + .attr('data-src', drawioSvg.apiFilePath) + .attr('data-title', 'diagram') + .attr('data-width', '100%') + .attr('data-align', 'center') + .attr('data-attachment-id', drawioSvg.attachmentId); - const width = $vid.attr('width') ?? '100%'; - const align = $vid.attr('data-align') ?? 'center'; + $el.replaceWith($drawio); + unwrapFromParagraph($, $drawio); + continue; + } - $vid - .attr('src', apiFilePath) - .attr('data-attachment-id', attachmentId) - .attr('data-size', stat.size.toString()) - .attr('width', width) - .attr('data-align', align); + const { attachmentId, apiFilePath, abs } = processFile(relPath); + const stat = await getCachedStat(abs); - unwrapFromParagraph($, $vid); - } - - //
- for (const el of $('div[data-type="attachment"]').toArray()) { - const $oldDiv = $(el); - const rawUrl = cleanUrlString($oldDiv.attr('data-attachment-url') ?? '')!; - if (!rawUrl || rawUrl.startsWith('http')) continue; - - const relPath = resolveRelativeAttachmentPath( - rawUrl, - pageDir, - attachmentCandidates, - ); - if (!relPath) continue; - - const { attachmentId, apiFilePath, abs } = processFile(relPath); - const stat = await fs.stat(abs); - const fileName = path.basename(abs); - const mime = getMimeType(abs); - - const $newDiv = $('
') - .attr('data-type', 'attachment') - .attr('data-attachment-url', apiFilePath) - .attr('data-attachment-name', fileName) - .attr('data-attachment-mime', mime) - .attr('data-attachment-size', stat.size.toString()) - .attr('data-attachment-id', attachmentId); - - $oldDiv.replaceWith($newDiv); - unwrapFromParagraph($, $newDiv); - } - - // rewrite other attachments via - for (const aEl of $('a').toArray()) { - const $a = $(aEl); - const href = cleanUrlString($a.attr('href') ?? '')!; - if (!href || href.startsWith('http')) continue; - - const relPath = resolveRelativeAttachmentPath( - href, - pageDir, - attachmentCandidates, - ); - if (!relPath) continue; - - const { attachmentId, apiFilePath, abs } = processFile(relPath); - const stat = await fs.stat(abs); - const ext = path.extname(relPath).toLowerCase(); - - if (ext === '.mp4') { - const $video = $('