Merge branch 'main' into ai-vector

This commit is contained in:
Philipinho
2025-09-10 02:41:19 +01:00
51 changed files with 1385 additions and 365 deletions

View File

@ -2,10 +2,18 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no" />
<title>Docmost</title> <title>Docmost</title>
<meta name="theme-color" content="#1f1f1f" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#f6f7f9" media="(prefers-color-scheme: light)" />
<link rel="manifest" href="/manifest.json" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-touch-fullscreen" content="yes" />
<meta name="apple-mobile-web-app-title" content="Docmost" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<!--meta-tags--> <!--meta-tags-->
</head> </head>
<body> <body>

View File

@ -1,7 +1,7 @@
{ {
"name": "client", "name": "client",
"private": true, "private": true,
"version": "0.22.2", "version": "0.23.0",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
@ -40,7 +40,7 @@
"katex": "0.16.22", "katex": "0.16.22",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",
"mantine-form-zod-resolver": "^1.3.0", "mantine-form-zod-resolver": "^1.3.0",
"mermaid": "^11.6.0", "mermaid": "^11.11.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"posthog-js": "^1.255.1", "posthog-js": "^1.255.1",
"react": "^18.3.1", "react": "^18.3.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "z.B. Bereich für das Produktteam", "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", "e.g Space for sales team to collaborate": "z.B. Bereich für das Vertriebsteam zur Zusammenarbeit",
"Edit": "Bearbeiten", "Edit": "Bearbeiten",
"Read": "Lesen",
"Edit group": "Gruppe bearbeiten", "Edit group": "Gruppe bearbeiten",
"Email": "E-Mail", "Email": "E-Mail",
"Enter a strong password": "Geben Sie ein starkes Passwort ein", "Enter a strong password": "Geben Sie ein starkes Passwort ein",
@ -495,5 +496,36 @@
"Page restored successfully": "Seite erfolgreich wiederhergestellt", "Page restored successfully": "Seite erfolgreich wiederhergestellt",
"Deleted by": "Gelöscht von", "Deleted by": "Gelöscht von",
"Deleted at": "Gelöscht am", "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"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "e.g Space for product team", "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", "e.g Space for sales team to collaborate": "e.g Space for sales team to collaborate",
"Edit": "Edit", "Edit": "Edit",
"Read": "Read",
"Edit group": "Edit group", "Edit group": "Edit group",
"Email": "Email", "Email": "Email",
"Enter a strong password": "Enter a strong password", "Enter a strong password": "Enter a strong password",
@ -500,5 +501,31 @@
"Failed to load subpages": "Failed to load subpages", "Failed to load subpages": "Failed to load subpages",
"No subpages": "No subpages", "No subpages": "No subpages",
"Subpages (Child pages)": "Subpages (Child pages)", "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"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "ej: Espacio para el equipo de producto", "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", "e.g Space for sales team to collaborate": "ej: Espacio para que el equipo de ventas colabore",
"Edit": "Editar", "Edit": "Editar",
"Read": "Leer",
"Edit group": "Editar grupo", "Edit group": "Editar grupo",
"Email": "Correo electrónico", "Email": "Correo electrónico",
"Enter a strong password": "Introduce una contraseña fuerte", "Enter a strong password": "Introduce una contraseña fuerte",
@ -495,5 +496,36 @@
"Page restored successfully": "Página restaurada con éxito", "Page restored successfully": "Página restaurada con éxito",
"Deleted by": "Eliminado por", "Deleted by": "Eliminado por",
"Deleted at": "Eliminado en", "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}}"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "par ex. Espace pour l'équipe produit", "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", "e.g Space for sales team to collaborate": "par ex. Espace pour l'équipe de vente pour collaborer",
"Edit": "Modifier", "Edit": "Modifier",
"Read": "Lire",
"Edit group": "Modifier groupe", "Edit group": "Modifier groupe",
"Email": "Email", "Email": "Email",
"Enter a strong password": "Entrez un mot de passe fort", "Enter a strong password": "Entrez un mot de passe fort",
@ -495,5 +496,36 @@
"Page restored successfully": "Page restaurée avec succès", "Page restored successfully": "Page restaurée avec succès",
"Deleted by": "Supprimé par", "Deleted by": "Supprimé par",
"Deleted at": "Supprimé à", "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}}"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "es. Spazio per il team di prodotto", "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", "e.g Space for sales team to collaborate": "es. Spazio per la collaborazione del team di vendita",
"Edit": "Modifica", "Edit": "Modifica",
"Read": "Leggi",
"Edit group": "Modifica gruppo", "Edit group": "Modifica gruppo",
"Email": "Email", "Email": "Email",
"Enter a strong password": "Inserisci una password sicura", "Enter a strong password": "Inserisci una password sicura",
@ -495,5 +496,36 @@
"Page restored successfully": "Pagina ripristinata con successo", "Page restored successfully": "Pagina ripristinata con successo",
"Deleted by": "Eliminato da", "Deleted by": "Eliminato da",
"Deleted at": "Eliminato il", "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}}"
} }

View File

@ -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": "読む",
"Edit group": "グループを編集", "Edit group": "グループを編集",
"Email": "メールアドレス", "Email": "メールアドレス",
"Enter a strong password": "強力なパスワードを入力してください", "Enter a strong password": "強力なパスワードを入力してください",
@ -495,5 +496,36 @@
"Page restored successfully": "ページが正常に復元されました", "Page restored successfully": "ページが正常に復元されました",
"Deleted by": "削除者", "Deleted by": "削除者",
"Deleted at": "削除日時", "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}}の構成"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "예: 제품 팀을 위한 Space", "e.g Space for product team": "예: 제품 팀을 위한 Space",
"e.g Space for sales team to collaborate": "예: 영업 팀의 Space", "e.g Space for sales team to collaborate": "예: 영업 팀의 Space",
"Edit": "편집", "Edit": "편집",
"Read": "읽기",
"Edit group": "팀 편집", "Edit group": "팀 편집",
"Email": "이메일", "Email": "이메일",
"Enter a strong password": "강력한 비밀번호를 입력하세요", "Enter a strong password": "강력한 비밀번호를 입력하세요",
@ -495,5 +496,36 @@
"Page restored successfully": "페이지가 성공적으로 복구되었습니다", "Page restored successfully": "페이지가 성공적으로 복구되었습니다",
"Deleted by": "삭제자", "Deleted by": "삭제자",
"Deleted at": "삭제 시간", "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}} 구성"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "bijv. Ruimte voor productteam", "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", "e.g Space for sales team to collaborate": "bijv. Ruimte voor verkoopteam om samen te werken",
"Edit": "Bewerken", "Edit": "Bewerken",
"Read": "Lezen",
"Edit group": "Groep bewerken", "Edit group": "Groep bewerken",
"Email": "E-mailadres", "Email": "E-mailadres",
"Enter a strong password": "Voer een sterk wachtwoord in", "Enter a strong password": "Voer een sterk wachtwoord in",
@ -495,5 +496,36 @@
"Page restored successfully": "Pagina succesvol hersteld", "Page restored successfully": "Pagina succesvol hersteld",
"Deleted by": "Verwijderd door", "Deleted by": "Verwijderd door",
"Deleted at": "Verwijderd op", "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"
} }

View File

@ -53,6 +53,7 @@
"e.g Space for product team": "ex.: Espaço para a equipe de produto", "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", "e.g Space for sales team to collaborate": "ex.: Espaço para a equipe de vendas colaborar",
"Edit": "Editar", "Edit": "Editar",
"Read": "Ler",
"Edit group": "Editar grupo", "Edit group": "Editar grupo",
"Email": "Email", "Email": "Email",
"Enter a strong password": "Insira uma senha forte", "Enter a strong password": "Insira uma senha forte",
@ -495,5 +496,36 @@
"Page restored successfully": "Página restaurada com sucesso", "Page restored successfully": "Página restaurada com sucesso",
"Deleted by": "Excluído por", "Deleted by": "Excluído por",
"Deleted at": "Excluído em", "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}}"
} }

View File

@ -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": "Читать",
"Edit group": "Редактировать группу", "Edit group": "Редактировать группу",
"Email": "Электронная почта", "Email": "Электронная почта",
"Enter a strong password": "Введите надёжный пароль", "Enter a strong password": "Введите надёжный пароль",
@ -495,5 +496,36 @@
"Page restored successfully": "Страница успешно восстановлена", "Page restored successfully": "Страница успешно восстановлена",
"Deleted by": "Удалено пользователем", "Deleted by": "Удалено пользователем",
"Deleted at": "Удалено в", "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}}"
} }

View File

@ -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": "Читати",
"Edit group": "Редагувати групу", "Edit group": "Редагувати групу",
"Email": "Електронна пошта", "Email": "Електронна пошта",
"Enter a strong password": "Введіть надійний пароль", "Enter a strong password": "Введіть надійний пароль",
@ -495,5 +496,36 @@
"Page restored successfully": "Сторінку успішно відновлено", "Page restored successfully": "Сторінку успішно відновлено",
"Deleted by": "Видалено", "Deleted by": "Видалено",
"Deleted at": "Видалено о", "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}}"
} }

View File

@ -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": "阅读",
"Edit group": "编辑群组", "Edit group": "编辑群组",
"Email": "电子邮箱", "Email": "电子邮箱",
"Enter a strong password": "输入一个强密码", "Enter a strong password": "输入一个强密码",
@ -495,5 +496,36 @@
"Page restored successfully": "页面恢复成功", "Page restored successfully": "页面恢复成功",
"Deleted by": "删除人", "Deleted by": "删除人",
"Deleted at": "删除时间", "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}} 配置"
} }

View File

@ -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"
}
]
}

View File

@ -50,7 +50,7 @@ export default function AppVersion() {
href="https://github.com/docmost/docmost/releases" href="https://github.com/docmost/docmost/releases"
target="_blank" target="_blank"
> >
v{APP_VERSION} {appVersion?.currentVersion && <>v{appVersion?.currentVersion}</>}
</Text> </Text>
</Indicator> </Indicator>
</Tooltip> </Tooltip>

View File

@ -0,0 +1,47 @@
import { Box } from "@mantine/core";
import React from "react";
interface ResponsiveSettingsRowProps {
children: React.ReactNode;
}
export function ResponsiveSettingsRow({ children }: ResponsiveSettingsRowProps) {
return (
<Box
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
gap: "1rem",
flexWrap: "wrap",
}}
>
{children}
</Box>
);
}
interface ResponsiveSettingsContentProps {
children: React.ReactNode;
}
export function ResponsiveSettingsContent({ children }: ResponsiveSettingsContentProps) {
return (
<Box style={{ flex: "1 1 300px", minWidth: 0 }}>
{children}
</Box>
);
}
interface ResponsiveSettingsControlProps {
children: React.ReactNode;
}
export function ResponsiveSettingsControl({ children }: ResponsiveSettingsControlProps) {
return (
<Box style={{ flex: "0 0 auto" }}>
{children}
</Box>
);
}

View File

@ -9,6 +9,7 @@ import { MfaDisableModal } from "@/ee/mfa";
import { MfaBackupCodesModal } from "@/ee/mfa"; import { MfaBackupCodesModal } from "@/ee/mfa";
import { isCloud } from "@/lib/config.ts"; import { isCloud } from "@/lib/config.ts";
import useLicense from "@/ee/hooks/use-license.tsx"; import useLicense from "@/ee/hooks/use-license.tsx";
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
export function MfaSettings() { export function MfaSettings() {
const { t } = useTranslation(); const { t } = useTranslation();
@ -53,8 +54,8 @@ export function MfaSettings() {
return ( return (
<> <>
<Group justify="space-between" wrap="nowrap" gap="xl"> <ResponsiveSettingsRow>
<div style={{ minWidth: 0, flex: 1 }}> <ResponsiveSettingsContent>
<Text size="md">{t("2-step verification")}</Text> <Text size="md">{t("2-step verification")}</Text>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{!isMfaEnabled {!isMfaEnabled
@ -63,8 +64,9 @@ export function MfaSettings() {
) )
: t("Two-factor authentication is active on your account.")} : t("Two-factor authentication is active on your account.")}
</Text> </Text>
</div> </ResponsiveSettingsContent>
<ResponsiveSettingsControl>
{!isMfaEnabled ? ( {!isMfaEnabled ? (
<Tooltip <Tooltip
label={t("Available in enterprise edition")} label={t("Available in enterprise edition")}
@ -100,7 +102,8 @@ export function MfaSettings() {
</Button> </Button>
</Group> </Group>
)} )}
</Group> </ResponsiveSettingsControl>
</ResponsiveSettingsRow>
<MfaSetupModal <MfaSetupModal
opened={setupModalOpen} opened={setupModalOpen}

View File

@ -55,9 +55,11 @@ export default function AllowedDomains() {
return ( return (
<> <>
<div> <div>
<Text size="md">Allowed email domains</Text> <Text size="md">{t("Allowed email domains")}</Text>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
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.",
)}
</Text> </Text>
</div> </div>
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { z } from "zod"; 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 { Box, Button, Group, Stack, Switch, TextInput } from "@mantine/core";
import classes from "@/ee/security/components/sso.module.css"; import classes from "@/ee/security/components/sso.module.css";
import { IAuthProvider } from "@/ee/security/types/security.types.ts"; import { IAuthProvider } from "@/ee/security/types/security.types.ts";

View File

@ -110,7 +110,7 @@ export function SsoLDAPForm({ provider, onClose }: SsoFormProps) {
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>
<Stack> <Stack>
<TextInput <TextInput
label="Display name" label={t("Display name")}
placeholder="e.g Company LDAP" placeholder="e.g Company LDAP"
data-autofocus data-autofocus
{...form.getInputProps("name")} {...form.getInputProps("name")}
@ -155,7 +155,7 @@ export function SsoLDAPForm({ provider, onClose }: SsoFormProps) {
<Accordion variant="separated"> <Accordion variant="separated">
<Accordion.Item value="advanced"> <Accordion.Item value="advanced">
<Accordion.Control icon={<IconInfoCircle size={20} />}> <Accordion.Control icon={<IconInfoCircle size={20} />}>
Advanced Settings {t("Advanced Settings")}
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<Stack> <Stack>

View File

@ -83,7 +83,7 @@ export function SsoOIDCForm({ provider, onClose }: SsoFormProps) {
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>
<Stack> <Stack>
<TextInput <TextInput
label="Display name" label={t("Display name")}
placeholder="e.g Google SSO" placeholder="e.g Google SSO"
data-autofocus data-autofocus
{...form.getInputProps("name")} {...form.getInputProps("name")}

View File

@ -69,7 +69,7 @@ export default function SsoProviderList() {
return ( return (
<> <>
<Card shadow="sm" radius="sm"> <Card shadow="sm" radius="sm">
<Table.ScrollContainer minWidth={500}> <Table.ScrollContainer minWidth={600}>
<Table verticalSpacing="sm"> <Table verticalSpacing="sm">
<Table.Thead> <Table.Thead>
<Table.Tr> <Table.Tr>
@ -104,7 +104,7 @@ export default function SsoProviderList() {
</Group> </Group>
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Badge color={"gray"} variant="light"> <Badge color={"gray"} variant="light" style={{ whiteSpace: "nowrap" }}>
{provider.type.toUpperCase()} {provider.type.toUpperCase()}
</Badge> </Badge>
</Table.Td> </Table.Td>
@ -133,6 +133,7 @@ export default function SsoProviderList() {
)} )}
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Group gap="xs" wrap="nowrap">
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
color="gray" color="gray"
@ -168,6 +169,7 @@ export default function SsoProviderList() {
</Menu.Item> </Menu.Item>
</Menu.Dropdown> </Menu.Dropdown>
</Menu> </Menu>
</Group>
</Table.Td> </Table.Td>
</Table.Tr> </Table.Tr>
))} ))}

View File

@ -6,6 +6,7 @@ import { SSO_PROVIDER } from "@/ee/security/contants.ts";
import { SsoOIDCForm } from "@/ee/security/components/sso-oidc-form.tsx"; import { SsoOIDCForm } from "@/ee/security/components/sso-oidc-form.tsx";
import { SsoGoogleForm } from "@/ee/security/components/sso-google-form.tsx"; import { SsoGoogleForm } from "@/ee/security/components/sso-google-form.tsx";
import { SsoLDAPForm } from "@/ee/security/components/sso-ldap-form.tsx"; import { SsoLDAPForm } from "@/ee/security/components/sso-ldap-form.tsx";
import { useTranslation } from "react-i18next";
interface SsoModalProps { interface SsoModalProps {
opened: boolean; opened: boolean;
@ -18,6 +19,8 @@ export default function SsoProviderModal({
onClose, onClose,
provider, provider,
}: SsoModalProps) { }: SsoModalProps) {
const { t } = useTranslation();
if (!provider) { if (!provider) {
return null; return null;
} }
@ -25,7 +28,9 @@ export default function SsoProviderModal({
return ( return (
<Modal <Modal
opened={opened} opened={opened}
title={`${provider.type.toUpperCase()} Configuration`} title={t("{{ssoProviderType}} configuration", {
ssoProviderType: provider.type.toUpperCase(),
})}
onClose={onClose} onClose={onClose}
> >
{provider.type === SSO_PROVIDER.SAML && ( {provider.type === SSO_PROVIDER.SAML && (

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { z } from "zod"; import { z } from "zod";
import { useForm, zodResolver } from "@mantine/form"; import { useForm } from "@mantine/form";
import { zodResolver } from "mantine-form-zod-resolver";
import { import {
Box, Box,
Button, Button,
@ -91,7 +92,7 @@ export function SsoSamlForm({ provider, onClose }: SsoFormProps) {
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>
<Stack> <Stack>
<TextInput <TextInput
label="Display name" label={t("Display name")}
placeholder="e.g Azure Entra" placeholder="e.g Azure Entra"
data-autofocus data-autofocus
{...form.getInputProps("name")} {...form.getInputProps("name")}

View File

@ -164,7 +164,6 @@ export function CalloutMenu({ editor }: EditorMenuProps) {
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip position="top" label={t("Custom emoji")}>
<EmojiPicker <EmojiPicker
onEmojiSelect={setCalloutIcon} onEmojiSelect={setCalloutIcon}
removeEmojiAction={removeCalloutIcon} removeEmojiAction={removeCalloutIcon}
@ -173,10 +172,9 @@ export function CalloutMenu({ editor }: EditorMenuProps) {
actionIconProps={{ actionIconProps={{
size: "lg", size: "lg",
variant: "default", variant: "default",
c: undefined c: undefined,
}} }}
/> />
</Tooltip>
</ActionIcon.Group> </ActionIcon.Group>
</BaseBubbleMenu> </BaseBubbleMenu>
); );

View File

@ -145,6 +145,7 @@ export default function DrawioView(props: NodeViewProps) {
variant="default" variant="default"
color="gray" color="gray"
mx="xs" mx="xs"
className="print-hide"
style={{ style={{
position: "absolute", position: "absolute",
top: 8, top: 8,

View File

@ -183,6 +183,7 @@ export default function ExcalidrawView(props: NodeViewProps) {
variant="default" variant="default"
color="gray" color="gray"
mx="xs" mx="xs"
className="print-hide"
style={{ style={{
position: "absolute", position: "absolute",
top: 8, top: 8,

View File

@ -33,8 +33,8 @@
} }
p { p {
margin-top: 0.65em; margin-top: 0.5em;
margin-bottom: 0.65em; margin-bottom: 0.5em;
} }
ul, ul,
@ -57,6 +57,7 @@
h5, h5,
h6 { h6 {
line-height: 1.1; line-height: 1.1;
margin-bottom: 10px;
} }
code { code {
@ -136,7 +137,7 @@
.selection, .selection,
*::selection { *::selection {
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-gray-7)); background-color: Highlight;
} }
.comment-mark { .comment-mark {

View File

@ -1,11 +1,23 @@
@media print { @media print {
.mantine-AppShell-header, .mantine-AppShell-header,
.mantine-AppShell-navbar, .mantine-AppShell-navbar,
.mantine-AppShell-aside{ .mantine-AppShell-aside,
.print-hide,
.drag-handle {
display: none !important; display: none !important;
} }
.mantine-AppShell-main { .mantine-AppShell-main {
padding-top: 0 !important; padding-top: 0 !important;
min-height: auto !important;
}
.ProseMirror-selectednode {
outline: none !important;
box-shadow: none !important;
}
.tableWrapper {
overflow: hidden !important;
} }
} }

View File

@ -10,6 +10,7 @@ ul[data-type="taskList"] {
display: flex; display: flex;
> label { > label {
padding-top: 0.2rem;
flex: 0 0 auto; flex: 0 0 auto;
margin-right: 0.5rem; margin-right: 0.5rem;
user-select: none; user-select: none;

View File

@ -1,5 +1,13 @@
import React from "react"; 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 { Spotlight } from "@mantine/spotlight";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { IconFile, IconDownload } from "@tabler/icons-react"; import { IconFile, IconDownload } from "@tabler/icons-react";
@ -10,6 +18,7 @@ import {
IPageSearch, IPageSearch,
} from "@/features/search/types/search.types"; } from "@/features/search/types/search.types";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import { useTranslation } from "react-i18next";
interface SearchResultItemProps { interface SearchResultItemProps {
result: IPageSearch | IAttachmentSearch; result: IPageSearch | IAttachmentSearch;
@ -22,6 +31,8 @@ export function SearchResultItem({
isAttachmentResult, isAttachmentResult,
showSpace, showSpace,
}: SearchResultItemProps) { }: SearchResultItemProps) {
const { t } = useTranslation();
if (isAttachmentResult) { if (isAttachmentResult) {
const attachmentResult = result as IAttachmentSearch; const attachmentResult = result as IAttachmentSearch;
@ -45,7 +56,7 @@ export function SearchResultItem({
> >
<Group wrap="nowrap" w="100%"> <Group wrap="nowrap" w="100%">
<Center> <Center>
<IconFile size={20} /> <IconFile size={16} />
</Center> </Center>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
@ -61,21 +72,22 @@ export function SearchResultItem({
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(attachmentResult.highlight, { __html: DOMPurify.sanitize(attachmentResult.highlight, {
ALLOWED_TAGS: ["mark", "em", "strong", "b"], ALLOWED_TAGS: ["mark", "em", "strong", "b"],
ALLOWED_ATTR: [] ALLOWED_ATTR: [],
}), }),
}} }}
/> />
)} )}
</div> </div>
<ActionIcon <Tooltip
variant="subtle" label={t("Download attachment")}
color="gray" zIndex={getDefaultZIndex("max")}
onClick={handleDownload} withArrow
title="Download attachment"
> >
<ActionIcon variant="subtle" color="gray" onClick={handleDownload}>
<IconDownload size={18} /> <IconDownload size={18} />
</ActionIcon> </ActionIcon>
</Tooltip>
</Group> </Group>
</Spotlight.Action> </Spotlight.Action>
); );
@ -111,7 +123,7 @@ export function SearchResultItem({
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(pageResult.highlight, { __html: DOMPurify.sanitize(pageResult.highlight, {
ALLOWED_TAGS: ["mark", "em", "strong", "b"], ALLOWED_TAGS: ["mark", "em", "strong", "b"],
ALLOWED_ATTR: [] ALLOWED_ATTR: [],
}), }),
}} }}
/> />

View File

@ -25,6 +25,7 @@ import { useDebouncedValue } from "@mantine/hooks";
import { useGetSpacesQuery } from "@/features/space/queries/space-query"; import { useGetSpacesQuery } from "@/features/space/queries/space-query";
import { useLicense } from "@/ee/hooks/use-license"; import { useLicense } from "@/ee/hooks/use-license";
import classes from "./search-spotlight-filters.module.css"; import classes from "./search-spotlight-filters.module.css";
import { isCloud } from "@/lib/config.ts";
interface SearchSpotlightFiltersProps { interface SearchSpotlightFiltersProps {
onFiltersChange?: (filters: any) => void; onFiltersChange?: (filters: any) => void;
@ -81,11 +82,11 @@ export function SearchSpotlightFilters({
}, []); }, []);
const contentTypeOptions = [ const contentTypeOptions = [
{ value: "page", label: "Pages" }, { value: "page", label: t("Pages") },
{ {
value: "attachment", value: "attachment",
label: "Attachments", label: t("Attachments"),
disabled: !hasLicenseKey, disabled: !isCloud() && !hasLicenseKey,
}, },
]; ];
@ -165,13 +166,13 @@ export function SearchSpotlightFilters({
fw={500} fw={500}
> >
{selectedSpaceId {selectedSpaceId
? `Space: ${selectedSpaceData?.name || "Unknown"}` ? `${t("Space")}: ${selectedSpaceData?.name || t("Unknown")}`
: "Space: All spaces"} : `${t("Space")}: ${t("All spaces")}`}
</Button> </Button>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
<TextInput <TextInput
placeholder="Find a space" placeholder={t("Find a space")}
data-autofocus data-autofocus
autoFocus autoFocus
leftSection={<IconSearch size={16} />} leftSection={<IconSearch size={16} />}
@ -189,15 +190,15 @@ export function SearchSpotlightFilters({
<Avatar <Avatar
color="initials" color="initials"
variant="filled" variant="filled"
name="All spaces" name={t("All spaces")}
size={20} size={20}
/> />
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<Text size="sm" fw={500}> <Text size="sm" fw={500}>
All spaces {t("All spaces")}
</Text> </Text>
<Text size="xs" c="dimmed"> <Text size="xs" c="dimmed">
Search in all your spaces {t("Search in all your spaces")}
</Text> </Text>
</div> </div>
{!selectedSpaceId && <IconCheck size={20} />} {!selectedSpaceId && <IconCheck size={20} />}
@ -246,8 +247,8 @@ export function SearchSpotlightFilters({
fw={500} fw={500}
> >
{contentType {contentType
? `Type: ${contentTypeOptions.find((opt) => opt.value === contentType)?.label || contentType}` ? `${t("Type")}: ${contentTypeOptions.find((opt) => opt.value === contentType)?.label || t(contentType === "page" ? "Pages" : "Attachments")}`
: "Type"} : t("Type")}
</Button> </Button>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
@ -266,7 +267,7 @@ export function SearchSpotlightFilters({
<Text size="sm">{option.label}</Text> <Text size="sm">{option.label}</Text>
{option.disabled && ( {option.disabled && (
<Badge size="xs" mt={4}> <Badge size="xs" mt={4}>
Enterprise {t("Enterprise")}
</Badge> </Badge>
)} )}
</div> </div>

View File

@ -9,6 +9,7 @@ import {
IPageSearchParams, IPageSearchParams,
} from "@/features/search/types/search.types"; } from "@/features/search/types/search.types";
import { useLicense } from "@/ee/hooks/use-license"; import { useLicense } from "@/ee/hooks/use-license";
import { isCloud } from "@/lib/config";
export type UnifiedSearchResult = IPageSearch | IAttachmentSearch; export type UnifiedSearchResult = IPageSearch | IAttachmentSearch;
@ -23,7 +24,7 @@ export function useUnifiedSearch(
const { hasLicenseKey } = useLicense(); const { hasLicenseKey } = useLicense();
const isAttachmentSearch = const isAttachmentSearch =
params.contentType === "attachment" && hasLicenseKey; params.contentType === "attachment" && (isCloud() || hasLicenseKey);
const searchType = isAttachmentSearch ? "attachment" : "page"; const searchType = isAttachmentSearch ? "attachment" : "page";
return useQuery({ return useQuery({

View File

@ -1,25 +1,28 @@
import { Group, Text, MantineSize, SegmentedControl } from "@mantine/core"; import { Text, MantineSize, SegmentedControl } from "@mantine/core";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
import { updateUser } from "@/features/user/services/user-service.ts"; import { updateUser } from "@/features/user/services/user-service.ts";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { PageEditMode } from "@/features/user/types/user.types.ts"; import { PageEditMode } from "@/features/user/types/user.types.ts";
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
export default function PageStatePref() { export default function PageStatePref() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Group justify="space-between" wrap="nowrap" gap="xl"> <ResponsiveSettingsRow>
<div> <ResponsiveSettingsContent>
<Text size="md">{t("Default page edit mode")}</Text> <Text size="md">{t("Default page edit mode")}</Text>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{t("Choose your preferred page edit mode. Avoid accidental edits.")} {t("Choose your preferred page edit mode. Avoid accidental edits.")}
</Text> </Text>
</div> </ResponsiveSettingsContent>
<ResponsiveSettingsControl>
<PageStateSegmentedControl /> <PageStateSegmentedControl />
</Group> </ResponsiveSettingsControl>
</ResponsiveSettingsRow>
); );
} }

View File

@ -1,24 +1,27 @@
import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
import { updateUser } from "@/features/user/services/user-service.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 { useAtom } from "jotai/index";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
export default function PageWidthPref() { export default function PageWidthPref() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Group justify="space-between" wrap="nowrap" gap="xl"> <ResponsiveSettingsRow>
<div> <ResponsiveSettingsContent>
<Text size="md">{t("Full page width")}</Text> <Text size="md">{t("Full page width")}</Text>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{t("Choose your preferred page width.")} {t("Choose your preferred page width.")}
</Text> </Text>
</div> </ResponsiveSettingsContent>
<ResponsiveSettingsControl>
<PageWidthToggle /> <PageWidthToggle />
</Group> </ResponsiveSettingsControl>
</ResponsiveSettingsRow>
); );
} }

View File

@ -26,7 +26,7 @@ export default function WorkspaceInvitesTable() {
)} )}
</Alert> </Alert>
<Table.ScrollContainer minWidth={500}> <Table.ScrollContainer minWidth={600}>
<Table highlightOnHover verticalSpacing="sm"> <Table highlightOnHover verticalSpacing="sm">
<Table.Thead> <Table.Thead>
<Table.Tr> <Table.Tr>

View File

@ -4,7 +4,7 @@ import {
useWorkspaceMembersQuery, useWorkspaceMembersQuery,
} from "@/features/workspace/queries/workspace-query.ts"; } from "@/features/workspace/queries/workspace-query.ts";
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; 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 RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
import { import {
getUserRoleLabel, getUserRoleLabel,
@ -54,7 +54,7 @@ export default function WorkspaceMembersTable() {
return ( return (
<> <>
<SearchInput onSearch={handleSearch} /> <SearchInput onSearch={handleSearch} />
<Table.ScrollContainer minWidth={500}> <Table.ScrollContainer minWidth={600}>
<Table highlightOnHover verticalSpacing="sm"> <Table highlightOnHover verticalSpacing="sm">
<Table.Thead> <Table.Thead>
<Table.Tr> <Table.Tr>

View File

@ -1,6 +1,6 @@
{ {
"name": "server", "name": "server",
"version": "0.22.2", "version": "0.23.0",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,

View File

@ -37,7 +37,7 @@ export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy {
// eslint-disable-next-line @typescript-eslint/no-require-imports // eslint-disable-next-line @typescript-eslint/no-require-imports
AttachmentEeModule = require('./../../../ee/attachments-ee/attachment-ee.service'); AttachmentEeModule = require('./../../../ee/attachments-ee/attachment-ee.service');
} catch (err) { } catch (err) {
this.logger.error( this.logger.debug(
'Attachment enterprise module requested but EE module not bundled in this build', 'Attachment enterprise module requested but EE module not bundled in this build',
); );
return; return;
@ -67,10 +67,16 @@ export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy {
@OnWorkerEvent('failed') @OnWorkerEvent('failed')
onError(job: Job) { onError(job: Job) {
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( this.logger.error(
`Error processing ${job.name} job. Reason: ${job.failedReason}`, `Error processing ${job.name} job. Reason: ${job.failedReason}`,
); );
} }
}
@OnWorkerEvent('completed') @OnWorkerEvent('completed')
onCompleted(job: Job) { onCompleted(job: Job) {

View File

@ -6,6 +6,7 @@ import { cleanUrlString } from '../utils/file.utils';
import { StorageService } from '../../storage/storage.service'; import { StorageService } from '../../storage/storage.service';
import { createReadStream } from 'node:fs'; import { createReadStream } from 'node:fs';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { Readable } from 'stream';
import { getMimeType, sanitizeFileName } from '../../../common/helpers'; import { getMimeType, sanitizeFileName } from '../../../common/helpers';
import { v7 } from 'uuid'; import { v7 } from 'uuid';
import { FileTask } from '@docmost/db/types/entity.types'; 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 { resolveRelativeAttachmentPath } from '../utils/import.utils';
import { load } from 'cheerio'; import { load } from 'cheerio';
import pLimit from 'p-limit'; 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() @Injectable()
export class ImportAttachmentService { export class ImportAttachmentService {
@ -26,6 +42,7 @@ export class ImportAttachmentService {
constructor( constructor(
private readonly storageService: StorageService, private readonly storageService: StorageService,
@InjectKysely() private readonly db: KyselyDB, @InjectKysely() private readonly db: KyselyDB,
@InjectQueue(QueueName.ATTACHMENT_QUEUE) private attachmentQueue: Queue,
) {} ) {}
async processAttachments(opts: { async processAttachments(opts: {
@ -35,6 +52,7 @@ export class ImportAttachmentService {
pageId: string; pageId: string;
fileTask: FileTask; fileTask: FileTask;
attachmentCandidates: Map<string, string>; attachmentCandidates: Map<string, string>;
pageAttachments?: AttachmentInfo[];
}): Promise<string> { }): Promise<string> {
const { const {
html, html,
@ -43,6 +61,7 @@ export class ImportAttachmentService {
pageId, pageId,
fileTask, fileTask,
attachmentCandidates, attachmentCandidates,
pageAttachments = [],
} = opts; } = opts;
const attachmentTasks: (() => Promise<void>)[] = []; const attachmentTasks: (() => Promise<void>)[] = [];
@ -57,7 +76,7 @@ export class ImportAttachmentService {
/** /**
* Cache keyed by the *relative* path that appears in the HTML. * Cache keyed by the *relative* path that appears in the HTML.
* Ensures we upload (and DB-insert) each attachment at most once, * Ensures we upload (and DB-insert) each attachment at most once,
* even if its referenced multiple times on the page. * even if it's referenced multiple times on the page.
*/ */
const processed = new Map< const processed = new Map<
string, 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 uploadOnce = (relPath: string) => {
const abs = attachmentCandidates.get(relPath)!; const abs = attachmentCandidates.get(relPath)!;
const attachmentId = v7(); const attachmentId = v7();
@ -85,7 +197,8 @@ export class ImportAttachmentService {
const apiFilePath = `/api/files/${attachmentId}/${fileNameWithExt}`; const apiFilePath = `/api/files/${attachmentId}/${fileNameWithExt}`;
attachmentTasks.push(() => this.uploadWithRetry({ attachmentTasks.push(() =>
this.uploadWithRetry({
abs, abs,
storageFilePath, storageFilePath,
attachmentId, attachmentId,
@ -94,7 +207,8 @@ export class ImportAttachmentService {
pageId, pageId,
fileTask, fileTask,
uploadStats, uploadStats,
})); }),
);
return { return {
attachmentId, attachmentId,
@ -121,79 +235,108 @@ export class ImportAttachmentService {
const pageDir = path.dirname(pageRelativePath); const pageDir = path.dirname(pageRelativePath);
const $ = load(html); const $ = load(html);
// image // Cache for resolved paths to avoid repeated lookups
for (const imgEl of $('img').toArray()) { const resolvedPathCache = new Map<string, string | null>();
const $img = $(imgEl);
const src = cleanUrlString($img.attr('src') ?? '')!;
if (!src || src.startsWith('http')) continue;
const relPath = resolveRelativeAttachmentPath( const getCachedResolvedPath = (rawPath: string): string | null => {
src, if (resolvedPathCache.has(rawPath)) {
return resolvedPathCache.get(rawPath)!;
}
const resolved = resolveRelativeAttachmentPath(
rawPath,
pageDir, pageDir,
attachmentCandidates, attachmentCandidates,
); );
if (!relPath) continue; resolvedPathCache.set(rawPath, resolved);
return resolved;
};
const { attachmentId, apiFilePath, abs } = processFile(relPath); // Cache for file stats to avoid repeated file system calls
const stat = await fs.stat(abs); const statCache = new Map<string, any>();
const width = $img.attr('width') ?? '100%'; const getCachedStat = async (absPath: string) => {
const align = $img.attr('data-align') ?? 'center'; if (statCache.has(absPath)) {
return statCache.get(absPath);
$img
.attr('src', apiFilePath)
.attr('data-attachment-id', attachmentId)
.attr('data-size', stat.size.toString())
.attr('width', width)
.attr('data-align', align);
unwrapFromParagraph($, $img);
} }
const stat = await fs.stat(absPath);
statCache.set(absPath, stat);
return stat;
};
// video // Single DOM traversal for all attachment elements
for (const vidEl of $('video').toArray()) { const selector =
const $vid = $(vidEl); 'img, video, div[data-type="attachment"], a, div[data-type="excalidraw"], div[data-type="drawio"]';
const src = cleanUrlString($vid.attr('src') ?? '')!; const elements = $(selector).toArray();
for (const element of elements) {
const $el = $(element);
const tagName = element.tagName.toLowerCase();
// Process based on element type
if (tagName === 'img') {
const src = cleanUrlString($el.attr('src') ?? '');
if (!src || src.startsWith('http')) continue; if (!src || src.startsWith('http')) continue;
const relPath = resolveRelativeAttachmentPath( const relPath = getCachedResolvedPath(src);
src,
pageDir,
attachmentCandidates,
);
if (!relPath) continue; if (!relPath) continue;
// Check if this image is part of a Draw.io pair
const drawioSvg = drawioSvgMap.get(relPath);
if (drawioSvg) {
const $drawio = $('<div>')
.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);
$el.replaceWith($drawio);
unwrapFromParagraph($, $drawio);
continue;
}
const { attachmentId, apiFilePath, abs } = processFile(relPath); const { attachmentId, apiFilePath, abs } = processFile(relPath);
const stat = await fs.stat(abs); const stat = await getCachedStat(abs);
const width = $vid.attr('width') ?? '100%'; $el
const align = $vid.attr('data-align') ?? 'center';
$vid
.attr('src', apiFilePath) .attr('src', apiFilePath)
.attr('data-attachment-id', attachmentId) .attr('data-attachment-id', attachmentId)
.attr('data-size', stat.size.toString()) .attr('data-size', stat.size.toString())
.attr('width', width) .attr('width', $el.attr('width') ?? '100%')
.attr('data-align', align); .attr('data-align', $el.attr('data-align') ?? 'center');
unwrapFromParagraph($, $vid); unwrapFromParagraph($, $el);
} } else if (tagName === 'video') {
const src = cleanUrlString($el.attr('src') ?? '');
if (!src || src.startsWith('http')) continue;
// <div data-type="attachment"> const relPath = getCachedResolvedPath(src);
for (const el of $('div[data-type="attachment"]').toArray()) { if (!relPath) continue;
const $oldDiv = $(el);
const rawUrl = cleanUrlString($oldDiv.attr('data-attachment-url') ?? '')!; const { attachmentId, apiFilePath, abs } = processFile(relPath);
const stat = await getCachedStat(abs);
$el
.attr('src', apiFilePath)
.attr('data-attachment-id', attachmentId)
.attr('data-size', stat.size.toString())
.attr('width', $el.attr('width') ?? '100%')
.attr('data-align', $el.attr('data-align') ?? 'center');
unwrapFromParagraph($, $el);
} else if (tagName === 'div') {
const dataType = $el.attr('data-type');
if (dataType === 'attachment') {
const rawUrl = cleanUrlString($el.attr('data-attachment-url') ?? '');
if (!rawUrl || rawUrl.startsWith('http')) continue; if (!rawUrl || rawUrl.startsWith('http')) continue;
const relPath = resolveRelativeAttachmentPath( const relPath = getCachedResolvedPath(rawUrl);
rawUrl,
pageDir,
attachmentCandidates,
);
if (!relPath) continue; if (!relPath) continue;
const { attachmentId, apiFilePath, abs } = processFile(relPath); const { attachmentId, apiFilePath, abs } = processFile(relPath);
const stat = await fs.stat(abs); const stat = await getCachedStat(abs);
const fileName = path.basename(abs); const fileName = path.basename(abs);
const mime = getMimeType(abs); const mime = getMimeType(abs);
@ -205,25 +348,62 @@ export class ImportAttachmentService {
.attr('data-attachment-size', stat.size.toString()) .attr('data-attachment-size', stat.size.toString())
.attr('data-attachment-id', attachmentId); .attr('data-attachment-id', attachmentId);
$oldDiv.replaceWith($newDiv); $el.replaceWith($newDiv);
unwrapFromParagraph($, $newDiv); unwrapFromParagraph($, $newDiv);
} } else if (dataType === 'excalidraw' || dataType === 'drawio') {
const rawSrc = cleanUrlString($el.attr('data-src') ?? '');
if (!rawSrc || rawSrc.startsWith('http')) continue;
// rewrite other attachments via <a> const relPath = getCachedResolvedPath(rawSrc);
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; if (!relPath) continue;
const { attachmentId, apiFilePath, abs } = processFile(relPath); const { attachmentId, apiFilePath, abs } = processFile(relPath);
const stat = await fs.stat(abs); const stat = await getCachedStat(abs);
const fileName = path.basename(abs);
const $newDiv = $('<div>')
.attr('data-type', dataType)
.attr('data-src', apiFilePath)
.attr('data-title', fileName)
.attr('data-width', $el.attr('data-width') || '100%')
.attr('data-size', stat.size.toString())
.attr('data-align', $el.attr('data-align') || 'center')
.attr('data-attachment-id', attachmentId);
$el.replaceWith($newDiv);
unwrapFromParagraph($, $newDiv);
}
} else if (tagName === 'a') {
const href = cleanUrlString($el.attr('href') ?? '');
if (!href || href.startsWith('http')) continue;
const relPath = getCachedResolvedPath(href);
if (!relPath) continue;
// Check if this is a Draw.io file
const drawioSvg = drawioSvgMap.get(relPath);
if (drawioSvg) {
const $drawio = $('<div>')
.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);
$el.replaceWith($drawio);
unwrapFromParagraph($, $drawio);
continue;
}
// Skip files that should be ignored
if (skipFiles.has(relPath)) {
$el.remove();
continue;
}
const { attachmentId, apiFilePath, abs } = processFile(relPath);
const stat = await getCachedStat(abs);
const ext = path.extname(relPath).toLowerCase(); const ext = path.extname(relPath).toLowerCase();
if (ext === '.mp4') { if (ext === '.mp4') {
@ -233,10 +413,10 @@ export class ImportAttachmentService {
.attr('data-size', stat.size.toString()) .attr('data-size', stat.size.toString())
.attr('width', '100%') .attr('width', '100%')
.attr('data-align', 'center'); .attr('data-align', 'center');
$a.replaceWith($video); $el.replaceWith($video);
unwrapFromParagraph($, $video); unwrapFromParagraph($, $video);
} else { } else {
const confAliasName = $a.attr('data-linked-resource-default-alias'); const confAliasName = $el.attr('data-linked-resource-default-alias');
let attachmentName = path.basename(abs); let attachmentName = path.basename(abs);
if (confAliasName) attachmentName = confAliasName; if (confAliasName) attachmentName = confAliasName;
@ -248,43 +428,87 @@ export class ImportAttachmentService {
.attr('data-attachment-size', stat.size.toString()) .attr('data-attachment-size', stat.size.toString())
.attr('data-attachment-id', attachmentId); .attr('data-attachment-id', attachmentId);
$a.replaceWith($div); $el.replaceWith($div);
unwrapFromParagraph($, $div); unwrapFromParagraph($, $div);
} }
} }
}
// excalidraw and drawio // Collect all attachment IDs in the HTML in a single DOM traversal - O(n)
for (const type of ['excalidraw', 'drawio'] as const) { const usedAttachmentIds = new Set<string>();
for (const el of $(`div[data-type="${type}"]`).toArray()) { $.root()
const $oldDiv = $(el); .find('[data-attachment-id]')
const rawSrc = cleanUrlString($oldDiv.attr('data-src') ?? '')!; .each((_, el) => {
if (!rawSrc || rawSrc.startsWith('http')) continue; const attachmentId = $(el).attr('data-attachment-id');
if (attachmentId) {
usedAttachmentIds.add(attachmentId);
}
});
const relPath = resolveRelativeAttachmentPath( // Add Draw.io diagrams that weren't referenced in the HTML content
rawSrc, for (const [drawioHref, pair] of drawioPairs) {
pageDir, const drawioSvg = drawioSvgMap.get(drawioHref);
attachmentCandidates, if (!drawioSvg) continue;
);
if (!relPath) continue;
const { attachmentId, apiFilePath, abs } = processFile(relPath); if (usedAttachmentIds.has(drawioSvg.attachmentId)) {
continue; // Already in content
}
const $drawio = $('<div>')
.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);
$.root().append($drawio);
}
// Process attachments from the attachment section that weren't referenced in HTML
// These need to be added as attachment nodes so they get uploaded
for (const attachment of pageAttachments) {
const { href, fileName, mimeType } = attachment;
// Skip temporary files or files that should be ignored
if (skipFiles.has(href)) {
continue;
}
// Check if this was part of a Draw.io pair that was already handled
if (drawioSvgMap.has(href)) {
continue;
}
// Check if already processed (was referenced in HTML)
if (processed.has(href)) {
continue;
}
// Skip if the file doesn't exist
if (!attachmentCandidates.has(href)) {
continue;
}
// This attachment was in the list but not referenced in HTML - add it
const { attachmentId, apiFilePath, abs } = processFile(href);
try {
const stat = await fs.stat(abs); const stat = await fs.stat(abs);
const fileName = path.basename(abs); const mime = mimeType || getMimeType(abs);
const width = $oldDiv.attr('data-width') || '100%'; // Add as attachment node at the end
const align = $oldDiv.attr('data-align') || 'center'; const $attachmentDiv = $('<div>')
.attr('data-type', 'attachment')
const $newDiv = $('<div>') .attr('data-attachment-url', apiFilePath)
.attr('data-type', type) .attr('data-attachment-name', fileName)
.attr('data-src', apiFilePath) .attr('data-attachment-mime', mime)
.attr('data-title', fileName) .attr('data-attachment-size', stat.size.toString())
.attr('data-width', width)
.attr('data-size', stat.size.toString())
.attr('data-align', align)
.attr('data-attachment-id', attachmentId); .attr('data-attachment-id', attachmentId);
$oldDiv.replaceWith($newDiv); $.root().append($attachmentDiv);
unwrapFromParagraph($, $newDiv); } catch (error) {
this.logger.error(`Failed to process attachment ${fileName}:`, error);
} }
} }
@ -292,24 +516,20 @@ export class ImportAttachmentService {
uploadStats.total = attachmentTasks.length; uploadStats.total = attachmentTasks.length;
if (uploadStats.total > 0) { if (uploadStats.total > 0) {
this.logger.debug(`Starting upload of ${uploadStats.total} attachments...`);
try { try {
await Promise.all( await Promise.all(attachmentTasks.map((task) => limit(task)));
attachmentTasks.map(task => limit(task))
);
} catch (err) { } catch (err) {
this.logger.error('Import attachment upload error', err); this.logger.error('Import attachment upload error', err);
} }
this.logger.debug( this.logger.debug(
`Upload completed: ${uploadStats.completed}/${uploadStats.total} successful, ${uploadStats.failed} failed` `Upload completed: ${uploadStats.completed}/${uploadStats.total} successful, ${uploadStats.failed} failed`,
); );
if (uploadStats.failed > 0) { if (uploadStats.failed > 0) {
this.logger.warn( this.logger.warn(
`Failed to upload ${uploadStats.failed} files:`, `Failed to upload ${uploadStats.failed} files:`,
uploadStats.failedFiles uploadStats.failedFiles,
); );
} }
} }
@ -317,6 +537,214 @@ export class ImportAttachmentService {
return $.root().html() || ''; return $.root().html() || '';
} }
private analyzeAttachments(attachments: AttachmentInfo[]): {
drawioPairs: Map<string, DrawioPair>;
skipFiles: Set<string>;
} {
const drawioPairs = new Map<string, DrawioPair>();
const skipFiles = new Set<string>();
// Group attachments by type
const drawioFiles: AttachmentInfo[] = [];
const pngByBaseName = new Map<string, AttachmentInfo[]>();
const nonDrawioExtensions = new Set([
'.png',
'.jpg',
'.jpeg',
'.gif',
'.svg',
'.txt',
'.pdf',
'.doc',
'.docx',
'.xls',
'.xlsx',
'.csv',
'.zip',
'.tar',
'.gz',
]);
// Single pass through attachments
for (const attachment of attachments) {
const { fileName, mimeType, href } = attachment;
const fileNameLower = fileName.toLowerCase();
// Skip temporary files
if (fileName.endsWith('.tmp') || fileName.includes('~drawio~')) {
skipFiles.add(href);
continue;
}
// Check for Draw.io files
if (mimeType === 'application/vnd.jgraph.mxfile') {
const ext = fileNameLower.substring(fileNameLower.lastIndexOf('.'));
if (!nonDrawioExtensions.has(ext)) {
drawioFiles.push(attachment);
} else {
//Skipped non-Draw.io file with mxfile MIME.
}
}
if (mimeType === 'image/png' || fileNameLower.endsWith('.png')) {
const baseNames: string[] = [];
if (fileName.endsWith('.drawio.png')) {
// Cloud format: "name.drawio.png" -> base is "name"
baseNames.push(fileName.slice(0, -11)); // Remove .drawio.png
} else if (fileName.endsWith('.png')) {
// Server format: "name.png" -> base is "name"
baseNames.push(fileName.slice(0, -4)); // Remove .png
}
for (const baseName of baseNames) {
if (!pngByBaseName.has(baseName)) {
pngByBaseName.set(baseName, []);
}
pngByBaseName.get(baseName)!.push(attachment);
}
}
}
// Match Draw.io files with PNG counterparts
for (const drawio of drawioFiles) {
let baseName: string;
if (drawio.fileName.endsWith('.drawio')) {
baseName = drawio.fileName.slice(0, -7); // Remove .drawio
} else {
// Confluence Server: no extension
baseName = drawio.fileName;
}
const candidatePngs = pngByBaseName.get(baseName) || [];
let matchingPng: AttachmentInfo | undefined;
// Extract the attachment ID from the Draw.io href
// Format: attachments/16941088/36044817.png -> ID is 36044817
const drawioIdMatch = drawio.href.match(/\/(\d+)\.\w+$/);
const drawioId = drawioIdMatch ? drawioIdMatch[1] : null;
if (drawioId) {
// Look for PNG with adjacent ID (usually PNG ID = Draw.io ID + small increment)
// In Confluence, related files often have sequential or near-sequential IDs
for (const png of candidatePngs) {
const pngIdMatch = png.href.match(/\/(\d+)\.png$/);
const pngId = pngIdMatch ? pngIdMatch[1] : null;
//TODO: should revisit this
// but seem to be the best option for now
// to prevent reusing the first drawio preview image if there are more with the same name
if (pngId && drawioId) {
const idDiff = Math.abs(parseInt(pngId) - parseInt(drawioId));
// PNG is usually within ~30 IDs of the Draw.io file
if (idDiff <= 30) {
// Verify filename match
if (
png.fileName === `${baseName}.drawio.png` ||
(!drawio.fileName.endsWith('.drawio') &&
png.fileName === `${baseName}.png`)
) {
matchingPng = png;
break;
}
}
}
}
}
// Fallback to name-only matching if ID-based matching fails
if (!matchingPng) {
for (const png of candidatePngs) {
if (png.fileName === `${baseName}.drawio.png`) {
matchingPng = png;
break;
}
if (
!drawio.fileName.endsWith('.drawio') &&
png.fileName === `${baseName}.png`
) {
matchingPng = png;
break;
}
}
}
if (matchingPng) {
this.logger.debug(
`Found Draw.io pair: ${drawio.fileName} -> ${matchingPng.fileName}`,
);
} else {
this.logger.debug(`No PNG found for Draw.io file: ${drawio.fileName}`);
}
const pair: DrawioPair = {
drawioFile: drawio,
pngFile: matchingPng,
baseName,
};
drawioPairs.set(drawio.href, pair);
skipFiles.add(drawio.href);
if (matchingPng) {
skipFiles.add(matchingPng.href);
// Remove the matched PNG from the candidates to prevent reuse
const remainingPngs = pngByBaseName
.get(baseName)
?.filter((png) => png.href !== matchingPng.href);
if (remainingPngs && remainingPngs.length > 0) {
pngByBaseName.set(baseName, remainingPngs);
} else {
pngByBaseName.delete(baseName);
}
}
}
return { drawioPairs, skipFiles };
}
private async createDrawioSvg(
drawioPath: string,
pngPath?: string,
): Promise<Buffer> {
try {
const drawioContent = await fs.readFile(drawioPath, 'utf-8');
const drawioBase64 = Buffer.from(drawioContent).toString('base64');
let imageElement = '';
// If we have a PNG, include it in the SVG
if (pngPath) {
try {
const pngBuffer = await fs.readFile(pngPath);
const pngBase64 = pngBuffer.toString('base64');
imageElement = `<image href="data:image/png;base64,${pngBase64}" width="100%" height="100%"/>`;
} catch (error) {
this.logger.warn(
`Could not read PNG file for Draw.io diagram: ${pngPath}`,
error,
);
}
}
// Create the SVG with embedded Draw.io data and image
// Default dimensions for Draw.io diagrams if no image is provided
const svgContent = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600"
height="400"
viewBox="0 0 600 400"
content="${drawioBase64}">${imageElement}</svg>`;
return Buffer.from(svgContent, 'utf-8');
} catch (error) {
this.logger.error(`Failed to create Draw.io SVG: ${error}`);
throw error;
}
}
private async uploadWithRetry(opts: { private async uploadWithRetry(opts: {
abs: string; abs: string;
storageFilePath: string; storageFilePath: string;
@ -368,11 +796,41 @@ export class ImportAttachmentService {
}) })
.execute(); .execute();
// Queue PDF and DOCX files for indexing
const supportedExtensions = ['.pdf', '.docx'];
if (supportedExtensions.includes(ext.toLowerCase())) {
try {
await this.attachmentQueue.add(
QueueJob.ATTACHMENT_INDEX_CONTENT,
{ attachmentId },
{
attempts: 1,
backoff: {
type: 'exponential',
delay: 30 * 1000,
},
deduplication: {
id: attachmentId,
},
removeOnComplete: true,
removeOnFail: false,
},
);
this.logger.debug(
`Queued ${fileNameWithExt} for indexing (attachment ID: ${attachmentId})`,
);
} catch (err) {
this.logger.error(
`Failed to queue indexing for imported attachment ${attachmentId}: ${err}`,
);
}
}
uploadStats.completed++; uploadStats.completed++;
if (uploadStats.completed % 10 === 0) { if (uploadStats.completed % 10 === 0) {
this.logger.debug( this.logger.debug(
`Upload progress: ${uploadStats.completed}/${uploadStats.total}` `Upload progress: ${uploadStats.completed}/${uploadStats.total}`,
); );
} }
@ -380,12 +838,12 @@ export class ImportAttachmentService {
} catch (error) { } catch (error) {
lastError = error as Error; lastError = error as Error;
this.logger.warn( this.logger.warn(
`Upload attempt ${attempt}/${this.MAX_RETRIES} failed for ${fileNameWithExt}: ${error instanceof Error ? error.message : String(error)}` `Upload attempt ${attempt}/${this.MAX_RETRIES} failed for ${fileNameWithExt}: ${error instanceof Error ? error.message : String(error)}`,
); );
if (attempt < this.MAX_RETRIES) { if (attempt < this.MAX_RETRIES) {
await new Promise(resolve => await new Promise((resolve) =>
setTimeout(resolve, this.RETRY_DELAY * attempt) setTimeout(resolve, this.RETRY_DELAY * attempt),
); );
} }
} }
@ -395,7 +853,7 @@ export class ImportAttachmentService {
uploadStats.failedFiles.push(fileNameWithExt); uploadStats.failedFiles.push(fileNameWithExt);
this.logger.error( this.logger.error(
`Failed to upload ${fileNameWithExt} after ${this.MAX_RETRIES} attempts:`, `Failed to upload ${fileNameWithExt} after ${this.MAX_RETRIES} attempts:`,
lastError lastError,
); );
} }
} }

View File

@ -1,5 +1,3 @@
version: '3'
services: services:
docmost: docmost:
image: docmost/docmost:latest image: docmost/docmost:latest

View File

@ -1,7 +1,7 @@
{ {
"name": "docmost", "name": "docmost",
"homepage": "https://docmost.com", "homepage": "https://docmost.com",
"version": "0.22.2", "version": "0.23.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "nx run-many -t build", "build": "nx run-many -t build",

156
pnpm-lock.yaml generated
View File

@ -306,8 +306,8 @@ importers:
specifier: ^1.3.0 specifier: ^1.3.0
version: 1.3.0(@mantine/form@8.1.3(react@18.3.1))(zod@3.25.56) version: 1.3.0(@mantine/form@8.1.3(react@18.3.1))(zod@3.25.56)
mermaid: mermaid:
specifier: ^11.6.0 specifier: ^11.11.0
version: 11.6.0 version: 11.11.0
mitt: mitt:
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.0.1 version: 3.0.1
@ -746,11 +746,11 @@ packages:
resolution: {integrity: sha512-AP6FvhMybCYs3gs+vzEAzSU1K//AFT3SVTRFv+C3WMO5dLeAHeGzM8I2dxD5EHQQtqIE/8apP6CxGrnpA5YlFg==} resolution: {integrity: sha512-AP6FvhMybCYs3gs+vzEAzSU1K//AFT3SVTRFv+C3WMO5dLeAHeGzM8I2dxD5EHQQtqIE/8apP6CxGrnpA5YlFg==}
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
'@antfu/install-pkg@0.4.1': '@antfu/install-pkg@1.1.0':
resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
'@antfu/utils@0.7.10': '@antfu/utils@9.2.0':
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==}
'@asamuzakjp/css-color@2.8.3': '@asamuzakjp/css-color@2.8.3':
resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==}
@ -2292,8 +2292,8 @@ packages:
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
'@iconify/utils@2.1.33': '@iconify/utils@3.0.1':
resolution: {integrity: sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==} resolution: {integrity: sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==}
'@inquirer/checkbox@4.1.2': '@inquirer/checkbox@4.1.2':
resolution: {integrity: sha512-PL9ixC5YsPXzXhAZFUPmkXGxfgjkdfZdPEPPmt4kFwQ4LBMDG9n/nHXYRGGZSKZJs+d1sGKWgS2GiPzVRKUdtQ==} resolution: {integrity: sha512-PL9ixC5YsPXzXhAZFUPmkXGxfgjkdfZdPEPPmt4kFwQ4LBMDG9n/nHXYRGGZSKZJs+d1sGKWgS2GiPzVRKUdtQ==}
@ -2614,8 +2614,8 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true hasBin: true
'@mermaid-js/parser@0.4.0': '@mermaid-js/parser@0.6.2':
resolution: {integrity: sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==} resolution: {integrity: sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==}
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2': '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2':
resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
@ -4786,6 +4786,11 @@ packages:
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
hasBin: true hasBin: true
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
hasBin: true
address@1.2.2: address@1.2.2:
resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
@ -5338,6 +5343,9 @@ packages:
confbox@0.1.8: confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
confbox@0.2.2:
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
config-chain@1.1.13: config-chain@1.1.13:
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
@ -5674,6 +5682,15 @@ packages:
supports-color: supports-color:
optional: true optional: true
debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decamelize@1.2.0: decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -6098,6 +6115,9 @@ packages:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
exsolve@1.0.7:
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
external-editor@3.1.0: external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -7236,8 +7256,8 @@ packages:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
engines: {node: '>=6.11.5'} engines: {node: '>=6.11.5'}
local-pkg@0.5.1: local-pkg@1.1.2:
resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
engines: {node: '>=14'} engines: {node: '>=14'}
locate-path@5.0.0: locate-path@5.0.0:
@ -7422,8 +7442,8 @@ packages:
mermaid@10.9.3: mermaid@10.9.3:
resolution: {integrity: sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==} resolution: {integrity: sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==}
mermaid@11.6.0: mermaid@11.11.0:
resolution: {integrity: sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==} resolution: {integrity: sha512-9lb/VNkZqWTRjVgCV+l1N+t4kyi94y+l5xrmBmbbxZYkfRl5hEDaTPMOcaWKCl1McG8nBEaMlWwkcAEEgjhBgg==}
methods@1.1.2: methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
@ -7577,8 +7597,8 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
mlly@1.7.3: mlly@1.8.0:
resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
mri@1.2.0: mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
@ -7912,8 +7932,8 @@ packages:
package-json-from-dist@1.0.0: package-json-from-dist@1.0.0:
resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
package-manager-detector@0.2.5: package-manager-detector@1.3.0:
resolution: {integrity: sha512-3dS7y28uua+UDbRCLBqltMBrbI+A5U2mI9YuxHRxIWYmLj3DwntEBmERYzIAQ4DMeuCUOBSak7dBHHoXKpOTYQ==} resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
pako@1.0.11: pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
@ -8001,8 +8021,8 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'} engines: {node: '>=8'}
pathe@1.1.2: pathe@2.0.3:
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pause@0.0.1: pause@0.0.1:
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
@ -8119,8 +8139,11 @@ packages:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
pkg-types@1.2.1: pkg-types@1.3.1:
resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
pluralize@8.0.0: pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
@ -8388,6 +8411,9 @@ packages:
resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -9133,8 +9159,8 @@ packages:
thread-stream@3.0.2: thread-stream@3.0.2:
resolution: {integrity: sha512-cBL4xF2A3lSINV4rD5tyqnKH4z/TgWPvT+NaVhJDSwK962oo/Ye7cHSMbDzwcu7tAE1SfU6Q4XtV6Hucmi6Hlw==} resolution: {integrity: sha512-cBL4xF2A3lSINV4rD5tyqnKH4z/TgWPvT+NaVhJDSwK962oo/Ye7cHSMbDzwcu7tAE1SfU6Q4XtV6Hucmi6Hlw==}
tinyexec@0.3.1: tinyexec@1.0.1:
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
tinyglobby@0.2.12: tinyglobby@0.2.12:
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
@ -9379,8 +9405,8 @@ packages:
uc.micro@2.1.0: uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
ufo@1.5.4: ufo@1.6.1:
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
uid2@0.0.4: uid2@0.0.4:
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
@ -9965,12 +9991,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- chokidar - chokidar
'@antfu/install-pkg@0.4.1': '@antfu/install-pkg@1.1.0':
dependencies: dependencies:
package-manager-detector: 0.2.5 package-manager-detector: 1.3.0
tinyexec: 0.3.1 tinyexec: 1.0.1
'@antfu/utils@0.7.10': {} '@antfu/utils@9.2.0': {}
'@asamuzakjp/css-color@2.8.3': '@asamuzakjp/css-color@2.8.3':
dependencies: dependencies:
@ -12190,15 +12216,16 @@ snapshots:
'@iconify/types@2.0.0': {} '@iconify/types@2.0.0': {}
'@iconify/utils@2.1.33': '@iconify/utils@3.0.1':
dependencies: dependencies:
'@antfu/install-pkg': 0.4.1 '@antfu/install-pkg': 1.1.0
'@antfu/utils': 0.7.10 '@antfu/utils': 9.2.0
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
debug: 4.4.0 debug: 4.4.1
globals: 15.15.0
kolorist: 1.8.0 kolorist: 1.8.0
local-pkg: 0.5.1 local-pkg: 1.1.2
mlly: 1.7.3 mlly: 1.8.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -12661,7 +12688,7 @@ snapshots:
- encoding - encoding
- supports-color - supports-color
'@mermaid-js/parser@0.4.0': '@mermaid-js/parser@0.6.2':
dependencies: dependencies:
langium: 3.3.1 langium: 3.3.1
@ -14971,6 +14998,8 @@ snapshots:
acorn@8.14.0: {} acorn@8.14.0: {}
acorn@8.15.0: {}
address@1.2.2: {} address@1.2.2: {}
agent-base@6.0.2: agent-base@6.0.2:
@ -15637,6 +15666,8 @@ snapshots:
confbox@0.1.8: {} confbox@0.1.8: {}
confbox@0.2.2: {}
config-chain@1.1.13: config-chain@1.1.13:
dependencies: dependencies:
ini: 1.3.8 ini: 1.3.8
@ -16006,6 +16037,10 @@ snapshots:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
debug@4.4.1:
dependencies:
ms: 2.1.3
decamelize@1.2.0: {} decamelize@1.2.0: {}
decimal.js@10.4.3: {} decimal.js@10.4.3: {}
@ -16589,6 +16624,8 @@ snapshots:
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
exsolve@1.0.7: {}
external-editor@3.1.0: external-editor@3.1.0:
dependencies: dependencies:
chardet: 0.7.0 chardet: 0.7.0
@ -17957,10 +17994,11 @@ snapshots:
loader-runner@4.3.0: {} loader-runner@4.3.0: {}
local-pkg@0.5.1: local-pkg@1.1.2:
dependencies: dependencies:
mlly: 1.7.3 mlly: 1.8.0
pkg-types: 1.2.1 pkg-types: 2.3.0
quansync: 0.2.11
locate-path@5.0.0: locate-path@5.0.0:
dependencies: dependencies:
@ -18163,11 +18201,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
mermaid@11.6.0: mermaid@11.11.0:
dependencies: dependencies:
'@braintree/sanitize-url': 7.1.0 '@braintree/sanitize-url': 7.1.0
'@iconify/utils': 2.1.33 '@iconify/utils': 3.0.1
'@mermaid-js/parser': 0.4.0 '@mermaid-js/parser': 0.6.2
'@types/d3': 7.4.3 '@types/d3': 7.4.3
cytoscape: 3.30.2 cytoscape: 3.30.2
cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.2) cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.2)
@ -18391,12 +18429,12 @@ snapshots:
mkdirp@1.0.4: {} mkdirp@1.0.4: {}
mlly@1.7.3: mlly@1.8.0:
dependencies: dependencies:
acorn: 8.14.0 acorn: 8.15.0
pathe: 1.1.2 pathe: 2.0.3
pkg-types: 1.2.1 pkg-types: 1.3.1
ufo: 1.5.4 ufo: 1.6.1
mri@1.2.0: {} mri@1.2.0: {}
@ -18755,7 +18793,7 @@ snapshots:
package-json-from-dist@1.0.0: {} package-json-from-dist@1.0.0: {}
package-manager-detector@0.2.5: {} package-manager-detector@1.3.0: {}
pako@1.0.11: {} pako@1.0.11: {}
@ -18846,7 +18884,7 @@ snapshots:
path-type@4.0.0: {} path-type@4.0.0: {}
pathe@1.1.2: {} pathe@2.0.3: {}
pause@0.0.1: {} pause@0.0.1: {}
@ -18963,11 +19001,17 @@ snapshots:
dependencies: dependencies:
find-up: 4.1.0 find-up: 4.1.0
pkg-types@1.2.1: pkg-types@1.3.1:
dependencies: dependencies:
confbox: 0.1.8 confbox: 0.1.8
mlly: 1.7.3 mlly: 1.8.0
pathe: 1.1.2 pathe: 2.0.3
pkg-types@2.3.0:
dependencies:
confbox: 0.2.2
exsolve: 1.0.7
pathe: 2.0.3
pluralize@8.0.0: {} pluralize@8.0.0: {}
@ -19248,6 +19292,8 @@ snapshots:
dependencies: dependencies:
side-channel: 1.0.6 side-channel: 1.0.6
quansync@0.2.11: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
quick-format-unescaped@4.0.4: {} quick-format-unescaped@4.0.4: {}
@ -20090,7 +20136,7 @@ snapshots:
dependencies: dependencies:
real-require: 0.2.0 real-require: 0.2.0
tinyexec@0.3.1: {} tinyexec@1.0.1: {}
tinyglobby@0.2.12: tinyglobby@0.2.12:
dependencies: dependencies:
@ -20348,7 +20394,7 @@ snapshots:
uc.micro@2.1.0: {} uc.micro@2.1.0: {}
ufo@1.5.4: {} ufo@1.6.1: {}
uid2@0.0.4: {} uid2@0.0.4: {}