mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 14:43:06 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c354bc7be3 |
+2
-4
@@ -1,4 +1,4 @@
|
||||
FROM node:22-slim AS base
|
||||
FROM node:22-alpine AS base
|
||||
LABEL org.opencontainers.image.source="https://github.com/docmost/docmost"
|
||||
|
||||
FROM base AS builder
|
||||
@@ -13,9 +13,7 @@ RUN pnpm build
|
||||
|
||||
FROM base AS installer
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache curl bash
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"version": "0.24.1",
|
||||
"version": "0.23.2",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"Delete group": "Gruppe löschen",
|
||||
"Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Sind Sie sicher, dass Sie diese Seite löschen möchten? Dadurch werden ihre Unterseiten und die Seitengeschichte gelöscht. Diese Aktion ist unwiderruflich.",
|
||||
"Description": "Beschreibung",
|
||||
"Details": "Details",
|
||||
"Details": "Einzelheiten",
|
||||
"e.g ACME": "z.B. ACME",
|
||||
"e.g ACME Inc": "z.B. ACME Inc.",
|
||||
"e.g Developers": "z.B. Entwickler",
|
||||
@@ -525,47 +525,5 @@
|
||||
"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",
|
||||
"Icon": "Icon",
|
||||
"Upload image": "Bild hochladen",
|
||||
"Remove image": "Bild entfernen",
|
||||
"Failed to remove image": "Fehler beim Entfernen des Bildes",
|
||||
"Image exceeds 10MB limit.": "Bild überschreitet das Limit von 10 MB.",
|
||||
"Image removed successfully": "Bild erfolgreich entfernt",
|
||||
"API key": "API-Schlüssel",
|
||||
"API key created successfully": "API-Schlüssel erfolgreich erstellt",
|
||||
"API keys": "API-Schlüssel",
|
||||
"API management": "API-Verwaltung",
|
||||
"Are you sure you want to revoke this API key": "Sind Sie sicher, dass Sie diesen API-Schlüssel widerrufen möchten?",
|
||||
"Create API Key": "API-Schlüssel erstellen",
|
||||
"Custom expiration date": "Benutzerdefiniertes Ablaufdatum",
|
||||
"Enter a descriptive token name": "Geben Sie einen beschreibenden Token-Namen ein",
|
||||
"Expiration": "Ablauf",
|
||||
"Expired": "Abgelaufen",
|
||||
"Expires": "Läuft ab",
|
||||
"I've saved my API key": "Ich habe meinen API-Schlüssel gespeichert",
|
||||
"Last use": "Zuletzt verwendet",
|
||||
"No API keys found": "Keine API-Schlüssel gefunden",
|
||||
"No expiration": "Kein Ablauf",
|
||||
"Revoke API key": "API-Schlüssel widerrufen",
|
||||
"Revoked successfully": "Erfolgreich widerrufen",
|
||||
"Select expiration date": "Ablaufdatum wählen",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Anwendungen, die diesen API-Schlüssel verwenden, werden nicht mehr funktionieren.",
|
||||
"Update API key": "API-Schlüssel aktualisieren",
|
||||
"Manage API keys for all users in the workspace": "Verwalten Sie API-Schlüssel für alle Benutzer im Arbeitsbereich",
|
||||
"AI settings": "KI-Einstellungen",
|
||||
"AI search": "KI-Suche",
|
||||
"AI Answer": "KI-Antwort",
|
||||
"Ask AI": "KI fragen",
|
||||
"AI is thinking...": "Die KI überlegt...",
|
||||
"Ask a question...": "Fragen stellen...",
|
||||
"AI-powered search (Ask AI)": "KI-gestützte Suche (KI fragen)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Die KI-Suche verwendet Vektor-Einbettungen, um semantische Suchfunktionen in Ihrem Arbeitsbereich bereitzustellen.",
|
||||
"Toggle AI search": "KI-Suche umschalten",
|
||||
"Sources": "Quellen",
|
||||
"Ask AI not available for attachments": "KI fragen nicht für Anhänge verfügbar",
|
||||
"No answer available": "Keine Antwort verfügbar",
|
||||
"Background color": "Hintergrundfarbe",
|
||||
"Highlight color": "Hervorhebungsfarbe",
|
||||
"Remove color": "Farbe entfernen"
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}-Konfiguration"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"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}}",
|
||||
"Icon": "Icono",
|
||||
"Upload image": "Subir imagen",
|
||||
"Remove image": "Eliminar imagen",
|
||||
"Failed to remove image": "No se ha podido eliminar la imagen",
|
||||
"Image exceeds 10MB limit.": "La imagen excede del límite de 10 MB",
|
||||
"Image removed successfully": "Imagen eliminada correctamente",
|
||||
"API key": "Clave API",
|
||||
"API key created successfully": "Clave API creada correctamente",
|
||||
"API keys": "Claves API",
|
||||
"API management": "Gestión de API",
|
||||
"Are you sure you want to revoke this API key": "¿Está seguro de que desea revocar esta clave API? ",
|
||||
"Create API Key": "Crear clave API",
|
||||
"Custom expiration date": "Fecha de vencimiento personalizada",
|
||||
"Enter a descriptive token name": "Introduce un nombre descriptivo del token",
|
||||
"Expiration": "Vencimiento",
|
||||
"Expired": "Vencido",
|
||||
"Expires": "Vence",
|
||||
"I've saved my API key": "He guardado mi clave API",
|
||||
"Last use": "Último uso",
|
||||
"No API keys found": "No se han encontrado claves API",
|
||||
"No expiration": "Sin vencimiento",
|
||||
"Revoke API key": "Revocar clave API",
|
||||
"Revoked successfully": "Revocada correctamente",
|
||||
"Select expiration date": "Seleccionar fecha de vencimiento",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Esta acción no se puede deshacer. Las aplicaciones que utilicen esta clave API dejarán de funcionar.",
|
||||
"Update API key": "Actualizar clave API",
|
||||
"Manage API keys for all users in the workspace": "Gestionar claves API para todos los usuarios en el espacio de trabajo",
|
||||
"AI settings": "Configuración de IA",
|
||||
"AI search": "Búsqueda de IA",
|
||||
"AI Answer": "Respuesta de IA",
|
||||
"Ask AI": "Preguntar a IA",
|
||||
"AI is thinking...": "IA está pensando...",
|
||||
"Ask a question...": "Haz una pregunta...",
|
||||
"AI-powered search (Ask AI)": "Búsqueda impulsada por IA (Preguntar a IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La búsqueda de IA utiliza incrustaciones vectoriales para proporcionar capacidades de búsqueda semántica en todo el contenido de su espacio de trabajo.",
|
||||
"Toggle AI search": "Alternar búsqueda de IA",
|
||||
"Sources": "Fuentes",
|
||||
"Ask AI not available for attachments": "Preguntar a IA no está disponible para adjuntos",
|
||||
"No answer available": "No hay respuesta disponible",
|
||||
"Background color": "Color de fondo",
|
||||
"Highlight color": "Color de resaltado",
|
||||
"Remove color": "Eliminar color"
|
||||
"{{ssoProviderType}} configuration": "Configuración de {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"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}}",
|
||||
"Icon": "Icône",
|
||||
"Upload image": "Téléverser une image",
|
||||
"Remove image": "Supprimer l'image",
|
||||
"Failed to remove image": "Échec de la suppression de l'image",
|
||||
"Image exceeds 10MB limit.": "L'image dépasse la limite de 10 Mo.",
|
||||
"Image removed successfully": "Image supprimée avec succès",
|
||||
"API key": "Clé API",
|
||||
"API key created successfully": "Clé API créée avec succès",
|
||||
"API keys": "Clés API",
|
||||
"API management": "Gestion des API",
|
||||
"Are you sure you want to revoke this API key": "Êtes-vous sûr de vouloir révoquer cette clé API",
|
||||
"Create API Key": "Créer une clé API",
|
||||
"Custom expiration date": "Date d'expiration personnalisée",
|
||||
"Enter a descriptive token name": "Entrez un nom descriptif pour le jeton",
|
||||
"Expiration": "Expiration",
|
||||
"Expired": "Expiré(e)",
|
||||
"Expires": "Expire",
|
||||
"I've saved my API key": "J'ai enregistré ma clé API",
|
||||
"Last use": "Dernière utilisation",
|
||||
"No API keys found": "Aucune clé API trouvée",
|
||||
"No expiration": "Pas d'expiration",
|
||||
"Revoke API key": "Révoquer la clé API",
|
||||
"Revoked successfully": "Révoqué(e) avec succès",
|
||||
"Select expiration date": "Sélectionnez la date d'expiration",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Cette action ne peut pas être annulée. Toutes les applications utilisant cette clé API cesseront de fonctionner.",
|
||||
"Update API key": "Mettre à jour la clé API",
|
||||
"Manage API keys for all users in the workspace": "Gérer les clés API pour tous les utilisateurs dans l'espace de travail",
|
||||
"AI settings": "Paramètres de l'IA",
|
||||
"AI search": "Recherche IA",
|
||||
"AI Answer": "Réponse IA",
|
||||
"Ask AI": "Demander à l'IA",
|
||||
"AI is thinking...": "L'IA réfléchit...",
|
||||
"Ask a question...": "Posez une question...",
|
||||
"AI-powered search (Ask AI)": "Recherche assistée par l'IA (Demander à l'IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La recherche IA utilise des incorporations vectorielles pour fournir des capacités de recherche sémantique à travers le contenu de votre espace de travail.",
|
||||
"Toggle AI search": "Basculer la recherche IA",
|
||||
"Sources": "Sources",
|
||||
"Ask AI not available for attachments": "Demande à l'IA non disponible pour les pièces jointes",
|
||||
"No answer available": "Pas de réponse disponible",
|
||||
"Background color": "Couleur de fond",
|
||||
"Highlight color": "Couleur de surbrillance",
|
||||
"Remove color": "Supprimer la couleur"
|
||||
"{{ssoProviderType}} configuration": "Configuration {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"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}}",
|
||||
"Icon": "Icona",
|
||||
"Upload image": "Carica immagine",
|
||||
"Remove image": "Rimuovi immagine",
|
||||
"Failed to remove image": "Rimozione immagine fallita",
|
||||
"Image exceeds 10MB limit.": "L'immagine supera il limite di 10MB.",
|
||||
"Image removed successfully": "Immagine rimossa con successo",
|
||||
"API key": "Chiave API",
|
||||
"API key created successfully": "Chiave API creata con successo",
|
||||
"API keys": "Chiavi API",
|
||||
"API management": "Gestione API",
|
||||
"Are you sure you want to revoke this API key": "Sei sicuro di voler revocare questa chiave API",
|
||||
"Create API Key": "Crea Chiave API",
|
||||
"Custom expiration date": "Data di scadenza personalizzata",
|
||||
"Enter a descriptive token name": "Inserisci un nome descrittivo del token",
|
||||
"Expiration": "Scadenza",
|
||||
"Expired": "Scaduto",
|
||||
"Expires": "Scade",
|
||||
"I've saved my API key": "Ho salvato la mia chiave API",
|
||||
"Last use": "Ultimo utilizzo",
|
||||
"No API keys found": "Nessuna chiave API trovata",
|
||||
"No expiration": "Nessuna scadenza",
|
||||
"Revoke API key": "Revoca chiave API",
|
||||
"Revoked successfully": "Revocata con successo",
|
||||
"Select expiration date": "Seleziona la data di scadenza",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Questa azione non può essere annullata. Qualsiasi applicazione che utilizza questa chiave API smetterà di funzionare.",
|
||||
"Update API key": "Aggiorna chiave API",
|
||||
"Manage API keys for all users in the workspace": "Gestisci le chiavi API per tutti gli utenti nell'area di lavoro",
|
||||
"AI settings": "Impostazioni AI",
|
||||
"AI search": "Ricerca AI",
|
||||
"AI Answer": "Risposta AI",
|
||||
"Ask AI": "Chiedi all'AI",
|
||||
"AI is thinking...": "L'AI sta pensando...",
|
||||
"Ask a question...": "Fai una domanda...",
|
||||
"AI-powered search (Ask AI)": "Ricerca potenziata dall'AI (Chiedi all'AI)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "La ricerca AI utilizza embeddings vettoriali per fornire capacità di ricerca semantica nel contenuto della tua area di lavoro.",
|
||||
"Toggle AI search": "Attiva/disattiva ricerca AI",
|
||||
"Sources": "Fonti",
|
||||
"Ask AI not available for attachments": "Chiedere all'AI non è disponibile per gli allegati",
|
||||
"No answer available": "Nessuna risposta disponibile",
|
||||
"Background color": "Colore di sfondo",
|
||||
"Highlight color": "Colore evidenziato",
|
||||
"Remove color": "Rimuovi colore"
|
||||
"{{ssoProviderType}} configuration": "Configurazione {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"Delete SSO provider": "SSOプロバイダーを削除する",
|
||||
"Are you sure you want to delete this SSO provider?": "このSSOプロバイダーを削除してもよろしいですか?",
|
||||
"Action": "アクション",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}の構成",
|
||||
"Icon": "アイコン",
|
||||
"Upload image": "画像をアップロード",
|
||||
"Remove image": "画像を削除",
|
||||
"Failed to remove image": "画像の削除に失敗しました",
|
||||
"Image exceeds 10MB limit.": "画像が10MBの制限を超えています。",
|
||||
"Image removed successfully": "画像が正常に削除されました",
|
||||
"API key": "APIキー",
|
||||
"API key created successfully": "APIキーが正常に作成されました",
|
||||
"API keys": "APIキー",
|
||||
"API management": "API管理",
|
||||
"Are you sure you want to revoke this API key": "このAPIキーを無効にしてもよろしいですか",
|
||||
"Create API Key": "APIキーを作成",
|
||||
"Custom expiration date": "カスタム有効期限",
|
||||
"Enter a descriptive token name": "説明的なトークン名を入力してください",
|
||||
"Expiration": "有効期限",
|
||||
"Expired": "期限切れ",
|
||||
"Expires": "期限が切れます",
|
||||
"I've saved my API key": "APIキーを保存しました",
|
||||
"Last use": "最終使用",
|
||||
"No API keys found": "APIキーが見つかりません",
|
||||
"No expiration": "期限なし",
|
||||
"Revoke API key": "APIキーを無効にする",
|
||||
"Revoked successfully": "正常に無効化されました",
|
||||
"Select expiration date": "有効期限を選択してください",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "この操作は元に戻せません。このAPIキーを使用しているアプリケーションは動作を停止します。",
|
||||
"Update API key": "APIキーを更新",
|
||||
"Manage API keys for all users in the workspace": "ワークスペース内のすべてのユーザーのAPIキーを管理",
|
||||
"AI settings": "AI設定",
|
||||
"AI search": "AI検索",
|
||||
"AI Answer": "AI回答",
|
||||
"Ask AI": "AIに質問する",
|
||||
"AI is thinking...": "AIが考え中...",
|
||||
"Ask a question...": "質問を入力...",
|
||||
"AI-powered search (Ask AI)": "AIによる検索(AIに質問)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI検索はベクター埋め込みを使用して、ワークスペースコンテンツ全体にわたって意味検索機能を提供します。",
|
||||
"Toggle AI search": "AI検索を切り替え",
|
||||
"Sources": "ソース",
|
||||
"Ask AI not available for attachments": "添付ファイルにはAI質問は利用できません",
|
||||
"No answer available": "回答がありません",
|
||||
"Background color": "背景色",
|
||||
"Highlight color": "ハイライト色",
|
||||
"Remove color": "色を削除"
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}}の構成"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"Delete SSO provider": "SSO 제공자 삭제",
|
||||
"Are you sure you want to delete this SSO provider?": "이 SSO 제공자를 삭제하시겠습니까?",
|
||||
"Action": "작업",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} 구성",
|
||||
"Icon": "아이콘",
|
||||
"Upload image": "이미지 업로드",
|
||||
"Remove image": "이미지 제거",
|
||||
"Failed to remove image": "이미지 제거 실패",
|
||||
"Image exceeds 10MB limit.": "이미지가 10MB 용량 제한을 초과합니다.",
|
||||
"Image removed successfully": "이미지가 성공적으로 제거되었습니다",
|
||||
"API key": "API 키",
|
||||
"API key created successfully": "API 키 생성 완료",
|
||||
"API keys": "API 키",
|
||||
"API management": "API 관리",
|
||||
"Are you sure you want to revoke this API key": "이 API 키를 취소하시겠습니까?",
|
||||
"Create API Key": "API 키 생성",
|
||||
"Custom expiration date": "사용자 정의 만료일",
|
||||
"Enter a descriptive token name": "토큰 이름을 입력하세요",
|
||||
"Expiration": "만료",
|
||||
"Expired": "만료됨",
|
||||
"Expires": "만료일",
|
||||
"I've saved my API key": "API 키를 저장했습니다",
|
||||
"Last use": "최근 사용",
|
||||
"No API keys found": "API 키를 찾을 수 없습니다",
|
||||
"No expiration": "유효기간 없음",
|
||||
"Revoke API key": "API 키 취소",
|
||||
"Revoked successfully": "성공적으로 취소되었습니다",
|
||||
"Select expiration date": "만료일 선택",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "이 작업은 되돌릴 수 없습니다. 이 API 키를 사용하는 모든 응용 프로그램이 작동을 멈출 것입니다.",
|
||||
"Update API key": "API 키 갱신",
|
||||
"Manage API keys for all users in the workspace": "워크스페이스 내 모든 사용자의 API 키 관리",
|
||||
"AI settings": "AI 설정",
|
||||
"AI search": "AI 검색",
|
||||
"AI Answer": "AI 답변",
|
||||
"Ask AI": "AI에게 묻기",
|
||||
"AI is thinking...": "AI가 생각 중입니다...",
|
||||
"Ask a question...": "질문하세요...",
|
||||
"AI-powered search (Ask AI)": "AI 지원 검색 (AI에게 묻기)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI 검색은 벡터 임베딩을 사용하여 작업공간 콘텐츠에 대한 의미 검색 기능을 제공합니다.",
|
||||
"Toggle AI search": "AI 검색 전환",
|
||||
"Sources": "출처",
|
||||
"Ask AI not available for attachments": "AI에게 묻기 기능은 첨부 파일에 대해 사용할 수 없습니다",
|
||||
"No answer available": "답변을 제공할 수 없습니다",
|
||||
"Background color": "배경 색",
|
||||
"Highlight color": "강조 색",
|
||||
"Remove color": "색 제거"
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} 구성"
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"Create group": "Groep aanmaken",
|
||||
"Create page": "Pagina aanmaken",
|
||||
"Create space": "Ruimte aanmaken",
|
||||
"Create workspace": "Werkruimte aanmaken",
|
||||
"Create workspace": "Wwerkruimte aanmaken",
|
||||
"Current password": "Huidig wachtwoord",
|
||||
"Dark": "Donker",
|
||||
"Date": "Datum",
|
||||
@@ -91,7 +91,7 @@
|
||||
"Invite by email": "Uitnodigen via e-mail",
|
||||
"Invite members": "Leden uitnodigen",
|
||||
"Invite new members": "Nieuwe leden uitnodigen",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Uitgenodigde leden die hun uitnodiging nog moeten accepteren zullen hier worden getoond.",
|
||||
"Invited members who are yet to accept their invitation will appear here.": "Uigenodigde leden die hun uitnodiging nog moeten accepteren zullen hier worden getoond.",
|
||||
"Invited members will be granted access to spaces the groups can access": "Uitgenodigde leden wordt toegang gegeven tot ruimtes de groepen toegang toe heeft",
|
||||
"Join the workspace": "Word lid van de werkruimte",
|
||||
"Language": "Taal",
|
||||
@@ -527,47 +527,5 @@
|
||||
"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",
|
||||
"Icon": "Icoon",
|
||||
"Upload image": "Afbeelding uploaden",
|
||||
"Remove image": "Afbeelding verwijderen",
|
||||
"Failed to remove image": "Afbeelding verwijderen mislukt",
|
||||
"Image exceeds 10MB limit.": "Afbeelding overschrijdt de limiet van 10MB.",
|
||||
"Image removed successfully": "Afbeelding succesvol verwijderd",
|
||||
"API key": "API-sleutel",
|
||||
"API key created successfully": "API-sleutel succesvol aangemaakt",
|
||||
"API keys": "API-sleutels",
|
||||
"API management": "API-beheer",
|
||||
"Are you sure you want to revoke this API key": "Weet u zeker dat u deze API-sleutel wilt intrekken",
|
||||
"Create API Key": "API-sleutel aanmaken",
|
||||
"Custom expiration date": "Aangepaste vervaldatum",
|
||||
"Enter a descriptive token name": "Voer een beschrijvende tokennaam in",
|
||||
"Expiration": "Vervaldatum",
|
||||
"Expired": "Verlopen",
|
||||
"Expires": "Verloopt",
|
||||
"I've saved my API key": "Ik heb mijn API-sleutel opgeslagen",
|
||||
"Last use": "Laatst gebruikt",
|
||||
"No API keys found": "Geen API-sleutels gevonden",
|
||||
"No expiration": "Geen vervaldatum",
|
||||
"Revoke API key": "API-sleutel intrekken",
|
||||
"Revoked successfully": "Succesvol ingetrokken",
|
||||
"Select expiration date": "Selecteer vervaldatum",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Deze actie kan niet ongedaan worden gemaakt. Alle toepassingen die deze API-sleutel gebruiken, zullen niet meer werken.",
|
||||
"Update API key": "API-sleutel bijwerken",
|
||||
"Manage API keys for all users in the workspace": "Beheer API-sleutels voor alle gebruikers in de werkruimte",
|
||||
"AI settings": "AI-instellingen",
|
||||
"AI search": "AI-zoekopdracht",
|
||||
"AI Answer": "AI Antwoord",
|
||||
"Ask AI": "Vraag AI",
|
||||
"AI is thinking...": "AI is aan het nadenken...",
|
||||
"Ask a question...": "Stel een vraag...",
|
||||
"AI-powered search (Ask AI)": "AI-ondersteunde zoekopdracht (Vraag AI)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI-zoekopdracht maakt gebruik van vectorembeddings om semantische zoekmogelijkheden te bieden in uw werkruimte-inhoud.",
|
||||
"Toggle AI search": "Schakel AI-zoekopdracht in/uit",
|
||||
"Sources": "Bronnen",
|
||||
"Ask AI not available for attachments": "Vraag AI is niet beschikbaar voor bijlages",
|
||||
"No answer available": "Geen antwoord beschikbaar",
|
||||
"Background color": "Achtergrondkleur",
|
||||
"Highlight color": "Markeerkleur",
|
||||
"Remove color": "Kleur verwijderen"
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} configuratie"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"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}}",
|
||||
"Icon": "Ícone",
|
||||
"Upload image": "Fazer upload da imagem",
|
||||
"Remove image": "Remover imagem",
|
||||
"Failed to remove image": "Falha ao remover imagem",
|
||||
"Image exceeds 10MB limit.": "A imagem excede o limite de 10MB.",
|
||||
"Image removed successfully": "Imagem removida com sucesso",
|
||||
"API key": "Chave API",
|
||||
"API key created successfully": "Chave API criada com sucesso",
|
||||
"API keys": "Chaves API",
|
||||
"API management": "Gestão de API",
|
||||
"Are you sure you want to revoke this API key": "Tem certeza de que deseja revogar esta chave API",
|
||||
"Create API Key": "Criar Chave API",
|
||||
"Custom expiration date": "Data de expiração personalizada",
|
||||
"Enter a descriptive token name": "Insira um nome descritivo para o token",
|
||||
"Expiration": "Expiração",
|
||||
"Expired": "Expirado",
|
||||
"Expires": "Expira",
|
||||
"I've saved my API key": "Salvei minha chave API",
|
||||
"Last use": "Último uso",
|
||||
"No API keys found": "Nenhuma chave API encontrada",
|
||||
"No expiration": "Sem expiração",
|
||||
"Revoke API key": "Revogar chave API",
|
||||
"Revoked successfully": "Revogada com sucesso",
|
||||
"Select expiration date": "Selecionar data de expiração",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Esta ação não pode ser desfeita. Qualquer aplicação usando esta chave API deixará de funcionar.",
|
||||
"Update API key": "Atualizar chave API",
|
||||
"Manage API keys for all users in the workspace": "Gerenciar chaves API para todos os usuários no espaço de trabalho",
|
||||
"AI settings": "Configurações de IA",
|
||||
"AI search": "Pesquisa IA",
|
||||
"AI Answer": "Resposta de IA",
|
||||
"Ask AI": "Pergunte à IA",
|
||||
"AI is thinking...": "IA está pensando...",
|
||||
"Ask a question...": "Faça uma pergunta...",
|
||||
"AI-powered search (Ask AI)": "Pesquisa com IA (Pergunte à IA)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "A pesquisa IA usa vetores de incorporação para fornecer capacidades de pesquisa semântica em todo o conteúdo do seu espaço de trabalho.",
|
||||
"Toggle AI search": "Alternar pesquisa de IA",
|
||||
"Sources": "Fontes",
|
||||
"Ask AI not available for attachments": "Perguntar à IA não está disponível para anexos",
|
||||
"No answer available": "Nenhuma resposta disponível",
|
||||
"Background color": "Cor de fundo",
|
||||
"Highlight color": "Cor de destaque",
|
||||
"Remove color": "Remover cor"
|
||||
"{{ssoProviderType}} configuration": "Configuração de {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -498,10 +498,10 @@
|
||||
"Deleted at": "Удалено в",
|
||||
"Preview": "Предпросмотр",
|
||||
"Subpages": "Подстраницы",
|
||||
"Failed to load subpages": "Не удалось загрузить под страницы",
|
||||
"Failed to load subpages": "Не удалось загрузить подстраницы",
|
||||
"No subpages": "Нет подстраниц",
|
||||
"Subpages (Child pages)": "Подстраницы (вложенные страницы)",
|
||||
"List all subpages of the current page": "Показать все под страницы",
|
||||
"List all subpages of the current page": "Показать все подстраницы текущей страницы",
|
||||
"Attachments": "Вложения",
|
||||
"All spaces": "Все пространства",
|
||||
"Unknown": "Неизвестно",
|
||||
@@ -527,47 +527,5 @@
|
||||
"Delete SSO provider": "Удалить поставщика SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Вы уверены, что хотите удалить этого поставщика SSO?",
|
||||
"Action": "Действие",
|
||||
"{{ssoProviderType}} configuration": "Настройка {{ssoProviderType}}",
|
||||
"Icon": "Иконка",
|
||||
"Upload image": "Загрузить изображение",
|
||||
"Remove image": "Удалить изображение",
|
||||
"Failed to remove image": "Не удалось удалить изображение",
|
||||
"Image exceeds 10MB limit.": "Изображение превышает предел 10MB.",
|
||||
"Image removed successfully": "Изображение успешно удалено",
|
||||
"API key": "API ключ",
|
||||
"API key created successfully": "API ключ успешно создан",
|
||||
"API keys": "API ключи",
|
||||
"API management": "Управление API",
|
||||
"Are you sure you want to revoke this API key": "Вы уверены, что хотите отозвать этот API ключ",
|
||||
"Create API Key": "Создать API ключ",
|
||||
"Custom expiration date": "Пользовательская дата срока действия",
|
||||
"Enter a descriptive token name": "Введите понятное имя токена",
|
||||
"Expiration": "Срок действия",
|
||||
"Expired": "Истек",
|
||||
"Expires": "Истекает",
|
||||
"I've saved my API key": "Я сохранил мой API ключ",
|
||||
"Last use": "Последнее использование",
|
||||
"No API keys found": "API ключи не найдены",
|
||||
"No expiration": "Не истекает",
|
||||
"Revoke API key": "Отозвать API ключ",
|
||||
"Revoked successfully": "Отозван успешно",
|
||||
"Select expiration date": "Выберете срок действия",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Это действие необратимо. Любые приложения, использующие этот API ключ, перестанут работать.",
|
||||
"Update API key": "Обновить API ключ",
|
||||
"Manage API keys for all users in the workspace": "Управлять API ключами для всех пользователей в рабочей области",
|
||||
"AI settings": "Настройки ИИ",
|
||||
"AI search": "Поиск ИИ",
|
||||
"AI Answer": "Ответ ИИ",
|
||||
"Ask AI": "Спросить ИИ",
|
||||
"AI is thinking...": "ИИ обрабатывает запрос...",
|
||||
"Ask a question...": "Задайте вопрос...",
|
||||
"AI-powered search (Ask AI)": "Поиск на базе ИИ (Спросить ИИ)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Поиск ИИ использует векторные встраивания для обеспечения семантического поиска по содержимому вашего рабочего пространства.",
|
||||
"Toggle AI search": "Переключить поиск ИИ",
|
||||
"Sources": "Источники",
|
||||
"Ask AI not available for attachments": "Функция \"Спросить ИИ\" недоступна для вложений",
|
||||
"No answer available": "Ответ недоступен",
|
||||
"Background color": "Цвет фона",
|
||||
"Highlight color": "Цвет выделения",
|
||||
"Remove color": "Удалить цвет"
|
||||
"{{ssoProviderType}} configuration": "Настройка {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"Delete SSO provider": "Видалити постачальника SSO",
|
||||
"Are you sure you want to delete this SSO provider?": "Ви впевнені, що хочете видалити цього постачальника SSO?",
|
||||
"Action": "Дія",
|
||||
"{{ssoProviderType}} configuration": "Конфігурація {{ssoProviderType}}",
|
||||
"Icon": "Іконка",
|
||||
"Upload image": "Завантажити зображення",
|
||||
"Remove image": "Видалити зображення",
|
||||
"Failed to remove image": "Не вдалося видалити зображення",
|
||||
"Image exceeds 10MB limit.": "Зображення має займати менше, ніж 10 МБ.",
|
||||
"Image removed successfully": "Зображення видалено",
|
||||
"API key": "Ключ API",
|
||||
"API key created successfully": "Ключ API успішно створено",
|
||||
"API keys": "Ключі API",
|
||||
"API management": "Управління API",
|
||||
"Are you sure you want to revoke this API key": "Ви впевнені, що хочете відкликати цей ключ API",
|
||||
"Create API Key": "Створити ключ API",
|
||||
"Custom expiration date": "Користувацька дата закінчення",
|
||||
"Enter a descriptive token name": "Введіть описову назву токена",
|
||||
"Expiration": "Термін дії",
|
||||
"Expired": "Закінчився",
|
||||
"Expires": "Закінчується",
|
||||
"I've saved my API key": "Я зберіг свій ключ API",
|
||||
"Last use": "Останнє використання",
|
||||
"No API keys found": "Ключі API не знайдено",
|
||||
"No expiration": "Без терміну дії",
|
||||
"Revoke API key": "Відкликати ключ API",
|
||||
"Revoked successfully": "Успішно відкликано",
|
||||
"Select expiration date": "Виберіть дату закінчення",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "Цю дію не можна скасувати. Будь-які додатки, що використовують цей ключ API, перестануть працювати.",
|
||||
"Update API key": "Оновити ключ API",
|
||||
"Manage API keys for all users in the workspace": "Керувати ключами API для всіх користувачів у робочій області",
|
||||
"AI settings": "Налаштування ШІ",
|
||||
"AI search": "Пошук з ШІ",
|
||||
"AI Answer": "Відповідь ШІ",
|
||||
"Ask AI": "Запитати ШІ",
|
||||
"AI is thinking...": "ШІ думає...",
|
||||
"Ask a question...": "Задайте питання...",
|
||||
"AI-powered search (Ask AI)": "Пошук на базі ШІ (Запитати ШІ)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "Пошук з ШІ використовує векторні вбудовування для надання можливостей семантичного пошуку у вашому робочому вмісті.",
|
||||
"Toggle AI search": "Переключити пошук з ШІ",
|
||||
"Sources": "Джерела",
|
||||
"Ask AI not available for attachments": "Запитати ШІ недоступно для вкладень",
|
||||
"No answer available": "Відповідь недоступна",
|
||||
"Background color": "Колір фону",
|
||||
"Highlight color": "Колір підсвічування",
|
||||
"Remove color": "Видалити колір"
|
||||
"{{ssoProviderType}} configuration": "Конфігурація {{ssoProviderType}}"
|
||||
}
|
||||
|
||||
@@ -527,47 +527,5 @@
|
||||
"Delete SSO provider": "删除SSO提供商",
|
||||
"Are you sure you want to delete this SSO provider?": "您确定要删除此SSO提供商吗?",
|
||||
"Action": "操作",
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} 配置",
|
||||
"Icon": "图标",
|
||||
"Upload image": "上传图片",
|
||||
"Remove image": "删除图片",
|
||||
"Failed to remove image": "无法删除图片",
|
||||
"Image exceeds 10MB limit.": "图片超过10MB限制。",
|
||||
"Image removed successfully": "图片删除成功",
|
||||
"API key": "API密钥",
|
||||
"API key created successfully": "API密钥创建成功",
|
||||
"API keys": "API密钥",
|
||||
"API management": "API管理",
|
||||
"Are you sure you want to revoke this API key": "确定要撤销此API密钥吗",
|
||||
"Create API Key": "创建API密钥",
|
||||
"Custom expiration date": "自定义到期日期",
|
||||
"Enter a descriptive token name": "输入描述性令牌名称",
|
||||
"Expiration": "到期",
|
||||
"Expired": "已过期",
|
||||
"Expires": "到期",
|
||||
"I've saved my API key": "我已保存我的API密钥",
|
||||
"Last use": "上次使用",
|
||||
"No API keys found": "找不到API密钥",
|
||||
"No expiration": "无到期",
|
||||
"Revoke API key": "撤销API密钥",
|
||||
"Revoked successfully": "撤销成功",
|
||||
"Select expiration date": "选择到期日期",
|
||||
"This action cannot be undone. Any applications using this API key will stop working.": "此操作无法撤销。使用此API密钥的任何应用程序将停止工作。",
|
||||
"Update API key": "更新API密钥",
|
||||
"Manage API keys for all users in the workspace": "管理工作空间中所有用户的API密钥",
|
||||
"AI settings": "AI设置",
|
||||
"AI search": "AI搜索",
|
||||
"AI Answer": "AI回答",
|
||||
"Ask AI": "询问AI",
|
||||
"AI is thinking...": "AI正在思考...",
|
||||
"Ask a question...": "提问...",
|
||||
"AI-powered search (Ask AI)": "AI驱动的搜索(询问AI)",
|
||||
"AI search uses vector embeddings to provide semantic search capabilities across your workspace content.": "AI搜索使用向量嵌入提供跨工作空间内容的语义搜索功能。",
|
||||
"Toggle AI search": "切换AI搜索",
|
||||
"Sources": "来源",
|
||||
"Ask AI not available for attachments": "附件不支持询问AI",
|
||||
"No answer available": "无可用答案",
|
||||
"Background color": "背景颜色",
|
||||
"Highlight color": "突出显示颜色",
|
||||
"Remove color": "移除颜色"
|
||||
"{{ssoProviderType}} configuration": "{{ssoProviderType}} 配置"
|
||||
}
|
||||
|
||||
@@ -37,18 +37,14 @@ export async function askAi(
|
||||
|
||||
let answer = "";
|
||||
let sources: any[] = [];
|
||||
let buffer = "";
|
||||
|
||||
if (reader) {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split("\n");
|
||||
|
||||
// Keep the last incomplete line in the buffer
|
||||
buffer = lines.pop() || "";
|
||||
const chunk = decoder.decode(value);
|
||||
const lines = chunk.split("\n");
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import classes from "./code-block.module.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useComputedColorScheme } from "@mantine/core";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
interface MermaidViewProps {
|
||||
props: NodeViewProps;
|
||||
@@ -38,7 +37,7 @@ export default function MermaidView({ props }: MermaidViewProps) {
|
||||
.catch((err) => {
|
||||
if (props.editor.isEditable) {
|
||||
setPreview(
|
||||
`<div class="${classes.error}">${t("Mermaid diagram error:")} ${DOMPurify.sanitize(err)}</div>`,
|
||||
`<div class="${classes.error}">${t("Mermaid diagram error:")} ${err}</div>`,
|
||||
);
|
||||
} else {
|
||||
setPreview(
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function DrawioView(props: NodeViewProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<Modal.Root opened={opened} onClose={close} fullScreen>
|
||||
<Modal.Overlay />
|
||||
<Modal.Content style={{ overflow: "hidden" }}>
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function EmbedView(props: NodeViewProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
{embedUrl ? (
|
||||
<ResizableWrapper
|
||||
initialHeight={nodeHeight || 480}
|
||||
|
||||
@@ -118,7 +118,7 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<ReactClearModal
|
||||
style={{
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function ImageView(props: NodeViewProps) {
|
||||
}, [align]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<Image
|
||||
radius="md"
|
||||
fit="contain"
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function MentionView(props: NodeViewProps) {
|
||||
});
|
||||
|
||||
return (
|
||||
<NodeViewWrapper style={{ display: "inline" }} data-drag-handle>
|
||||
<NodeViewWrapper style={{ display: "inline" }}>
|
||||
{entityType === "user" && (
|
||||
<Text className={classes.userMention} component="span">
|
||||
@{label}
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
IconCalendar,
|
||||
IconAppWindow,
|
||||
IconSitemap,
|
||||
IconLayoutColumns,
|
||||
IconPageBreak,
|
||||
} from "@tabler/icons-react";
|
||||
import {
|
||||
CommandProps,
|
||||
@@ -154,6 +154,19 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
command: ({ editor, range }: CommandProps) =>
|
||||
editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
|
||||
},
|
||||
{
|
||||
title: "Page break",
|
||||
description: "Insert page break",
|
||||
searchTerms: ["page break", "hr"],
|
||||
icon: IconPageBreak,
|
||||
command: ({ editor, range }: CommandProps) =>
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.insertContent('<hr data-type="pagebreak" /><p></p>')
|
||||
.run(),
|
||||
},
|
||||
{
|
||||
title: "Image",
|
||||
description: "Upload any image from your device.",
|
||||
@@ -244,51 +257,6 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
||||
.run(),
|
||||
},
|
||||
{
|
||||
title: "Columns",
|
||||
description: "Insert 2 columns layout.",
|
||||
searchTerms: ["columns", "layout", "grid", "side by side"],
|
||||
icon: IconLayoutColumns,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.insertContent({
|
||||
type: "column_container",
|
||||
content: [
|
||||
{
|
||||
type: "column",
|
||||
attrs: { colWidth: 200 },
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "column",
|
||||
attrs: { colWidth: 200 },
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "column",
|
||||
attrs: { colWidth: 200 },
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Toggle block",
|
||||
description: "Insert collapsible block.",
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function SubpagesView(props: NodeViewProps) {
|
||||
|
||||
if (error && !shareId) {
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<Text c="dimmed" size="md" py="md">
|
||||
{t("Failed to load subpages")}
|
||||
</Text>
|
||||
@@ -62,7 +62,7 @@ export default function SubpagesView(props: NodeViewProps) {
|
||||
|
||||
if (subpages.length === 0) {
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<div className={classes.container}>
|
||||
<Text c="dimmed" size="md" py="md">
|
||||
{t("No subpages")}
|
||||
@@ -73,7 +73,7 @@ export default function SubpagesView(props: NodeViewProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<div className={classes.container}>
|
||||
<Stack gap={5}>
|
||||
{subpages.map((page) => (
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function VideoView(props: NodeViewProps) {
|
||||
}, [align]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper data-drag-handle>
|
||||
<NodeViewWrapper>
|
||||
<video
|
||||
preload="metadata"
|
||||
width={width}
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
Heading,
|
||||
Highlight,
|
||||
UniqueID,
|
||||
ColumnsExtension,
|
||||
HorizontalRule,
|
||||
} from "@docmost/editor-ext";
|
||||
import {
|
||||
randomElement,
|
||||
@@ -109,7 +109,9 @@ export const mainExtensions = [
|
||||
spellcheck: false,
|
||||
},
|
||||
},
|
||||
horizontalRule: false,
|
||||
}),
|
||||
HorizontalRule,
|
||||
Heading,
|
||||
UniqueID.configure({
|
||||
types: ["heading", "paragraph"],
|
||||
@@ -230,7 +232,6 @@ export const mainExtensions = [
|
||||
Subpages.configure({
|
||||
view: SubpagesView,
|
||||
}),
|
||||
ColumnsExtension,
|
||||
MarkdownClipboard.configure({
|
||||
transformPastedText: true,
|
||||
}),
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
.resize-cursor {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.prosemirror-column-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: calc(100% - 8px);
|
||||
gap: 12px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.prosemirror-column-container.has-focus .prosemirror-column,
|
||||
.prosemirror-column-container:hover .prosemirror-column {
|
||||
background-color: rgba(100, 106, 115, 0.05);
|
||||
}
|
||||
|
||||
.prosemirror-column-container .prosemirror-column {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
min-width: 50px;
|
||||
padding: 12px;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
> :not(div.grid-resize-handle):nth-child(1),
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
> div.grid-resize-handle
|
||||
+ :nth-child(2) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.prosemirror-column-container .prosemirror-column > :nth-last-child(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.prosemirror-column-container .prosemirror-column .grid-resize-handle {
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
z-index: 20;
|
||||
background-color: #336df4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button {
|
||||
top: -8px;
|
||||
left: -9px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #007bff;
|
||||
border: 4px solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button:hover {
|
||||
transform: scale(1.35);
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button
|
||||
.plus {
|
||||
position: relative;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button
|
||||
.plus::before,
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button
|
||||
.plus::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button
|
||||
.plus::before {
|
||||
width: 8px;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.prosemirror-column-container
|
||||
.prosemirror-column
|
||||
.grid-resize-handle
|
||||
.circle-button
|
||||
.plus::after {
|
||||
width: 24px;
|
||||
height: 8px;
|
||||
}
|
||||
@@ -110,6 +110,14 @@
|
||||
border-top: 1px solid #68cef8;
|
||||
}
|
||||
|
||||
hr[data-type="pagebreak"] {
|
||||
border-top: 1px dashed var(--mantine-color-dark-2) !important;
|
||||
}
|
||||
|
||||
.ProseMirror[contenteditable="false"] hr[data-type="pagebreak"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ProseMirror-selectednode {
|
||||
outline: 2px solid #70cff8;
|
||||
}
|
||||
@@ -186,7 +194,6 @@
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ProseMirror > h1,
|
||||
@@ -195,13 +202,11 @@
|
||||
.ProseMirror > h4,
|
||||
.ProseMirror > h5,
|
||||
.ProseMirror > h6 {
|
||||
|
||||
> .link-btn {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
|
||||
> .link-btn > .link-btn-content {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
@@ -213,7 +218,7 @@
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
&:hover > .link-btn > .link-btn-content {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
@import "./mention.css";
|
||||
@import "./ordered-list.css";
|
||||
@import "./highlight.css";
|
||||
@import "./column.css";
|
||||
|
||||
@@ -20,4 +20,10 @@
|
||||
.tableWrapper {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
hr[data-type="pagebreak"] {
|
||||
break-before: always;
|
||||
page-break-before: always;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,17 +62,14 @@ export default function SpaceSettingsModal({
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="general">
|
||||
<ScrollArea h={580} scrollbarSize={5} pr={8}>
|
||||
<div style={{ paddingBottom: "100px"}}>
|
||||
<SpaceDetails
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Settings,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ScrollArea h={550} scrollbarSize={4} pr={8}>
|
||||
<SpaceDetails
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Settings,
|
||||
)}
|
||||
/>
|
||||
</ScrollArea>
|
||||
</Tabs.Panel>
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function SpaceMembersList({
|
||||
return (
|
||||
<>
|
||||
<SearchInput onSearch={handleSearch} />
|
||||
<ScrollArea h={450}>
|
||||
<ScrollArea h={400}>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table highlightOnHover verticalSpacing={8}>
|
||||
<Table.Thead>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "0.24.1",
|
||||
"version": "0.23.2",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
||||
@@ -35,8 +35,8 @@ import {
|
||||
Subpages,
|
||||
Highlight,
|
||||
UniqueID,
|
||||
ColumnsExtension,
|
||||
addUniqueIdsToDoc,
|
||||
HorizontalRule,
|
||||
} from '@docmost/editor-ext';
|
||||
import { generateText, getSchema, JSONContent } from '@tiptap/core';
|
||||
import { generateHTML, generateJSON } from '../common/helpers/prosemirror/html';
|
||||
@@ -49,7 +49,9 @@ export const tiptapExtensions = [
|
||||
StarterKit.configure({
|
||||
codeBlock: false,
|
||||
heading: false,
|
||||
horizontalRule: false,
|
||||
}),
|
||||
HorizontalRule,
|
||||
Heading,
|
||||
UniqueID.configure({
|
||||
types: ['heading', 'paragraph'],
|
||||
@@ -89,7 +91,6 @@ export const tiptapExtensions = [
|
||||
Embed,
|
||||
Mention,
|
||||
Subpages,
|
||||
ColumnsExtension
|
||||
] as any;
|
||||
|
||||
export function jsonToHtml(tiptapJson: any) {
|
||||
|
||||
@@ -69,8 +69,8 @@ export class ShareService {
|
||||
return await this.shareRepo.insertShare({
|
||||
key: nanoIdGen().toLowerCase(),
|
||||
pageId: page.id,
|
||||
includeSubPages: createShareDto.includeSubPages ?? false,
|
||||
searchIndexing: createShareDto.searchIndexing ?? false,
|
||||
includeSubPages: createShareDto.includeSubPages || true,
|
||||
searchIndexing: createShareDto.searchIndexing || true,
|
||||
creatorId: authUserId,
|
||||
spaceId: page.spaceId,
|
||||
workspaceId,
|
||||
|
||||
+1
-1
Submodule apps/server/src/ee updated: 075761c2d9...18e00b1866
@@ -117,7 +117,7 @@ export class EnvironmentVariables {
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_EMBEDDING_DIMENSION)
|
||||
@IsIn(['768', '1024', '1536', '2000'])
|
||||
@IsIn(['768', '1024', '1536'])
|
||||
@IsString()
|
||||
AI_EMBEDDING_DIMENSION: string;
|
||||
|
||||
|
||||
@@ -15,12 +15,10 @@ async function bootstrap() {
|
||||
const app = await NestFactory.create<NestFastifyApplication>(
|
||||
AppModule,
|
||||
new FastifyAdapter({
|
||||
ignoreTrailingSlash: true,
|
||||
ignoreDuplicateSlashes: true,
|
||||
maxParamLength: 1000,
|
||||
trustProxy: true,
|
||||
routerOptions: {
|
||||
maxParamLength: 1000,
|
||||
ignoreTrailingSlash: true,
|
||||
ignoreDuplicateSlashes: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
rawBody: true,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "docmost",
|
||||
"homepage": "https://docmost.com",
|
||||
"version": "0.24.1",
|
||||
"version": "0.23.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nx run-many -t build",
|
||||
@@ -38,6 +38,7 @@
|
||||
"@tiptap/extension-heading": "2.27.1",
|
||||
"@tiptap/extension-highlight": "2.27.1",
|
||||
"@tiptap/extension-history": "2.27.1",
|
||||
"@tiptap/extension-horizontal-rule": "2.27.1",
|
||||
"@tiptap/extension-image": "2.27.1",
|
||||
"@tiptap/extension-link": "2.27.1",
|
||||
"@tiptap/extension-list-item": "2.27.1",
|
||||
@@ -100,7 +101,6 @@
|
||||
},
|
||||
"overrides": {
|
||||
"jsdom": "25.0.1",
|
||||
"jsonwebtoken": "9.0.3",
|
||||
"y-prosemirror": "1.3.7"
|
||||
},
|
||||
"neverBuiltDependencies": []
|
||||
|
||||
@@ -23,4 +23,4 @@ export * from "./lib/subpages";
|
||||
export * from "./lib/highlight";
|
||||
export * from "./lib/heading/heading";
|
||||
export * from "./lib/unique-id";
|
||||
export * from "./lib/columns";
|
||||
export * from "./lib/hr";
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import { EditorState } from '@tiptap/pm/state';
|
||||
import { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view';
|
||||
import { gridResizingPluginKey } from './state';
|
||||
import {
|
||||
draggedWidth,
|
||||
findBoundaryPosition,
|
||||
getColumnInfoAtPos,
|
||||
updateColumnNodeWidth,
|
||||
} from './utils';
|
||||
|
||||
function updateActiveHandle(view: EditorView, value: number) {
|
||||
view.dispatch(
|
||||
view.state.tr.setMeta(gridResizingPluginKey, {
|
||||
setHandle: value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function handleMouseMove(
|
||||
view: EditorView,
|
||||
event: MouseEvent,
|
||||
handleWidth: number,
|
||||
): boolean {
|
||||
const pluginState = gridResizingPluginKey.getState(view.state);
|
||||
if (!pluginState) return false;
|
||||
|
||||
// TODO: limit call?
|
||||
|
||||
if (pluginState.dragging) return false;
|
||||
|
||||
const boundaryPos = findBoundaryPosition(view, event, handleWidth);
|
||||
if (boundaryPos !== pluginState.activeHandle) {
|
||||
updateActiveHandle(view, boundaryPos);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function handleMouseLeave(view: EditorView) {
|
||||
const pluginState = gridResizingPluginKey.getState(view.state);
|
||||
if (!pluginState) return false;
|
||||
if (pluginState.activeHandle > -1 && !pluginState.dragging) {
|
||||
updateActiveHandle(view, -1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function handleMouseDown(
|
||||
view: EditorView,
|
||||
event: MouseEvent,
|
||||
columnMinWidth: number,
|
||||
): boolean {
|
||||
const pluginState = gridResizingPluginKey.getState(view.state);
|
||||
if (!pluginState) return false;
|
||||
if (pluginState.activeHandle === -1) return false;
|
||||
if (pluginState.dragging) return false;
|
||||
|
||||
const columnInfo = getColumnInfoAtPos(view, pluginState.activeHandle);
|
||||
if (!columnInfo) return false;
|
||||
|
||||
const { domWidth, $pos, node } = columnInfo;
|
||||
const nodeAttrs = { ...(node.attrs || {}) };
|
||||
const nodePos = $pos.before();
|
||||
|
||||
view.dispatch(
|
||||
view.state.tr.setMeta(gridResizingPluginKey, {
|
||||
setDragging: { startX: event.clientX, startWidth: domWidth },
|
||||
}),
|
||||
);
|
||||
|
||||
const win = view.dom.ownerDocument.defaultView || window;
|
||||
|
||||
const finish = (e: MouseEvent) => {
|
||||
win.removeEventListener('mouseup', finish);
|
||||
win.removeEventListener('mousemove', move);
|
||||
|
||||
const pluginState = gridResizingPluginKey.getState(view.state);
|
||||
if (!pluginState?.dragging) return;
|
||||
|
||||
const finalWidth = draggedWidth(pluginState.dragging, e, columnMinWidth);
|
||||
updateColumnNodeWidth(view, nodePos, nodeAttrs, finalWidth);
|
||||
view.dispatch(
|
||||
view.state.tr.setMeta(gridResizingPluginKey, {
|
||||
setDragging: null,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const move = (e: MouseEvent) => {
|
||||
if (!e.buttons) {
|
||||
finish(e);
|
||||
return;
|
||||
}
|
||||
const pluginState = gridResizingPluginKey.getState(view.state);
|
||||
if (!pluginState?.dragging) return;
|
||||
|
||||
const newWidth = draggedWidth(pluginState.dragging, e, columnMinWidth);
|
||||
updateColumnNodeWidth(view, nodePos, nodeAttrs, newWidth);
|
||||
};
|
||||
|
||||
win.addEventListener('mouseup', finish);
|
||||
win.addEventListener('mousemove', move);
|
||||
|
||||
updateColumnNodeWidth(view, nodePos, nodeAttrs, domWidth);
|
||||
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function handleGridDecorations(
|
||||
state: EditorState,
|
||||
boundaryPos: number,
|
||||
): DecorationSet {
|
||||
const decorations = [];
|
||||
const $pos = state.doc.resolve(boundaryPos);
|
||||
if ($pos.nodeAfter !== null) {
|
||||
const widget = document.createElement('div');
|
||||
widget.className = 'grid-resize-handle';
|
||||
const circleButton = document.createElement('div');
|
||||
circleButton.className = 'circle-button';
|
||||
widget.appendChild(circleButton);
|
||||
const plusIcon = document.createElement('div');
|
||||
plusIcon.className = 'plus';
|
||||
circleButton.appendChild(plusIcon);
|
||||
decorations.push(Decoration.widget(boundaryPos, widget));
|
||||
}
|
||||
return DecorationSet.create(state.doc, decorations);
|
||||
}
|
||||
|
||||
export function handleMouseUp(view: EditorView, event: MouseEvent): boolean {
|
||||
const div = event.target as HTMLElement;
|
||||
if (!div) return false;
|
||||
if (div.className !== 'circle-button' && div.className !== 'plus')
|
||||
return false;
|
||||
const column = div.closest('.prosemirror-column');
|
||||
if (!column) return false;
|
||||
const boundryPos = view.posAtDOM(column, 0);
|
||||
if (!boundryPos) return false;
|
||||
const $pos = view.state.doc.resolve(boundryPos);
|
||||
const { state } = view;
|
||||
view.dispatch(
|
||||
state.tr.insert(
|
||||
$pos.pos + $pos.parent.nodeSize - 1,
|
||||
state.schema.nodes.column.create(
|
||||
{
|
||||
colWidth: 100,
|
||||
},
|
||||
state.schema.nodes.paragraph.create(),
|
||||
),
|
||||
),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export * from './schema';
|
||||
export * from './resize';
|
||||
export * from './keymap';
|
||||
export * from './tiptap';
|
||||
@@ -1,63 +0,0 @@
|
||||
import { liftTarget, canSplit } from "@tiptap/pm/transform";
|
||||
import { TextSelection, Command } from "@tiptap/pm/state";
|
||||
import {
|
||||
splitBlock,
|
||||
chainCommands,
|
||||
newlineInCode,
|
||||
createParagraphNear,
|
||||
} from "@tiptap/pm/commands";
|
||||
import { keymap } from "@tiptap/pm/keymap";
|
||||
import { ResolvedPos } from "@tiptap/pm/model";
|
||||
|
||||
function findParentColumn($pos: ResolvedPos) {
|
||||
for (let depth = $pos.depth; depth > 0; depth--) {
|
||||
const node = $pos.node(depth);
|
||||
if (node.type.name === "column") {
|
||||
return { node, depth };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const liftEmptyBlock: Command = (state, dispatch) => {
|
||||
const { $cursor } = state.selection as TextSelection;
|
||||
if (!$cursor || $cursor.parent.content.size) return false;
|
||||
if ("column" === $cursor.node($cursor.depth - 1).type.name) return false;
|
||||
if ($cursor.depth > 1 && $cursor.after() != $cursor.end(-1)) {
|
||||
const before = $cursor.before();
|
||||
if (canSplit(state.doc, before)) {
|
||||
if (dispatch) dispatch(state.tr.split(before).scrollIntoView());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const range = $cursor.blockRange(),
|
||||
target = range && liftTarget(range);
|
||||
if (target == null) return false;
|
||||
if (dispatch) dispatch(state.tr.lift(range!, target).scrollIntoView());
|
||||
return true;
|
||||
};
|
||||
|
||||
export const columnsKeymap = keymap({
|
||||
Enter: chainCommands(
|
||||
newlineInCode,
|
||||
createParagraphNear,
|
||||
liftEmptyBlock,
|
||||
splitBlock,
|
||||
),
|
||||
"Mod-a": (state, dispatch, view) => {
|
||||
const { selection } = state;
|
||||
const { $from } = selection;
|
||||
const found = findParentColumn($from);
|
||||
if (found) {
|
||||
const { depth } = found;
|
||||
const start = $from.start(depth);
|
||||
const end = $from.end(depth);
|
||||
const tr = state.tr.setSelection(
|
||||
TextSelection.create(state.doc, start, end),
|
||||
);
|
||||
if (dispatch) dispatch(tr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
} as { [key: string]: Command });
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Plugin } from '@tiptap/pm/state';
|
||||
import {
|
||||
handleGridDecorations,
|
||||
handleMouseDown,
|
||||
handleMouseLeave,
|
||||
handleMouseMove,
|
||||
handleMouseUp,
|
||||
} from './dom';
|
||||
import { GridResizeState, gridResizingPluginKey } from './state';
|
||||
|
||||
export function gridResizingPlugin(options?: {
|
||||
handleWidth?: number;
|
||||
columnMinWidth?: number;
|
||||
}) {
|
||||
const handleWidth = options?.handleWidth ?? 2;
|
||||
const columnMinWidth = options?.columnMinWidth ?? 50;
|
||||
|
||||
return new Plugin<GridResizeState>({
|
||||
key: gridResizingPluginKey,
|
||||
|
||||
state: {
|
||||
init: () => new GridResizeState(-1, false),
|
||||
apply: (tr, prev) => {
|
||||
return prev.apply(tr);
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
attributes: (state): Record<string, string> => {
|
||||
const pluginState = gridResizingPluginKey.getState(state);
|
||||
if (pluginState && pluginState.activeHandle > -1) {
|
||||
return { class: 'resize-cursor' };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
// The main event handlers
|
||||
handleDOMEvents: {
|
||||
mousemove: (view, event: MouseEvent) => {
|
||||
return handleMouseMove(view, event, handleWidth);
|
||||
},
|
||||
mouseleave: (view) => {
|
||||
return handleMouseLeave(view);
|
||||
},
|
||||
mousedown: (view, event: MouseEvent) => {
|
||||
return handleMouseDown(view, event, columnMinWidth);
|
||||
},
|
||||
mouseup: (view, event: MouseEvent) => {
|
||||
return handleMouseUp(view, event);
|
||||
},
|
||||
},
|
||||
|
||||
decorations: (state) => {
|
||||
const pluginState = gridResizingPluginKey.getState(state);
|
||||
if (!pluginState) return null;
|
||||
if (pluginState.activeHandle === -1) return null;
|
||||
|
||||
return handleGridDecorations(state, pluginState.activeHandle);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { NodeSpec } from '@tiptap/pm/model';
|
||||
|
||||
export type ColumnNodes = Record<'column' | 'column_container', NodeSpec>;
|
||||
|
||||
export function columnNodes(): ColumnNodes {
|
||||
return {
|
||||
column: {
|
||||
group: 'block',
|
||||
content: 'block+',
|
||||
attrs: {
|
||||
colWidth: { default: 200 },
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'div.prosemirror-column',
|
||||
getAttrs(dom) {
|
||||
if (!(dom instanceof HTMLElement)) return false;
|
||||
const width = dom.style.width.replace('px', '') || 200;
|
||||
return {
|
||||
colWidth: width,
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
toDOM(node) {
|
||||
const { colWidth } = node.attrs;
|
||||
const style = colWidth ? `width: ${colWidth}px;` : '';
|
||||
return [
|
||||
'div',
|
||||
{
|
||||
class: 'prosemirror-column',
|
||||
style,
|
||||
},
|
||||
0,
|
||||
];
|
||||
},
|
||||
},
|
||||
column_container: {
|
||||
group: 'block',
|
||||
content: 'column+',
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'div.prosemirror-column-container',
|
||||
},
|
||||
],
|
||||
toDOM() {
|
||||
return ['div', { class: 'prosemirror-column-container' }, 0];
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { PluginKey, Transaction } from '@tiptap/pm/state';
|
||||
|
||||
export const gridResizingPluginKey = new PluginKey<GridResizeState>(
|
||||
'gridResizingPlugin',
|
||||
);
|
||||
|
||||
export type Dragging = {
|
||||
startX: number;
|
||||
startWidth: number;
|
||||
};
|
||||
|
||||
export class GridResizeState {
|
||||
constructor(
|
||||
public activeHandle: number,
|
||||
public dragging: Dragging | false,
|
||||
) {}
|
||||
|
||||
apply(tr: Transaction): GridResizeState {
|
||||
const action = tr.getMeta(gridResizingPluginKey);
|
||||
if (!action) return this;
|
||||
|
||||
if (typeof action.setHandle === 'number') {
|
||||
return new GridResizeState(action.setHandle, false);
|
||||
}
|
||||
if (action.setDragging !== undefined) {
|
||||
return new GridResizeState(this.activeHandle, action.setDragging);
|
||||
}
|
||||
if (this.activeHandle > -1 && tr.docChanged) {
|
||||
// remap when doc changes
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import { Node, mergeAttributes, Extension } from '@tiptap/core';
|
||||
import { columnsKeymap } from './keymap';
|
||||
import { gridResizingPlugin } from './resize';
|
||||
|
||||
const Column = Node.create({
|
||||
name: 'column',
|
||||
|
||||
group: 'block',
|
||||
content: 'block+',
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
colWidth: {
|
||||
default: 200,
|
||||
parseHTML: (element) => {
|
||||
const width = (element as HTMLElement).style.width.replace('px', '');
|
||||
return Number(width) || 200;
|
||||
},
|
||||
renderHTML: (attributes) => {
|
||||
const style = attributes.colWidth
|
||||
? `width: ${attributes.colWidth}px;`
|
||||
: '';
|
||||
return { style };
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'div.prosemirror-column',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
'div',
|
||||
mergeAttributes(HTMLAttributes, { class: 'prosemirror-column' }),
|
||||
0,
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
const ColumnContainer = Node.create({
|
||||
name: 'column_container',
|
||||
|
||||
group: 'block',
|
||||
content: 'column+',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'div.prosemirror-column-container',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
'div',
|
||||
mergeAttributes(HTMLAttributes, {
|
||||
class: 'prosemirror-column-container',
|
||||
}),
|
||||
0,
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
export const ColumnsExtension = Extension.create({
|
||||
name: 'columns',
|
||||
|
||||
addExtensions() {
|
||||
return [Column, ColumnContainer];
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
gridResizingPlugin({ handleWidth: 2, columnMinWidth: 50 }),
|
||||
columnsKeymap,
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -1,75 +0,0 @@
|
||||
import { EditorView } from '@tiptap/pm/view';
|
||||
import { type Dragging } from './state';
|
||||
|
||||
export function findBoundaryPosition(
|
||||
view: EditorView,
|
||||
event: MouseEvent,
|
||||
handleWidth: number,
|
||||
): number {
|
||||
const gridDOM = event
|
||||
.composedPath()
|
||||
.find((el) =>
|
||||
(el as HTMLElement).classList?.contains('prosemirror-column-container'),
|
||||
) as HTMLElement | undefined;
|
||||
if (!gridDOM) return -1;
|
||||
|
||||
const children = Array.from(gridDOM.children).filter((el) =>
|
||||
el.classList.contains('prosemirror-column'),
|
||||
);
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const colEl = children[i] as HTMLElement;
|
||||
const rect = colEl.getBoundingClientRect();
|
||||
if (
|
||||
event.clientX >= rect.right - handleWidth - 2 &&
|
||||
event.clientX <= rect.right + 10 + handleWidth
|
||||
) {
|
||||
const pos = view.posAtDOM(colEl, 0);
|
||||
if (pos != null) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function draggedWidth(
|
||||
dragging: Dragging,
|
||||
event: MouseEvent,
|
||||
minWidth: number,
|
||||
): number {
|
||||
const offset = event.clientX - dragging.startX;
|
||||
return Math.max(minWidth, dragging.startWidth + offset);
|
||||
}
|
||||
|
||||
export function updateColumnNodeWidth(
|
||||
view: EditorView,
|
||||
pos: number,
|
||||
attrs: Record<string, string>,
|
||||
width: number,
|
||||
) {
|
||||
view.dispatch(
|
||||
view.state.tr.setNodeMarkup(pos, undefined, {
|
||||
...attrs,
|
||||
colWidth: width - 12 * 2,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getColumnInfoAtPos(view: EditorView, boundaryPos: number) {
|
||||
const $pos = view.state.doc.resolve(boundaryPos);
|
||||
const node = $pos.parent;
|
||||
if (!node || node.type.name !== 'column') return null;
|
||||
|
||||
const dom = view.domAtPos($pos.pos);
|
||||
if (!dom.node) return null;
|
||||
|
||||
const columnEl =
|
||||
dom.node instanceof HTMLElement
|
||||
? dom.node
|
||||
: (dom.node.childNodes[dom.offset] as HTMLElement);
|
||||
|
||||
const domWidth = columnEl.offsetWidth;
|
||||
|
||||
return { $pos, node, columnEl, domWidth };
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { HorizontalRule as TiptapHorizontalRule } from "@tiptap/extension-horizontal-rule";
|
||||
|
||||
export type HorizontalRuleType = "pageBreak";
|
||||
|
||||
export const HorizontalRule = TiptapHorizontalRule.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
type: {
|
||||
default: null,
|
||||
parseHTML: (element) => element.getAttribute("data-type"),
|
||||
renderHTML: (attributes) => {
|
||||
if (attributes.type) {
|
||||
return {
|
||||
"data-type": attributes.type,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -165,7 +165,6 @@ export const Mention = Node.create<MentionOptions>({
|
||||
inline: true,
|
||||
selectable: true,
|
||||
atom: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
|
||||
@@ -28,7 +28,7 @@ export const Subpages = Node.create<SubpagesOptions>({
|
||||
|
||||
group: "block",
|
||||
atom: true,
|
||||
draggable: true,
|
||||
draggable: false,
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Editor, findParentNode, isTextSelection } from "@tiptap/core";
|
||||
import { Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { CellSelection, TableMap } from "@tiptap/pm/tables";
|
||||
import { Node, ResolvedPos } from "@tiptap/pm/model";
|
||||
import Table from "@tiptap/extension-table";
|
||||
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
@@ -288,7 +289,7 @@ export const isColumnGripSelected = ({
|
||||
const node = nodeDOM || domAtPos;
|
||||
|
||||
if (
|
||||
!editor.isActive("table") ||
|
||||
!editor.isActive(Table.name) ||
|
||||
!node ||
|
||||
isTableSelected(state.selection)
|
||||
) {
|
||||
|
||||
Generated
+18
-16
@@ -6,7 +6,6 @@ settings:
|
||||
|
||||
overrides:
|
||||
jsdom: 25.0.1
|
||||
jsonwebtoken: 9.0.3
|
||||
y-prosemirror: 1.3.7
|
||||
|
||||
patchedDependencies:
|
||||
@@ -78,6 +77,9 @@ importers:
|
||||
'@tiptap/extension-history':
|
||||
specifier: 2.27.1
|
||||
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)
|
||||
'@tiptap/extension-horizontal-rule':
|
||||
specifier: 2.27.1
|
||||
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)
|
||||
'@tiptap/extension-image':
|
||||
specifier: 2.27.1
|
||||
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
|
||||
@@ -558,8 +560,8 @@ importers:
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1
|
||||
jsonwebtoken:
|
||||
specifier: 9.0.3
|
||||
version: 9.0.3
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
kysely:
|
||||
specifier: ^0.28.2
|
||||
version: 0.28.2
|
||||
@@ -7405,8 +7407,8 @@ packages:
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
jsonwebtoken@9.0.3:
|
||||
resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==}
|
||||
jsonwebtoken@9.0.2:
|
||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
|
||||
jsx-ast-utils@3.3.5:
|
||||
@@ -7416,11 +7418,11 @@ packages:
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
jwa@2.0.1:
|
||||
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
|
||||
jwa@1.4.1:
|
||||
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
||||
|
||||
jws@4.0.1:
|
||||
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
|
||||
jws@3.2.2:
|
||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
||||
|
||||
jwt-decode@4.0.0:
|
||||
resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==}
|
||||
@@ -13414,7 +13416,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@types/jsonwebtoken': 9.0.7
|
||||
jsonwebtoken: 9.0.3
|
||||
jsonwebtoken: 9.0.2
|
||||
|
||||
'@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)':
|
||||
dependencies:
|
||||
@@ -18471,9 +18473,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jsonwebtoken@9.0.3:
|
||||
jsonwebtoken@9.0.2:
|
||||
dependencies:
|
||||
jws: 4.0.1
|
||||
jws: 3.2.2
|
||||
lodash.includes: 4.3.0
|
||||
lodash.isboolean: 3.0.3
|
||||
lodash.isinteger: 4.0.4
|
||||
@@ -18498,15 +18500,15 @@ snapshots:
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
|
||||
jwa@2.0.1:
|
||||
jwa@1.4.1:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
jws@4.0.1:
|
||||
jws@3.2.2:
|
||||
dependencies:
|
||||
jwa: 2.0.1
|
||||
jwa: 1.4.1
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
jwt-decode@4.0.0: {}
|
||||
@@ -19479,7 +19481,7 @@ snapshots:
|
||||
|
||||
passport-jwt@4.0.1:
|
||||
dependencies:
|
||||
jsonwebtoken: 9.0.3
|
||||
jsonwebtoken: 9.0.2
|
||||
passport-strategy: 1.0.0
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
|
||||
Reference in New Issue
Block a user