Browse Source

Improve chat dialog and connect form

* Fix chat dialog and connect form.

* Fix trusted flag
pull/1023/head
Sebastian Stehle 3 years ago
committed by GitHub
parent
commit
d778c17bce
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      backend/i18n/frontend_en.json
  2. 26
      backend/i18n/frontend_fr.json
  3. 14
      backend/i18n/frontend_it.json
  4. 14
      backend/i18n/frontend_nl.json
  5. 14
      backend/i18n/frontend_pt.json
  6. 14
      backend/i18n/frontend_zh.json
  7. 14
      backend/i18n/source/frontend_en.json
  8. 16
      backend/i18n/source/frontend_fr.json
  9. 5
      backend/i18n/source/frontend_it.json
  10. 5
      backend/i18n/source/frontend_nl.json
  11. 5
      backend/i18n/source/frontend_pt.json
  12. 5
      backend/i18n/source/frontend_zh.json
  13. 2
      frontend/src/app/app.component.html
  14. 104
      frontend/src/app/features/content/shared/forms/chat-dialog.component.html
  15. 60
      frontend/src/app/features/content/shared/forms/chat-dialog.component.scss
  16. 47
      frontend/src/app/features/content/shared/forms/chat-dialog.component.ts
  17. 2
      frontend/src/app/features/content/shared/forms/field-editor.component.ts
  18. 145
      frontend/src/app/features/settings/pages/clients/client-connect-form.component.html
  19. 50
      frontend/src/app/features/settings/pages/clients/client-connect-form.component.scss
  20. 19
      frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts
  21. 2
      frontend/src/app/features/settings/pages/clients/client.component.ts
  22. 6
      frontend/src/app/framework/angular/code.component.html
  23. 34
      frontend/src/app/framework/angular/code.component.scss
  24. 5
      frontend/src/app/framework/angular/code.component.ts
  25. 91
      frontend/src/app/framework/angular/forms/copy-global.directive.ts
  26. 5
      frontend/src/app/framework/angular/markdown.directive.ts
  27. 1
      frontend/src/app/framework/declarations.ts
  28. 4
      frontend/src/app/framework/module.ts
  29. 23
      frontend/src/app/framework/utils/markdown.ts
  30. 42
      frontend/src/app/shared/services/help.service.spec.ts
  31. 24
      frontend/src/app/shared/services/help.service.ts
  32. 38
      frontend/src/app/theme/_common.scss

14
backend/i18n/frontend_en.json

@ -166,6 +166,7 @@
"backups.started": "Backup started, it can take several minutes to complete.",
"backups.startedLabel": "Started",
"backups.startFailed": "Failed to start backup.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "Add your app name the CLI config",
"clients.connectWizard.cliStep3Hint": "You can manage configuration to multiple apps in the CLI and switch to an app.",
"clients.connectWizard.cliStep4": "Switch to your app in the CLI",
"clients.connectWizard.dotnetSdk": "Use the .NET SDK",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "Install the .NET SDK and establish a connection to this app.",
"clients.connectWizard.dotnetSdkStep1": "Install the .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "The SDK is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Version 14 and before: Create a client manager",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Add the token as HTTP header to all requests",
"clients.connectWizard.manuallyTokenHint": "Tokens usally expire after 30days, but you can request multiple tokens.",
"clients.connectWizard.postManDocs": "Start with the Postman tutorial in the [Documentation](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "You need another SDK?",
"clients.connectWizard.sdkHelpLink": "Contact us in the Support Forum",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Choose connection method",
"clients.connectWizard.step2Title": "Connect",
@ -387,6 +380,7 @@
"common.remember": "Don't ask again",
"common.rename": "Rename",
"common.renameTag": "Rename Tag",
"common.repository": "Repository",
"common.requiredHint": "required",
"common.reset": "Reset",
"common.restore": "Restore",

26
backend/i18n/frontend_fr.json

@ -166,6 +166,7 @@
"backups.started": "La sauvegarde a commencé, cela peut prendre plusieurs minutes.",
"backups.startedLabel": "Commencé",
"backups.startFailed": "Échec du démarrage de la sauvegarde.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,22 +195,12 @@
"clients.connectWizard.cliStep3": "Ajoutez le nom de votre application à la configuration CLI",
"clients.connectWizard.cliStep3Hint": "Vous pouvez gérer la configuration de plusieurs applications dans l'interface de ligne de commande et basculer vers une application.",
"clients.connectWizard.cliStep4": "Basculez vers votre application dans la CLI",
"clients.connectWizard.dotnetSdk": "Utiliser le SDK .NET",
"clients.connectWizard.dotnetSdkDocumentation": "Les documentations pour le SDK .NET sont disponibles\u00A0:",
"clients.connectWizard.dotnetSdkHint": "Installez le SDK .NET et établissez une connexion à cette application.",
"clients.connectWizard.dotnetSdkStep1": "Installer le SDK .NET",
"clients.connectWizard.dotnetSdkStep1Download": "Le SDK est disponible sur [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Version 14 et antérieure : Créer un gestionnaire de clientèle",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 et supérieures : Créer un client",
"clients.connectWizard.dotnetSdkStep3": "Facultatif\u00A0: installez les extensions de service pour le SDK",
"clients.connectWizard.dotnetSdkStep3Download": "L'extension SDK est disponible sur [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Facultativement\u00A0: Enregistrez le gestionnaire de clientèle et tous les clients",
"clients.connectWizard.javascriptSdk": "Utiliser le SDK JavaScript",
"clients.connectWizard.javascriptSdkDocumentation": "Les documentations pour le SDK JavaScript sont disponibles\u00A0:",
"clients.connectWizard.javascriptSdkHint": "Installez le SDK et établissez une connexion à cette application.",
"clients.connectWizard.javascriptSdkStep1": "Installer le SDK Javascript",
"clients.connectWizard.javascriptSdkStep1Download": "Le SDK est disponible sur [npm](https://www.npmjs.com/package/@squidex/squidex",
"clients.connectWizard.javascriptSdkStep2": "Créer un client",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.javascriptSdkStep1": "Install the Javascript SDK",
"clients.connectWizard.javascriptSdkStep1Download": "The SDK is available on [npm](https://www.npmjs.com/package/@squidex/squidex)",
"clients.connectWizard.javascriptSdkStep2": "Create a client",
"clients.connectWizard.manually": "Connectez-vous manuellement",
"clients.connectWizard.manuallyHint": "Obtenez des instructions pour établir une connexion avec Postman ou curl.",
"clients.connectWizard.manuallyStep1": "Obtenir un jeton en utilisant curl",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Ajouter le jeton en tant qu'en-tête HTTP à toutes les requêtes",
"clients.connectWizard.manuallyTokenHint": "Les jetons expirent généralement après 30 jours, mais vous pouvez demander plusieurs jetons.",
"clients.connectWizard.postManDocs": "Commencez par le tutoriel Postman dans la [Documentation](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "Vous avez besoin d'un autre SDK\u00A0?",
"clients.connectWizard.sdkHelpLink": "Contactez-nous sur le forum d'assistance",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Configurer le client",
"clients.connectWizard.step1Title": "Choisissez la méthode de connexion",
"clients.connectWizard.step2Title": "Connecter",
@ -387,6 +380,7 @@
"common.remember": "Ne demande plus",
"common.rename": "Renommer",
"common.renameTag": "Renommer la balise",
"common.repository": "Repository",
"common.requiredHint": "requis",
"common.reset": "Réinitialiser",
"common.restore": "Restaurer",

14
backend/i18n/frontend_it.json

@ -166,6 +166,7 @@
"backups.started": "Backup avviato, il suo completamento potrebbe richiedere alcuni minuti.",
"backups.startedLabel": "Avviato",
"backups.startFailed": "Non è stato possibile avviare il backup.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "Inserisci il nome della tua app per la configurazione della CLI",
"clients.connectWizard.cliStep3Hint": "È possibile gestire le configurazione per le diverse appi all'interno della CLI e passare ad un'app.",
"clients.connectWizard.cliStep4": "Passa alla tua app usando CLI",
"clients.connectWizard.dotnetSdk": "Connetti la tua APP utilizzando SDK",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "Scarica l'SDK e connetti quest'app.",
"clients.connectWizard.dotnetSdkStep1": "Installa .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "L'SDK è disponibile su [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Crea un client manager",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Aggiungi il token come header HTTP header a tutte le richieste",
"clients.connectWizard.manuallyTokenHint": "Solitamente i Token scadono dopo 30 giorni, ma puoi richiedere token multipli.",
"clients.connectWizard.postManDocs": "Per il tutorial Postman inizia da questo link [Documentazione](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "Hai bisogno di un altro SDK?",
"clients.connectWizard.sdkHelpLink": "Contattaci nel Forum di assistenza",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Scegli la tipologia di connessione",
"clients.connectWizard.step2Title": "Collega",
@ -387,6 +380,7 @@
"common.remember": "Ricorda la mia decisione",
"common.rename": "Rinomina",
"common.renameTag": "Rename Tag",
"common.repository": "Repository",
"common.requiredHint": "obbligatorio",
"common.reset": "Reimposta",
"common.restore": "Ripristina",

14
backend/i18n/frontend_nl.json

@ -166,6 +166,7 @@
"backups.started": "Back-up gestart, het kan enkele minuten duren om te voltooien.",
"backups.startedLabel": "Gestart",
"backups.startFailed": "Starten van back-up is mislukt.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "Voeg uw app-naam toe aan de CLI-configuratie",
"clients.connectWizard.cliStep3Hint": "Je kunt de configuratie voor meerdere apps in de CLI beheren en overschakelen naar een app.",
"clients.connectWizard.cliStep4": "Schakel over naar uw app in de CLI",
"clients.connectWizard.dotnetSdk": "Maak verbinding met uw app met SDK",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "Download een SDK en maak verbinding met deze app.",
"clients.connectWizard.dotnetSdkStep1": "Installeer de .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "De SDK is beschikbaar op [nuget] (https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Maak een klantenbeheerder",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Voeg het token toe als HTTP-header aan alle verzoeken",
"clients.connectWizard.manuallyTokenHint": "Tokens vervallen gewoonlijk na 30 dagen, maar je kunt meerdere tokens aanvragen.",
"clients.connectWizard.postManDocs": "Begin met de Postman-tutorial in de [Documentatie] (https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "Heb je een andere SDK nodig?",
"clients.connectWizard.sdkHelpLink": "Neem contact met ons op in het ondersteuningsforum",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Client instellen",
"clients.connectWizard.step1Title": "Kies verbindingsmethode",
"clients.connectWizard.step2Title": "Verbinden",
@ -387,6 +380,7 @@
"common.remember": "Onthoud mijn keuze",
"common.rename": "Hernoemen",
"common.renameTag": "Hernoem Tag",
"common.repository": "Repository",
"common.requiredHint": "verplicht",
"common.reset": "Reset",
"common.restore": "Herstellen",

14
backend/i18n/frontend_pt.json

@ -166,6 +166,7 @@
"backups.started": "O reforço começou, pode levar vários minutos para ser concluído.",
"backups.startedLabel": "Começou",
"backups.startFailed": "Falhou em começar o backup.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "Adicione o nome da sua aplicação ao CLI config",
"clients.connectWizard.cliStep3Hint": "Pode gerir a configuração de várias aplicações no CLI e mudar para uma aplicação.",
"clients.connectWizard.cliStep4": "Mude para a sua aplicação no CLI",
"clients.connectWizard.dotnetSdk": "Conecte-se à sua App com a SDK",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "Descarregue um SDK e estabeleça uma ligação a esta aplicação.",
"clients.connectWizard.dotnetSdkStep1": "Instale o .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "O SDK está disponível em [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Criar um gestor de clientes",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Adicione o token como cabeçalho HTTP a todos os pedidos",
"clients.connectWizard.manuallyTokenHint": "Tokens normalmente expiram após 30 dias, mas você pode solicitar várias tokens.",
"clients.connectWizard.postManDocs": "Comece com o tutorial do Carteiro na [Documentação](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "Precisa de outro SDK?",
"clients.connectWizard.sdkHelpLink": "Contacte-nos no Fórum de Apoio",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Cliente de configuração",
"clients.connectWizard.step1Title": "Escolha o método de ligação",
"clients.connectWizard.step2Title": "Ligar",
@ -387,6 +380,7 @@
"common.remember": "Não pergunte de novo.",
"common.rename": "Renomear",
"common.renameTag": "Renomear Etiqueta",
"common.repository": "Repository",
"common.requiredHint": "Necessário",
"common.reset": "Reset",
"common.restore": "Restaurar",

14
backend/i18n/frontend_zh.json

@ -166,6 +166,7 @@
"backups.started": "备份已开始,可能需要几分钟才能完成。",
"backups.startedLabel": "开始",
"backups.startFailed": "启动备份失败。",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "在 CLI 配置中添加你的应用名称",
"clients.connectWizard.cliStep3Hint": "您可以在 CLI 中管理多个应用程序的配置并切换到一个应用程序。",
"clients.connectWizard.cliStep4": "在 CLI 中切换到您的应用程序",
"clients.connectWizard.dotnetSdk": "使用 SDK 连接到您的应用程序",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "下载 SDK 并建立与此应用程序的连接。",
"clients.connectWizard.dotnetSdkStep1": "安装.NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "SDK 可在 [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "创建客户端管理器",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "将令牌作为 HTTP 标头添加到所有请求中",
"clients.connectWizard.manuallyTokenHint": "令牌通常会在 30 天后过期,但您可以请求多个令牌。",
"clients.connectWizard.postManDocs": "从 [文档](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman) 中的 Postman 教程开始。",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "你需要另一个 SDK?",
"clients.connectWizard.sdkHelpLink": "在支持论坛联系我们",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "设置客户端",
"clients.connectWizard.step1Title": "选择连接方式",
"clients.connectWizard.step2Title": "连接",
@ -387,6 +380,7 @@
"common.remember": "不要再问了",
"common.rename": "重命名",
"common.renameTag": "Rename Tag",
"common.repository": "Repository",
"common.requiredHint": "必需的",
"common.reset": "重置",
"common.restore": "恢复",

14
backend/i18n/source/frontend_en.json

@ -166,6 +166,7 @@
"backups.started": "Backup started, it can take several minutes to complete.",
"backups.startedLabel": "Started",
"backups.startFailed": "Failed to start backup.",
"chat.answer": "Here is my answer:",
"chat.answers": "Answers",
"chat.answersEmpty": "The ChatBot does not provide an answer or has not been configured yet.",
"chat.ask": "Ask",
@ -194,16 +195,6 @@
"clients.connectWizard.cliStep3": "Add your app name the CLI config",
"clients.connectWizard.cliStep3Hint": "You can manage configuration to multiple apps in the CLI and switch to an app.",
"clients.connectWizard.cliStep4": "Switch to your app in the CLI",
"clients.connectWizard.dotnetSdk": "Use the .NET SDK",
"clients.connectWizard.dotnetSdkDocumentation": "Documentations for the .NET SDK is available: ",
"clients.connectWizard.dotnetSdkHint": "Install the .NET SDK and establish a connection to this app.",
"clients.connectWizard.dotnetSdkStep1": "Install the .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "The SDK is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Version 14 and before: Create a client manager",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 and later: Create a client",
"clients.connectWizard.dotnetSdkStep3": "Optionally: Install the Service Extensions for the SDK",
"clients.connectWizard.dotnetSdkStep3Download": "The SDK Extension is available on [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Optionally: Register the client manager and all clients",
"clients.connectWizard.javascriptSdk": "Use the JavaScript SDK",
"clients.connectWizard.javascriptSdkDocumentation": "Documentations for the JavaScript SDK is available: ",
"clients.connectWizard.javascriptSdkHint": "Install the SDK and establish a connection to this app.",
@ -217,8 +208,10 @@
"clients.connectWizard.manuallyStep3": "Add the token as HTTP header to all requests",
"clients.connectWizard.manuallyTokenHint": "Tokens usally expire after 30days, but you can request multiple tokens.",
"clients.connectWizard.postManDocs": "Start with the Postman tutorial in the [Documentation](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Use the official SDK",
"clients.connectWizard.sdkHelp": "You need another SDK?",
"clients.connectWizard.sdkHelpLink": "Contact us in the Support Forum",
"clients.connectWizard.sdkHint": "Install the SDK and establish a connection to this app.",
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Choose connection method",
"clients.connectWizard.step2Title": "Connect",
@ -387,6 +380,7 @@
"common.remember": "Don't ask again",
"common.rename": "Rename",
"common.renameTag": "Rename Tag",
"common.repository": "Repository",
"common.requiredHint": "required",
"common.reset": "Reset",
"common.restore": "Restore",

16
backend/i18n/source/frontend_fr.json

@ -183,22 +183,6 @@
"clients.connectWizard.cliStep3": "Ajoutez le nom de votre application à la configuration CLI",
"clients.connectWizard.cliStep3Hint": "Vous pouvez gérer la configuration de plusieurs applications dans l'interface de ligne de commande et basculer vers une application.",
"clients.connectWizard.cliStep4": "Basculez vers votre application dans la CLI",
"clients.connectWizard.dotnetSdk": "Utiliser le SDK .NET",
"clients.connectWizard.dotnetSdkDocumentation": "Les documentations pour le SDK .NET sont disponibles\u00A0:",
"clients.connectWizard.dotnetSdkHint": "Installez le SDK .NET et établissez une connexion à cette application.",
"clients.connectWizard.dotnetSdkStep1": "Installer le SDK .NET",
"clients.connectWizard.dotnetSdkStep1Download": "Le SDK est disponible sur [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Version 14 et antérieure : Créer un gestionnaire de clientèle",
"clients.connectWizard.dotnetSdkStep2_15": "Version 15 et supérieures : Créer un client",
"clients.connectWizard.dotnetSdkStep3": "Facultatif\u00A0: installez les extensions de service pour le SDK",
"clients.connectWizard.dotnetSdkStep3Download": "L'extension SDK est disponible sur [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary.ServiceExtensions/)",
"clients.connectWizard.dotnetSdkStep4": "Facultativement\u00A0: Enregistrez le gestionnaire de clientèle et tous les clients",
"clients.connectWizard.javascriptSdk": "Utiliser le SDK JavaScript",
"clients.connectWizard.javascriptSdkDocumentation": "Les documentations pour le SDK JavaScript sont disponibles\u00A0:",
"clients.connectWizard.javascriptSdkHint": "Installez le SDK et établissez une connexion à cette application.",
"clients.connectWizard.javascriptSdkStep1": "Installer le SDK Javascript",
"clients.connectWizard.javascriptSdkStep1Download": "Le SDK est disponible sur [npm](https://www.npmjs.com/package/@squidex/squidex",
"clients.connectWizard.javascriptSdkStep2": "Créer un client",
"clients.connectWizard.manually": "Connectez-vous manuellement",
"clients.connectWizard.manuallyHint": "Obtenez des instructions pour établir une connexion avec Postman ou curl.",
"clients.connectWizard.manuallyStep1": "Obtenir un jeton en utilisant curl",

5
backend/i18n/source/frontend_it.json

@ -147,11 +147,6 @@
"clients.connectWizard.cliStep3": "Inserisci il nome della tua app per la configurazione della CLI",
"clients.connectWizard.cliStep3Hint": "È possibile gestire le configurazione per le diverse appi all'interno della CLI e passare ad un'app.",
"clients.connectWizard.cliStep4": "Passa alla tua app usando CLI",
"clients.connectWizard.dotnetSdk": "Connetti la tua APP utilizzando SDK",
"clients.connectWizard.dotnetSdkHint": "Scarica l'SDK e connetti quest'app.",
"clients.connectWizard.dotnetSdkStep1": "Installa .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "L'SDK è disponibile su [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Crea un client manager",
"clients.connectWizard.manually": "Connetti manualmente",
"clients.connectWizard.manuallyHint": "Leggi le istruzioni su come stabilire una connessione utilizzando Postman o curl.",
"clients.connectWizard.manuallyStep1": "Ottenere un token usando curl",

5
backend/i18n/source/frontend_nl.json

@ -169,11 +169,6 @@
"clients.connectWizard.cliStep3": "Voeg uw app-naam toe aan de CLI-configuratie",
"clients.connectWizard.cliStep3Hint": "Je kunt de configuratie voor meerdere apps in de CLI beheren en overschakelen naar een app.",
"clients.connectWizard.cliStep4": "Schakel over naar uw app in de CLI",
"clients.connectWizard.dotnetSdk": "Maak verbinding met uw app met SDK",
"clients.connectWizard.dotnetSdkHint": "Download een SDK en maak verbinding met deze app.",
"clients.connectWizard.dotnetSdkStep1": "Installeer de .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "De SDK is beschikbaar op [nuget] (https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Maak een klantenbeheerder",
"clients.connectWizard.manually": "Handmatig verbinden",
"clients.connectWizard.manuallyHint": "Krijg instructies om een ​​verbinding tot stand te brengen met Postman of curl.",
"clients.connectWizard.manuallyStep1": "Verkrijg een token met curl",

5
backend/i18n/source/frontend_pt.json

@ -180,11 +180,6 @@
"clients.connectWizard.cliStep3": "Adicione o nome da sua aplicação ao CLI config",
"clients.connectWizard.cliStep3Hint": "Pode gerir a configuração de várias aplicações no CLI e mudar para uma aplicação.",
"clients.connectWizard.cliStep4": "Mude para a sua aplicação no CLI",
"clients.connectWizard.dotnetSdk": "Conecte-se à sua App com a SDK",
"clients.connectWizard.dotnetSdkHint": "Descarregue um SDK e estabeleça uma ligação a esta aplicação.",
"clients.connectWizard.dotnetSdkStep1": "Instale o .NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "O SDK está disponível em [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "Criar um gestor de clientes",
"clients.connectWizard.manually": "Conecte-se manualmente",
"clients.connectWizard.manuallyHint": "Obtenha instruções sobre como estabelecer uma ligação com o Carteiro ou o caracol.",
"clients.connectWizard.manuallyStep1": "Obter um símbolo usando caracóis",

5
backend/i18n/source/frontend_zh.json

@ -157,11 +157,6 @@
"clients.connectWizard.cliStep3": "在 CLI 配置中添加你的应用名称",
"clients.connectWizard.cliStep3Hint": "您可以在 CLI 中管理多个应用程序的配置并切换到一个应用程序。",
"clients.connectWizard.cliStep4": "在 CLI 中切换到您的应用程序",
"clients.connectWizard.dotnetSdk": "使用 SDK 连接到您的应用程序",
"clients.connectWizard.dotnetSdkHint": "下载 SDK 并建立与此应用程序的连接。",
"clients.connectWizard.dotnetSdkStep1": "安装.NET SDK",
"clients.connectWizard.dotnetSdkStep1Download": "SDK 可在 [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.dotnetSdkStep2": "创建客户端管理器",
"clients.connectWizard.manually": "手动连接",
"clients.connectWizard.manuallyHint": "获取如何与 Postman 或 curl 建立连接的说明。",
"clients.connectWizard.manuallyStep1": "使用 curl 获取令牌",

2
frontend/src/app/app.component.html

@ -1,4 +1,4 @@
<main>
<main sqxCopyGlobal>
<sqx-root-view>
<router-outlet (activate)="isLoaded = true">
<div class="loading" *ngIf="!isLoaded">

104
frontend/src/app/features/content/shared/forms/chat-dialog.component.html

@ -1,17 +1,84 @@
<sqx-modal-dialog size="lg" (close)="close.emit()">
<sqx-modal-dialog size="lg" (close)="close.emit()" fullHeight="true">
<ng-container title>
{{ 'chat.title' | sqxTranslate }}
</ng-container>
<ng-container content>
<sqx-form-alert marginBottom="4">
<span [sqxMarkdown]="'chat.description' | sqxTranslate" inline="true"></span>
</sqx-form-alert>
<div class="item">
<div class="row">
<div class="col-auto">
<div class="squid squid-sm d-flex align-items-center justify-content-center">
<img src="./images/squid.svg">
</div>
</div>
<div class="col">
<div class="bubble bubble-right">
<span [sqxMarkdown]="'chat.description' | sqxTranslate" inline="false"></span>
</div>
</div>
</div>
</div>
<div class="item" *ngFor="let item of snapshot.chatTalk">
<div class="row mt-3" *ngIf="item.isUser">
<div class="col">
<div class="bubble bubble-left">
{{item.prompt}}
</div>
</div>
<div class="col-auto">
<img class="user-picture" title="{{user.displayName}}" [src]="user.id | sqxUserIdPicture" />
</div>
</div>
<div class="row mt-3" *ngIf="!item.isUser">
<div class="col-auto">
<div class="squid squid-sm d-flex align-items-center justify-content-center">
<img src="./images/squid.svg">
</div>
</div>
<div class="col">
<div class="bubble bubble-right">
<div class="mb-2">
{{ 'chat.answer' | sqxTranslate}}
</div>
<textarea class="form-control" readonly [ngModel]="item.prompt"></textarea>
<div class="mt-2">
<button type="button" class="btn btn-primary" (click)="select.emit(item.prompt)">
{{ 'chat.use' | sqxTranslate }}
</button>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-3" *ngIf="snapshot.isRunning">
<div class="col-auto">
<div class="squid squid-sm d-flex align-items-center justify-content-center">
<img src="./images/squid.svg">
</div>
</div>
<div class="col">
<div class="bubble bubble-right">
<svg height="40" width="40" class="loader">
<circle class="dot" cx="10" cy="20" r="3" />
<circle class="dot" cx="20" cy="20" r="3" />
<circle class="dot" cx="30" cy="20" r="3" />
</svg>
</div>
</div>
</div>
</ng-container>
<ng-container footer>
<form (ngSubmit)="ask()">
<div class="row row-cols-0 g-2">
<div class="col">
<input class="form-control" placeholder="{{ 'chat.prompt' | sqxTranslate }}"
<input class="form-control" #input
placeholder="{{ 'chat.prompt' | sqxTranslate }}"
sqxFocusOnInit
(ngModelChange)="setQuestion($event)"
[ngModel]="snapshot.chatQuestion"
@ -25,32 +92,5 @@
</div>
</div>
</form>
<ng-container *ngIf="!snapshot.isRunning; else loading">
<ng-container *ngIf="snapshot.chatAnswers">
<h4 class="mt-4">{{ 'chat.answers' | sqxTranslate }}</h4>
<div *ngIf="snapshot.chatAnswers?.length === 0">
<small class="text-muted">{{ 'chat.answersEmpty' | sqxTranslate }}</small>
</div>
<div class="row g-2 answer" *ngFor="let answer of (snapshot.chatAnswers || [])">
<div class="col">
<textarea class="form-control" readonly [ngModel]="answer"></textarea>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary" (click)="select.emit(answer)">
{{ 'chat.use' | sqxTranslate }}
</button>
</div>
</div>
</ng-container>
</ng-container>
<ng-template #loading>
<div class="mt-4">
<sqx-loader></sqx-loader>
</div>
</ng-template>
</ng-container>
</sqx-modal-dialog>

60
frontend/src/app/features/content/shared/forms/chat-dialog.component.scss

@ -1,6 +1,66 @@
@import 'mixins';
@import 'vars';
:host ::ng-deep {
.modal-body {
background-color: $color-background;
}
}
form {
width: 100%;
}
textarea {
height: 300px;
}
.flex-reverse {
flex-direction: row-reverse;
}
.bubble {
background-color: $color-white;
border: 0;
border-radius: $border-radius;
padding: 1rem;
position: relative;
&-right {
&::before {
@include caret-left($color-white, 10px);
@include absolute(.5rem, null, null, -18px);
}
}
&-left {
&::before {
@include caret-right($color-white, 10px);
@include absolute(.5rem, -18px);
}
}
}
@keyframes blink {
50% {
fill: transparent
}
}
.dot {
animation: 1s blink infinite;
}
svg {
.dot {
fill: $color-border;
}
}
.dot:nth-child(2) {
animation-delay: 250ms;
}
.dot:nth-child(3) {
animation-delay: 500ms;
}

47
frontend/src/app/features/content/shared/forms/chat-dialog.component.ts

@ -5,8 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Output } from '@angular/core';
import { AppsState, StatefulComponent, TranslationsService } from '@app/shared';
import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
import { AppsState, AuthService, StatefulComponent, TranslationsService } from '@app/shared';
interface State {
// True, when running
@ -16,7 +16,7 @@ interface State {
chatQuestion: string;
// The answers.
chatAnswers?: ReadonlyArray<string>;
chatTalk: ReadonlyArray<{ prompt: string; isUser?: boolean }>;
}
@Component({
@ -31,14 +31,20 @@ export class ChatDialogComponent extends StatefulComponent<State> {
@Output()
public select = new EventEmitter<string>();
@ViewChild('input', { static: false })
public input!: ElementRef<HTMLInputElement>;
public user = this.authService.user!;
constructor(
private readonly appsState: AppsState,
private readonly authService: AuthService,
private readonly translator: TranslationsService,
) {
super({
isRunning: false,
chatQuestion: '',
chatAnswers: undefined,
chatTalk: [],
});
}
@ -47,15 +53,40 @@ export class ChatDialogComponent extends StatefulComponent<State> {
}
public ask() {
this.next({ isRunning: true });
const prompt = this.snapshot.chatQuestion;
this.translator.ask(this.appsState.appName, { prompt: this.snapshot.chatQuestion })
if (!prompt || prompt.length === 0) {
return;
}
this.next(s => ({
...s,
chatQuestion: '',
chatTalk: [
...s.chatTalk,
{ prompt, isUser: true },
],
isRunning: true,
}));
this.translator.ask(this.appsState.appName, { prompt })
.subscribe({
next: chatAnswers => {
this.next({ chatAnswers, isRunning: false });
this.next(s => ({
...s,
chatTalk: [
...s.chatTalk,
...chatAnswers.map(answer => ({ prompt: answer })),
],
isRunning: false,
}));
setTimeout(() => {
this.input.nativeElement.focus();
}, 100);
},
error: () => {
this.next({ chatAnswers: [], isRunning: false });
this.next({ isRunning: false });
},
complete: () => {
this.next({ isRunning: false });

2
frontend/src/app/features/content/shared/forms/field-editor.component.ts

@ -57,7 +57,7 @@ export class FieldEditorComponent {
public isEmpty?: Observable<boolean>;
public isExpanded = false;
public chatDialog = new DialogModel();
public chatDialog = new DialogModel(true);
public get field() {
return this.formModel.field;

145
frontend/src/app/features/settings/pages/clients/client-connect-form.component.html

@ -9,7 +9,7 @@
<li class="breadcrumb-item done">
<i class="icon-checkmark"></i> {{ 'clients.connectWizard.step0Title' | sqxTranslate }}
</li>
<ng-container *ngIf="isStart; else noStart">
<ng-container *ngIf="step === 'Start'; else noStart">
<li class="breadcrumb-item active">
<span>
<i class="icon-checkmark"></i> {{ 'clients.connectWizard.step1Title' | sqxTranslate }}
@ -23,7 +23,7 @@
</a>
</li>
</ng-template>
<li class="breadcrumb-item" [class.active]="!isStart">
<li class="breadcrumb-item" [class.active]="step !== 'Start'">
<i class="icon-checkmark"></i> {{ 'clients.connectWizard.step2Title' | sqxTranslate }}
</li>
</ol>
@ -54,18 +54,10 @@
<i class="icon-angle-right"></i>
</div>
<div class="option" (click)="go('SDK_Dotnet')">
<h5>{{ 'clients.connectWizard.dotnetSdk' | sqxTranslate }}</h5>
<div class="option" (click)="go('SDK')">
<h5>{{ 'clients.connectWizard.sdk' | sqxTranslate }}</h5>
<sqx-form-hint>{{ 'clients.connectWizard.dotnetSdkHint' | sqxTranslate }}</sqx-form-hint>
<i class="icon-angle-right"></i>
</div>
<div class="option" (click)="go('SDK_Javascript')">
<h5>{{ 'clients.connectWizard.javascriptSdk' | sqxTranslate }}</h5>
<sqx-form-hint>{{ 'clients.connectWizard.javascriptSdkHint' | sqxTranslate }}</sqx-form-hint>
<sqx-form-hint>{{ 'clients.connectWizard.sdkHint' | sqxTranslate }}</sqx-form-hint>
<i class="icon-angle-right"></i>
</div>
@ -144,132 +136,35 @@
</p>
</div>
</ng-container>
<ng-container *ngSwitchCase="'SDK_Dotnet'">
<div class="section step">
<sqx-form-hint>
{{ 'clients.connectWizard.dotnetSdkDocumentation' | sqxTranslate }} <a href="https://docs.squidex.io/02-documentation/software-development-kits/.net-standard" sqxExternalLink>{{ 'common.documentation' | sqxTranslate }}</a>
</sqx-form-hint>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">1</span> {{ 'clients.connectWizard.dotnetSdkStep1' | sqxTranslate }}</h5>
<div [innerHTML]="'clients.connectWizard.dotnetSdkStep1Download' | sqxTranslate | sqxMarkdown | sqxSafeHtml"></div>
<p>
<sqx-code>dotnet add package Squidex.ClientLibrary</sqx-code>
</p>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">2</span> {{ 'clients.connectWizard.dotnetSdkStep2' | sqxTranslate }}</h5>
<p>
<sqx-code>
<div ngPreserveWhitespaces>using Squidex.ClientLibrary;</div>
<div ngPreserveWhitespaces>&nbsp;</div>
<div ngPreserveWhitespaces>var clientManager = new SquidexClientManager(new SquidexOptions</div>
<div ngPreserveWhitespaces>{{'{'}}</div>
<div ngPreserveWhitespaces> AppName = "{{appName}}",</div>
<div ngPreserveWhitespaces> ClientId = "{{appName}}:{{client.id}}",</div>
<div ngPreserveWhitespaces> ClientSecret = "{{client.secret}}",</div>
<div ngPreserveWhitespaces> Url = "{{apiUrl.value}}"</div>
<div ngPreserveWhitespaces>});</div>
</sqx-code>
</p>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">2</span> {{ 'clients.connectWizard.dotnetSdkStep2_15' | sqxTranslate }}</h5>
<p>
<sqx-code>
<div ngPreserveWhitespaces>using Squidex.ClientLibrary;</div>
<div ngPreserveWhitespaces>&nbsp;</div>
<div ngPreserveWhitespaces>var client = new SquidexClient(new SquidexOptions</div>
<div ngPreserveWhitespaces>{{'{'}}</div>
<div ngPreserveWhitespaces> AppName = "{{appName}}",</div>
<div ngPreserveWhitespaces> ClientId = "{{appName}}:{{client.id}},</div>
<div ngPreserveWhitespaces> ClientSecret = "{{client.secret}}",</div>
<div ngPreserveWhitespaces> Url = "{{apiUrl.value}}"</div>
<div ngPreserveWhitespaces>});</div>
</sqx-code>
</p>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">3</span> {{ 'clients.connectWizard.dotnetSdkStep3' | sqxTranslate }}</h5>
<div [innerHTML]="'clients.connectWizard.dotnetSdkStep3Download' | sqxTranslate | sqxMarkdown | sqxSafeHtml"></div>
<p>
<sqx-code>dotnet add package Squidex.ClientLibrary.ServiceExtensions</sqx-code>
</p>
<ng-container *ngSwitchCase="'SDK'">
<div *ngIf="sdks | async; let sdks">
<div class="sdk-header"*ngFor="let availableSDK of sdks | keyvalue" [class.active]="sdk === availableSDK.value" (click)="select(availableSDK.value)">
<div class="logo d-flex align-items-center justify-content-center" [innerHTML]="availableSDK.value.logo | sqxSafeHtml"></div>
{{availableSDK.value.name}}
</div>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">4</span> {{ 'clients.connectWizard.dotnetSdkStep4' | sqxTranslate }}</h5>
<div class="section markdown" *ngIf="sdk">
<div>
<a [attr.href]="sdk.documentation" sqxExternalLink>{{ 'common.documentation' | sqxTranslate }}</a>
<span> &middot; </span>
<a [attr.href]="sdk.repository" sqxExternalLink>{{ 'common.repository' | sqxTranslate }}</a>
</div>
<p>
<sqx-code>
<div ngPreserveWhitespaces>using Microsoft.Extensions.DependencyInjection;</div>
<div ngPreserveWhitespaces>&nbsp;</div>
<div ngPreserveWhitespaces>services.AddSquidex(options =></div>
<div ngPreserveWhitespaces>{{'{'}}</div>
<div ngPreserveWhitespaces> options.AppName = "{{appName}}";</div>
<div ngPreserveWhitespaces> options.ClientId = "{{appName}}:{{client.id}}";</div>
<div ngPreserveWhitespaces> options.ClientSecret = "{{client.secret}}";</div>
<div ngPreserveWhitespaces> options.Url = "{{apiUrl.value}}";</div>
<div ngPreserveWhitespaces>});</div>
</sqx-code>
</p>
<span [sqxMarkdown]="sdk.instructions" inline="false" trusted="true"></span>
</div>
<div class="section step">
<div class="section" *ngIf="sdk">
<sqx-form-hint>
{{ 'clients.connectWizard.sdkHelp' | sqxTranslate }} <a href="https://support.squidex.io" sqxExternalLink>{{ 'clients.connectWizard.sdkHelpLink' | sqxTranslate }}</a>
</sqx-form-hint>
</div>
</ng-container>
<ng-container *ngSwitchCase="'SDK_Javascript'">
<div class="section step">
<sqx-form-hint>
{{ 'clients.connectWizard.javascriptSdkDocumentation' | sqxTranslate }} <a href="https://docs.squidex.io/02-documentation/software-development-kits/javascript" sqxExternalLink>{{ 'common.documentation' | sqxTranslate }}</a>
</sqx-form-hint>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">1</span> {{ 'clients.connectWizard.javascriptSdkStep1' | sqxTranslate }}</h5>
<div [innerHTML]="'clients.connectWizard.javascriptSdkStep1Download' | sqxTranslate | sqxMarkdown | sqxSafeHtml"></div>
<p>
<sqx-code>npm i @squidex/squidex --save</sqx-code>
</p>
</div>
<div class="section step">
<h5><span class="badge rounded-pill bg-dark">2</span> {{ 'clients.connectWizard.javascriptSdkStep2' | sqxTranslate }}</h5>
<p>
<sqx-code>
<div ngPreserveWhitespaces>import {{'{'}} SquidexClient } from "@squidex/squidex";</div>
<div ngPreserveWhitespaces>&nbsp;</div>
<div ngPreserveWhitespaces>const client = new SquidexClient({{'{'}}</div>
<div ngPreserveWhitespaces> appName: "{{appName}}",</div>
<div ngPreserveWhitespaces> clientId: "{{appName}}:{{client.id}}",</div>
<div ngPreserveWhitespaces> clientSecret: "{{client.secret}}",</div>
<div ngPreserveWhitespaces> environment: "{{apiUrl.value}}"</div>
<div ngPreserveWhitespaces>});</div>
</sqx-code>
</p>
</div>
</ng-container>
</ng-container>
</ng-container>
<ng-container footer>
<button class="btn btn-text-secondary" [disabled]="isStart" (click)="go('Start')">
<button class="btn btn-text-secondary" [disabled]="step === 'Start'" (click)="go('Start')">
{{ 'common.back' | sqxTranslate }}
</button>
</ng-container>

50
frontend/src/app/features/settings/pages/clients/client-connect-form.component.scss

@ -1,6 +1,26 @@
@import 'mixins';
@import 'vars';
sqx-code {
pre {
white-space: pre;
}
}
:host ::ng-deep {
svg {
max-height: 100%;
}
h3 {
margin-top: 1.75rem;
}
.code-container {
margin: .5rem 0;
}
}
.badge {
@include circle(1.3rem);
font-size: $font-smallest;
@ -23,6 +43,30 @@
}
}
.sdk-header {
border: 1px solid $color-border;
border-radius: $border-radius;
display: inline-block;
cursor: pointer;
padding: 1rem;
margin-bottom: .5rem;
margin-right: .5rem;
width: 160px;
text-align: center;
&.active {
border-color: $color-theme-brand;
}
&:hover {
border-color: $color-theme-brand-dark;
}
.logo {
height: 100px;
}
}
.option {
background: none;
border: 1px solid $color-border;
@ -43,10 +87,4 @@
font-size: $font-smallest;
font-weight: normal;
}
}
sqx-code {
pre {
white-space: pre;
}
}

19
frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts

@ -6,7 +6,7 @@
*/
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { AccessTokenDto, ApiUrlConfig, AppsState, ClientDto, ClientsService, ClientTourStated, DialogService, MessageBus } from '@app/shared';
import { AccessTokenDto, ApiUrlConfig, AppsState, ClientDto, ClientsService, ClientTourStated, DialogService, HelpService, MessageBus, SDKEntry } from '@app/shared';
@Component({
selector: 'sqx-client-connect-form',
@ -20,21 +20,21 @@ export class ClientConnectFormComponent implements OnInit {
@Input({ required: true })
public client!: ClientDto;
public sdks = this.helpService.getSDKs();
public sdk?: SDKEntry;
public appName!: string;
public appToken?: AccessTokenDto;
public step = 'Start';
public get isStart() {
return this.step === 'Start';
}
constructor(
public readonly appsState: AppsState,
public readonly apiUrl: ApiUrlConfig,
private readonly changeDetector: ChangeDetectorRef,
private readonly clientsService: ClientsService,
private readonly dialogs: DialogService,
private readonly helpService: HelpService,
private readonly messageBus: MessageBus,
) {
}
@ -57,6 +57,15 @@ export class ClientConnectFormComponent implements OnInit {
this.messageBus.emit(new ClientTourStated());
}
public select(sdk: SDKEntry) {
sdk.instructions = sdk.instructions.replace(/<APP_NAME>/g, this.appName);
sdk.instructions = sdk.instructions.replace(/<CLIENT_ID>/g, `${this.appName}:${this.client.id}`);
sdk.instructions = sdk.instructions.replace(/<CLIENT_SECRET>/g, this.client.secret);
sdk.instructions = sdk.instructions.replace(/<URL>/g, this.apiUrl.value);
this.sdk = sdk;
}
public go(step: string) {
this.step = step;
}

2
frontend/src/app/features/settings/pages/clients/client.component.ts

@ -23,7 +23,7 @@ export class ClientComponent {
public apiCallsLimit = 0;
public connectDialog = new DialogModel();
public connectDialog = new DialogModel(false);
constructor(
public readonly appsState: AppsState,

6
frontend/src/app/framework/angular/code.component.html

@ -1,7 +1,7 @@
<div class="copy-container">
<button type="button" class="code-copy" [sqxCopy]="codeBlock">
<div class="code-container">
<button type="button" class="code-copy" [attr.copy]="id">
<i class="icon-copy"></i>
</button>
<pre class="code" #codeBlock><ng-content></ng-content></pre>
<pre class="code" [attr.id]="id"><ng-content></ng-content></pre>
</div>

34
frontend/src/app/framework/angular/code.component.scss

@ -1,34 +1,2 @@
@import 'mixins';
@import 'vars';
.code {
background: $color-code-background;
margin: .25rem 0;
padding-left: .5rem;
padding-right: 3rem;
}
.code,
.code-copy {
@include text-code;
min-height: 27px;
padding-bottom: .25rem;
padding-top: .25rem;
}
.copy-container {
position: relative;
}
.code-copy {
@include absolute(0, 0, auto, auto);
background: darken($color-border-dark, 30%);
border: 0;
color: $color-white;
padding-left: .375rem;
padding-right: .375rem;
&:focus {
background: darken($color-border-dark, 40%);
}
}
@import 'vars';

5
frontend/src/app/framework/angular/code.component.ts

@ -6,6 +6,7 @@
*/
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MathHelper } from '../internal';
@Component({
selector: 'sqx-code',
@ -13,4 +14,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
templateUrl: './code.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CodeComponent {}
export class CodeComponent {
public readonly id = MathHelper.guid();
}

91
frontend/src/app/framework/angular/forms/copy-global.directive.ts

@ -0,0 +1,91 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, HostListener, Renderer2 } from '@angular/core';
import { DialogService, Types } from '@app/framework/internal';
@Directive({
selector: '[sqxCopyGlobal]',
})
export class CopyGlobalDirective {
constructor(
private readonly dialogs: DialogService,
private readonly renderer: Renderer2,
) {
}
@HostListener('document:click', ['$event'])
public onClick(event: MouseEvent) {
function findElement(target: HTMLElement | null): string | null {
if (!target) {
return null;
}
const id = target.getAttribute('copy')!;
if (id) {
return id;
}
return findElement(target.parentElement);
}
const toCopy = document.getElementById(findElement(event.target as any)!);
if (!toCopy) {
return;
}
this.copyToClipbord(toCopy);
}
private copyToClipbord(element: HTMLElement) {
if (Types.is(element, HTMLInputElement) || Types.is(element, HTMLTextAreaElement)) {
const currentFocus: any = document.activeElement;
const prevSelectionStart = element.selectionStart;
const prevSelectionEnd = element.selectionEnd;
element.focus();
element.setSelectionRange(0, element.value.length);
this.copy();
element.setSelectionRange(prevSelectionStart!, prevSelectionEnd!);
if (currentFocus && Types.isFunction(currentFocus.focus)) {
currentFocus.focus();
}
} else {
const input = this.renderer.createElement('textarea');
this.renderer.setStyle(input, 'position', 'absolute');
this.renderer.setStyle(input, 'right', '-1000px');
this.renderer.appendChild(document.body, input);
input.value = element.innerText;
input.select();
this.copy();
this.renderer.removeChild(document.body, input);
}
}
private copy() {
try {
// eslint-disable-next-line deprecation/deprecation
document.execCommand('copy');
this.dialogs.notifyInfo('i18n:common.clipboardAdded');
return true;
} catch (e) {
return false;
}
}
}

5
frontend/src/app/framework/angular/markdown.directive.ts

@ -15,6 +15,9 @@ export class MarkdownDirective {
@Input('sqxMarkdown')
public markdown!: string;
@Input({ transform: booleanAttribute })
public trusted = false;
@Input({ transform: booleanAttribute })
public inline = true;
@ -43,7 +46,7 @@ export class MarkdownDirective {
} else if (this.optional && !hasExclamation) {
html = markdown;
} else if (this.markdown) {
html = renderMarkdown(markdown, this.inline);
html = renderMarkdown(markdown, this.inline, this.trusted);
}
const hasHtml = html.indexOf('<') >= 0 || html.indexOf('&') >= 0;

1
frontend/src/app/framework/declarations.ts

@ -14,6 +14,7 @@ export * from './angular/forms/confirm-click.directive';
export * from './angular/forms/control-errors-messages.component';
export * from './angular/forms/control-errors.component';
export * from './angular/forms/copy.directive';
export * from './angular/forms/copy-global.directive';
export * from './angular/forms/editable-title.component';
export * from './angular/forms/editors/autocomplete.component';
export * from './angular/forms/editors/checkbox-group.component';

4
frontend/src/app/framework/module.ts

@ -12,7 +12,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { ColorPickerModule } from 'ngx-color-picker';
import { TourService as BaseTourService } from 'ngx-ui-tour-core';
import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TourService, TourStepDirective, TourTemplateComponent, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations';
import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, CopyGlobalDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TourService, TourStepDirective, TourTemplateComponent, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations';
@NgModule({
imports: [
@ -34,6 +34,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc
ControlErrorsComponent,
ControlErrorsMessagesComponent,
CopyDirective,
CopyGlobalDirective,
DarkenPipe,
DatePipe,
DateTimeEditorComponent,
@ -124,6 +125,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc
ConfirmClickDirective,
ControlErrorsComponent,
CopyDirective,
CopyGlobalDirective,
DarkenPipe,
DatePipe,
DateTimeEditorComponent,

23
frontend/src/app/framework/utils/markdown.ts

@ -6,6 +6,7 @@
*/
import { marked } from 'marked';
import { MathHelper } from './math-helper';
function renderLink(href: string, _: string, text: string) {
if (href && href.startsWith('mailto')) {
@ -15,6 +16,20 @@ function renderLink(href: string, _: string, text: string) {
}
}
function renderCode(code: string) {
const id = MathHelper.guid();
return `
<div class="code-container">
<button type="button" class="code-copy" copy="${id}">
<i class="icon-copy"></i>
</button>
<pre class="code" id="${id}">${code}</pre>
</div>
`;
}
function renderInlineParagraph(text: string) {
return text;
}
@ -24,14 +39,18 @@ const RENDERER_INLINE = new marked.Renderer();
RENDERER_INLINE.paragraph = renderInlineParagraph;
RENDERER_INLINE.link = renderLink;
RENDERER_INLINE.code = renderCode;
RENDERER_DEFAULT.link = renderLink;
RENDERER_DEFAULT.code = renderCode;
export function renderMarkdown(input: string | undefined | null, inline: boolean) {
export function renderMarkdown(input: string | undefined | null, inline: boolean, trusted = false) {
if (!input) {
return '';
}
input = escapeHTML(input);
if (!trusted) {
input = escapeHTML(input);
}
if (inline) {
return marked(input, { renderer: RENDERER_INLINE, mangle: false, headerIds: false });

42
frontend/src/app/shared/services/help.service.spec.ts

@ -9,7 +9,7 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing';
import { HelpService } from '@app/shared/internal';
import { HelpService, SDKEntry } from '@app/shared/internal';
describe('HelpService', () => {
beforeEach(() => {
@ -62,4 +62,44 @@ describe('HelpService', () => {
expect(helpSections!).toEqual('');
}));
it('should make get request to get sdks',
inject([HelpService, HttpTestingController], (helpService: HelpService, httpMock: HttpTestingController) => {
let sdks: Record<string, SDKEntry>;
helpService.getSDKs().subscribe(result => {
sdks = result;
});
const req = httpMock.expectOne('https://raw.githubusercontent.com/Squidex/sdk-fern/main/sdks.json');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
dotnet: {},
});
expect(sdks!).toEqual({
dotnet: {} as any,
});
}));
it('should return empty sdks if get request fails',
inject([HelpService, HttpTestingController], (helpService: HelpService, httpMock: HttpTestingController) => {
let sdks: Record<string, SDKEntry>;
helpService.getSDKs().subscribe(result => {
sdks = result;
});
const req = httpMock.expectOne('https://raw.githubusercontent.com/Squidex/sdk-fern/main/sdks.json');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.error(<any>{});
expect(sdks!).toEqual({});
}));
});

24
frontend/src/app/shared/services/help.service.ts

@ -10,6 +10,23 @@ import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
export interface SDKEntry {
// The display name.
name: string;
// The link to the repository.
repository: string;
// The link to the documentation.
documentation: string;
// The instructions as markdown.
instructions: string;
// The SVG logo.
logo: string;
}
@Injectable()
export class HelpService {
constructor(
@ -23,4 +40,11 @@ export class HelpService {
return this.http.get(url, { responseType: 'text' }).pipe(
catchError(() => of('')));
}
public getSDKs(): Observable<Record<string, SDKEntry>> {
const url = 'https://raw.githubusercontent.com/Squidex/sdk-fern/main/sdks.json';
return this.http.get<Record<string, SDKEntry>>(url).pipe(
catchError(() => of({})));
}
}

38
frontend/src/app/theme/_common.scss

@ -154,6 +154,40 @@ hr {
}
}
//
// Code
//
.code {
background: $color-code-background;
margin: .25rem 0;
padding-left: .5rem;
padding-right: 3rem;
}
.code,
.code-copy {
@include text-code;
min-height: 27px;
padding-bottom: .25rem;
padding-top: .25rem;
}
.code-container {
position: relative;
}
.code-copy {
@include absolute(0, 0, auto, auto);
background: darken($color-border-dark, 30%);
border: 0;
color: $color-white;
padding-left: .375rem;
padding-right: .375rem;
&:focus {
background: darken($color-border-dark, 40%);
}
}
//
// Profile picture in circle
//
@ -187,6 +221,10 @@ hr {
img {
max-height: 70%;
}
&-sm {
@include circle(32px);
}
}
.ngx-ui-tour_backdrop {

Loading…
Cancel
Save