@devicai/ui 0.58.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +24 -0
  2. package/dist/cjs/api/assistantInfo.js.map +1 -1
  3. package/dist/cjs/api/client.js +17 -1
  4. package/dist/cjs/api/client.js.map +1 -1
  5. package/dist/cjs/api/types.js.map +1 -1
  6. package/dist/cjs/components/AICommandBar/AICommandBar.js +2 -1
  7. package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -1
  8. package/dist/cjs/components/AICommandBar/useAICommandBar.js +5 -1
  9. package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
  10. package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js +2 -1
  11. package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
  12. package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
  13. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js +5 -1
  14. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  15. package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js +2 -1
  16. package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
  17. package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js +5 -1
  18. package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  19. package/dist/cjs/components/ChatDrawer/ChatDrawer.js +2 -1
  20. package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
  21. package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
  22. package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
  23. package/dist/cjs/components/ChatDrawer/CompactionWidget.js.map +1 -1
  24. package/dist/cjs/components/ChatDrawer/ConversationSelector.js.map +1 -1
  25. package/dist/cjs/components/ChatDrawer/ErrorBoundary.js.map +1 -1
  26. package/dist/cjs/components/ChatDrawer/GuardrailNotice.js.map +1 -1
  27. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  28. package/dist/cjs/components/ChatDrawer/LimitBanner.js.map +1 -1
  29. package/dist/cjs/components/ChatDrawer/QueueNotice.js.map +1 -1
  30. package/dist/cjs/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
  31. package/dist/cjs/components/ChatDrawer/ReferenceChip.js.map +1 -1
  32. package/dist/cjs/components/ChatDrawer/ToolTimeline.js.map +1 -1
  33. package/dist/cjs/components/ChatDrawer/UsageBar.js.map +1 -1
  34. package/dist/cjs/components/ChatDrawer/pastedText.js.map +1 -1
  35. package/dist/cjs/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
  36. package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -1
  37. package/dist/cjs/components/Feedback/MessageActions.js.map +1 -1
  38. package/dist/cjs/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
  39. package/dist/cjs/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
  40. package/dist/cjs/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
  41. package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
  42. package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
  43. package/dist/cjs/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
  44. package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
  45. package/dist/cjs/components/IntegrationsModal/McpConnectForm.js.map +1 -1
  46. package/dist/cjs/components/IntegrationsModal/McpServersSection.js.map +1 -1
  47. package/dist/cjs/components/IntegrationsModal/integrationChoice.js.map +1 -1
  48. package/dist/cjs/components/IntegrationsModal/useIntegrations.js.map +1 -1
  49. package/dist/cjs/components/IntegrationsModal/useTenantMcp.js.map +1 -1
  50. package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  51. package/dist/cjs/components/theme.js.map +1 -1
  52. package/dist/cjs/hooks/useDevicChat.js +10 -2
  53. package/dist/cjs/hooks/useDevicChat.js.map +1 -1
  54. package/dist/cjs/hooks/useModelInterface.js.map +1 -1
  55. package/dist/cjs/hooks/usePolling.js +117 -7
  56. package/dist/cjs/hooks/usePolling.js.map +1 -1
  57. package/dist/cjs/hooks/useSpeechRecording.js.map +1 -1
  58. package/dist/cjs/i18n/translate.js.map +1 -1
  59. package/dist/cjs/i18n/useTranslations.js.map +1 -1
  60. package/dist/cjs/provider/DevicContext.js.map +1 -1
  61. package/dist/cjs/provider/DevicProvider.js +3 -1
  62. package/dist/cjs/provider/DevicProvider.js.map +1 -1
  63. package/dist/cjs/provider/sharedSession.js.map +1 -1
  64. package/dist/cjs/utils/avatar.js.map +1 -1
  65. package/dist/cjs/utils/consumeChatStream.js +44 -0
  66. package/dist/cjs/utils/consumeChatStream.js.map +1 -0
  67. package/dist/cjs/utils/index.js.map +1 -1
  68. package/dist/cjs/utils/logger.js.map +1 -1
  69. package/dist/cjs/utils/toolGroups.js.map +1 -1
  70. package/dist/esm/api/assistantInfo.js.map +1 -1
  71. package/dist/esm/api/client.d.ts +5 -1
  72. package/dist/esm/api/client.js +17 -1
  73. package/dist/esm/api/client.js.map +1 -1
  74. package/dist/esm/api/types.d.ts +2 -0
  75. package/dist/esm/api/types.js.map +1 -1
  76. package/dist/esm/components/AICommandBar/AICommandBar.js +2 -1
  77. package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -1
  78. package/dist/esm/components/AICommandBar/AICommandBar.types.d.ts +6 -0
  79. package/dist/esm/components/AICommandBar/useAICommandBar.d.ts +2 -0
  80. package/dist/esm/components/AICommandBar/useAICommandBar.js +6 -2
  81. package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
  82. package/dist/esm/components/AIElementWrapper/AIElementWrapper.js +2 -1
  83. package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
  84. package/dist/esm/components/AIElementWrapper/AIElementWrapper.types.d.ts +6 -0
  85. package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
  86. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.d.ts +2 -0
  87. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js +6 -2
  88. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  89. package/dist/esm/components/AIGenerationButton/AIGenerationButton.js +2 -1
  90. package/dist/esm/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
  91. package/dist/esm/components/AIGenerationButton/AIGenerationButton.types.d.ts +6 -0
  92. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.d.ts +2 -0
  93. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js +6 -2
  94. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  95. package/dist/esm/components/ChatDrawer/ChatDrawer.js +2 -1
  96. package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
  97. package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +8 -0
  98. package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
  99. package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
  100. package/dist/esm/components/ChatDrawer/CompactionWidget.js.map +1 -1
  101. package/dist/esm/components/ChatDrawer/ConversationSelector.js.map +1 -1
  102. package/dist/esm/components/ChatDrawer/ErrorBoundary.js.map +1 -1
  103. package/dist/esm/components/ChatDrawer/GuardrailNotice.js.map +1 -1
  104. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  105. package/dist/esm/components/ChatDrawer/LimitBanner.js.map +1 -1
  106. package/dist/esm/components/ChatDrawer/QueueNotice.js.map +1 -1
  107. package/dist/esm/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
  108. package/dist/esm/components/ChatDrawer/ReferenceChip.js.map +1 -1
  109. package/dist/esm/components/ChatDrawer/ToolTimeline.js.map +1 -1
  110. package/dist/esm/components/ChatDrawer/UsageBar.js.map +1 -1
  111. package/dist/esm/components/ChatDrawer/pastedText.js.map +1 -1
  112. package/dist/esm/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
  113. package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -1
  114. package/dist/esm/components/Feedback/MessageActions.js.map +1 -1
  115. package/dist/esm/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
  116. package/dist/esm/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
  117. package/dist/esm/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
  118. package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
  119. package/dist/esm/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
  120. package/dist/esm/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
  121. package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
  122. package/dist/esm/components/IntegrationsModal/McpConnectForm.js.map +1 -1
  123. package/dist/esm/components/IntegrationsModal/McpServersSection.js.map +1 -1
  124. package/dist/esm/components/IntegrationsModal/integrationChoice.js.map +1 -1
  125. package/dist/esm/components/IntegrationsModal/useIntegrations.js.map +1 -1
  126. package/dist/esm/components/IntegrationsModal/useTenantMcp.js.map +1 -1
  127. package/dist/esm/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  128. package/dist/esm/components/theme.js.map +1 -1
  129. package/dist/esm/hooks/index.d.ts +1 -1
  130. package/dist/esm/hooks/useDevicChat.d.ts +6 -0
  131. package/dist/esm/hooks/useDevicChat.js +12 -4
  132. package/dist/esm/hooks/useDevicChat.js.map +1 -1
  133. package/dist/esm/hooks/useModelInterface.js.map +1 -1
  134. package/dist/esm/hooks/usePolling.d.ts +35 -0
  135. package/dist/esm/hooks/usePolling.js +114 -8
  136. package/dist/esm/hooks/usePolling.js.map +1 -1
  137. package/dist/esm/hooks/useSpeechRecording.js.map +1 -1
  138. package/dist/esm/i18n/translate.js.map +1 -1
  139. package/dist/esm/i18n/useTranslations.js.map +1 -1
  140. package/dist/esm/provider/DevicContext.js.map +1 -1
  141. package/dist/esm/provider/DevicProvider.d.ts +1 -1
  142. package/dist/esm/provider/DevicProvider.js +3 -1
  143. package/dist/esm/provider/DevicProvider.js.map +1 -1
  144. package/dist/esm/provider/sharedSession.js.map +1 -1
  145. package/dist/esm/provider/types.d.ts +18 -0
  146. package/dist/esm/utils/avatar.js.map +1 -1
  147. package/dist/esm/utils/consumeChatStream.d.ts +6 -0
  148. package/dist/esm/utils/consumeChatStream.js +42 -0
  149. package/dist/esm/utils/consumeChatStream.js.map +1 -0
  150. package/dist/esm/utils/index.js.map +1 -1
  151. package/dist/esm/utils/logger.js.map +1 -1
  152. package/dist/esm/utils/toolGroups.js.map +1 -1
  153. package/package.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FeedbackModal.js","sources":["../../../../src/components/Feedback/FeedbackModal.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useMemo } from 'react';\nimport type { FeedbackModalProps } from './Feedback.types';\nimport { useTranslations } from '../../i18n';\n\n/**\n * Modal for submitting feedback with optional comment\n */\nexport function FeedbackModal({\n isOpen,\n onClose,\n onSubmit,\n feedbackType,\n isSubmitting = false,\n theme,\n}: FeedbackModalProps): JSX.Element | null {\n const t = useTranslations();\n const [comment, setComment] = useState('');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const modalRef = useRef<HTMLDivElement>(null);\n\n // Build inline styles from theme\n const modalStyle = useMemo(() => {\n if (!theme) return undefined;\n return {\n '--devic-bg': theme.backgroundColor,\n '--devic-text': theme.textColor,\n '--devic-text-muted': theme.textMutedColor,\n '--devic-text-secondary': theme.textMutedColor,\n '--devic-bg-secondary': theme.secondaryBackgroundColor,\n '--devic-border': theme.borderColor,\n '--devic-primary': theme.primaryColor,\n '--devic-primary-hover': theme.primaryHoverColor,\n } as React.CSSProperties;\n }, [theme]);\n\n // Focus textarea when modal opens\n useEffect(() => {\n if (isOpen && textareaRef.current) {\n setTimeout(() => textareaRef.current?.focus(), 50);\n }\n }, [isOpen]);\n\n // Reset comment when modal closes\n useEffect(() => {\n if (!isOpen) {\n setComment('');\n }\n }, [isOpen]);\n\n // Handle click outside\n useEffect(() => {\n if (!isOpen) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (modalRef.current && !modalRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleEscape);\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen, onClose]);\n\n if (!isOpen) return null;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n onSubmit(comment);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onSubmit(comment);\n }\n };\n\n return (\n <div className=\"devic-feedback-overlay\">\n <div className=\"devic-feedback-modal\" ref={modalRef} style={modalStyle}>\n <div className=\"devic-feedback-modal-header\">\n <span className=\"devic-feedback-modal-icon\">\n {feedbackType === 'positive' ? <ThumbsUpIcon filled /> : <ThumbsDownIcon filled />}\n </span>\n <span className=\"devic-feedback-modal-title\">\n {feedbackType === 'positive'\n ? t('What did you like?')\n : t('What could be improved?')}\n </span>\n <button\n type=\"button\"\n className=\"devic-feedback-modal-close\"\n onClick={onClose}\n aria-label={t('Close')}\n >\n <CloseIcon />\n </button>\n </div>\n\n <form onSubmit={handleSubmit}>\n <textarea\n ref={textareaRef}\n className=\"devic-feedback-textarea\"\n placeholder={t('Add a comment (optional)...')}\n value={comment}\n onChange={(e) => setComment(e.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n disabled={isSubmitting}\n />\n\n <div className=\"devic-feedback-modal-actions\">\n <button\n type=\"button\"\n className=\"devic-feedback-btn devic-feedback-btn--secondary\"\n onClick={onClose}\n disabled={isSubmitting}\n >\n {t('Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"devic-feedback-btn devic-feedback-btn--primary\"\n disabled={isSubmitting}\n >\n {isSubmitting ? t('Sending...') : t('Submit')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n\n/* ── Icons ── */\n\nfunction ThumbsUpIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 14V2M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"FeedbackModal.js","sources":["../../../../src/components/Feedback/FeedbackModal.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useMemo } from 'react';\nimport type { FeedbackModalProps } from './Feedback.types';\nimport { useTranslations } from '../../i18n';\n\n/**\n * Modal for submitting feedback with optional comment\n */\nexport function FeedbackModal({\n isOpen,\n onClose,\n onSubmit,\n feedbackType,\n isSubmitting = false,\n theme,\n}: FeedbackModalProps): JSX.Element | null {\n const t = useTranslations();\n const [comment, setComment] = useState('');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const modalRef = useRef<HTMLDivElement>(null);\n\n // Build inline styles from theme\n const modalStyle = useMemo(() => {\n if (!theme) return undefined;\n return {\n '--devic-bg': theme.backgroundColor,\n '--devic-text': theme.textColor,\n '--devic-text-muted': theme.textMutedColor,\n '--devic-text-secondary': theme.textMutedColor,\n '--devic-bg-secondary': theme.secondaryBackgroundColor,\n '--devic-border': theme.borderColor,\n '--devic-primary': theme.primaryColor,\n '--devic-primary-hover': theme.primaryHoverColor,\n } as React.CSSProperties;\n }, [theme]);\n\n // Focus textarea when modal opens\n useEffect(() => {\n if (isOpen && textareaRef.current) {\n setTimeout(() => textareaRef.current?.focus(), 50);\n }\n }, [isOpen]);\n\n // Reset comment when modal closes\n useEffect(() => {\n if (!isOpen) {\n setComment('');\n }\n }, [isOpen]);\n\n // Handle click outside\n useEffect(() => {\n if (!isOpen) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (modalRef.current && !modalRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleEscape);\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen, onClose]);\n\n if (!isOpen) return null;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n onSubmit(comment);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onSubmit(comment);\n }\n };\n\n return (\n <div className=\"devic-feedback-overlay\">\n <div className=\"devic-feedback-modal\" ref={modalRef} style={modalStyle}>\n <div className=\"devic-feedback-modal-header\">\n <span className=\"devic-feedback-modal-icon\">\n {feedbackType === 'positive' ? <ThumbsUpIcon filled /> : <ThumbsDownIcon filled />}\n </span>\n <span className=\"devic-feedback-modal-title\">\n {feedbackType === 'positive'\n ? t('What did you like?')\n : t('What could be improved?')}\n </span>\n <button\n type=\"button\"\n className=\"devic-feedback-modal-close\"\n onClick={onClose}\n aria-label={t('Close')}\n >\n <CloseIcon />\n </button>\n </div>\n\n <form onSubmit={handleSubmit}>\n <textarea\n ref={textareaRef}\n className=\"devic-feedback-textarea\"\n placeholder={t('Add a comment (optional)...')}\n value={comment}\n onChange={(e) => setComment(e.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n disabled={isSubmitting}\n />\n\n <div className=\"devic-feedback-modal-actions\">\n <button\n type=\"button\"\n className=\"devic-feedback-btn devic-feedback-btn--secondary\"\n onClick={onClose}\n disabled={isSubmitting}\n >\n {t('Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"devic-feedback-btn devic-feedback-btn--primary\"\n disabled={isSubmitting}\n >\n {isSubmitting ? t('Sending...') : t('Submit')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n\n/* ── Icons ── */\n\nfunction ThumbsUpIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 14V2M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MessageActions.js","sources":["../../../../src/components/Feedback/MessageActions.tsx"],"sourcesContent":["import React, { useState, useCallback, useMemo } from 'react';\nimport { FeedbackModal } from './FeedbackModal';\nimport type { MessageActionsProps, FeedbackState } from './Feedback.types';\nimport { useTranslations } from '../../i18n';\n\n/**\n * Action buttons for a message (copy, thumbs up, thumbs down)\n */\nexport function MessageActions({\n messageId,\n messageContent,\n currentFeedback = 'none',\n onFeedback,\n onCopy,\n showCopy = true,\n showFeedback = true,\n disabled = false,\n theme,\n}: MessageActionsProps): JSX.Element {\n const t = useTranslations();\n const [feedbackState, setFeedbackState] = useState<FeedbackState>(currentFeedback);\n const [modalOpen, setModalOpen] = useState(false);\n const [pendingFeedbackType, setPendingFeedbackType] = useState<'positive' | 'negative'>('positive');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [copied, setCopied] = useState(false);\n\n // Build inline styles from theme for the actions container\n const containerStyle = useMemo(() => {\n if (!theme) return undefined;\n return {\n '--devic-bg-secondary': theme.secondaryBackgroundColor,\n '--devic-text-secondary': theme.textMutedColor,\n } as React.CSSProperties;\n }, [theme]);\n\n const handleCopy = useCallback(async () => {\n if (!messageContent) return;\n\n try {\n await navigator.clipboard.writeText(messageContent);\n setCopied(true);\n onCopy?.(messageContent);\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.error('Failed to copy:', err);\n }\n }, [messageContent, onCopy]);\n\n const handleFeedbackClick = useCallback((type: 'positive' | 'negative') => {\n // If clicking the same feedback type that's already selected, toggle it off\n if ((type === 'positive' && feedbackState === 'positive') ||\n (type === 'negative' && feedbackState === 'negative')) {\n // Optionally could allow removal - for now we just open modal to change comment\n }\n\n setPendingFeedbackType(type);\n setModalOpen(true);\n }, [feedbackState]);\n\n const handleModalSubmit = useCallback(async (comment: string) => {\n setIsSubmitting(true);\n try {\n const isPositive = pendingFeedbackType === 'positive';\n await onFeedback?.(messageId, isPositive, comment || undefined);\n setFeedbackState(isPositive ? 'positive' : 'negative');\n setModalOpen(false);\n } catch (err) {\n console.error('Failed to submit feedback:', err);\n } finally {\n setIsSubmitting(false);\n }\n }, [messageId, pendingFeedbackType, onFeedback]);\n\n return (\n <>\n <div className=\"devic-message-actions\" style={containerStyle}>\n {showCopy && messageContent && (\n <button\n type=\"button\"\n className={`devic-action-btn ${copied ? 'devic-action-btn--active' : ''}`}\n onClick={handleCopy}\n disabled={disabled}\n title={t('Copy to clipboard')}\n aria-label={t('Copy to clipboard')}\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n </button>\n )}\n\n {showFeedback && (\n <>\n <button\n type=\"button\"\n className={`devic-action-btn ${feedbackState === 'positive' ? 'devic-action-btn--active devic-action-btn--positive' : ''}`}\n onClick={() => handleFeedbackClick('positive')}\n disabled={disabled}\n title={t('Good response')}\n aria-label={t('Good response')}\n >\n <ThumbsUpIcon filled={feedbackState === 'positive'} />\n </button>\n\n <button\n type=\"button\"\n className={`devic-action-btn ${feedbackState === 'negative' ? 'devic-action-btn--active devic-action-btn--negative' : ''}`}\n onClick={() => handleFeedbackClick('negative')}\n disabled={disabled}\n title={t('Bad response')}\n aria-label={t('Bad response')}\n >\n <ThumbsDownIcon filled={feedbackState === 'negative'} />\n </button>\n </>\n )}\n </div>\n\n <FeedbackModal\n isOpen={modalOpen}\n onClose={() => setModalOpen(false)}\n onSubmit={handleModalSubmit}\n feedbackType={pendingFeedbackType}\n isSubmitting={isSubmitting}\n theme={theme}\n />\n </>\n );\n}\n\n/* ── Icons ── */\n\nfunction CopyIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n );\n}\n\nfunction CheckIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20,6 9,17 4,12\" />\n </svg>\n );\n}\n\nfunction ThumbsUpIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 14V2M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"MessageActions.js","sources":["../../../../src/components/Feedback/MessageActions.tsx"],"sourcesContent":["import React, { useState, useCallback, useMemo } from 'react';\nimport { FeedbackModal } from './FeedbackModal';\nimport type { MessageActionsProps, FeedbackState } from './Feedback.types';\nimport { useTranslations } from '../../i18n';\n\n/**\n * Action buttons for a message (copy, thumbs up, thumbs down)\n */\nexport function MessageActions({\n messageId,\n messageContent,\n currentFeedback = 'none',\n onFeedback,\n onCopy,\n showCopy = true,\n showFeedback = true,\n disabled = false,\n theme,\n}: MessageActionsProps): JSX.Element {\n const t = useTranslations();\n const [feedbackState, setFeedbackState] = useState<FeedbackState>(currentFeedback);\n const [modalOpen, setModalOpen] = useState(false);\n const [pendingFeedbackType, setPendingFeedbackType] = useState<'positive' | 'negative'>('positive');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [copied, setCopied] = useState(false);\n\n // Build inline styles from theme for the actions container\n const containerStyle = useMemo(() => {\n if (!theme) return undefined;\n return {\n '--devic-bg-secondary': theme.secondaryBackgroundColor,\n '--devic-text-secondary': theme.textMutedColor,\n } as React.CSSProperties;\n }, [theme]);\n\n const handleCopy = useCallback(async () => {\n if (!messageContent) return;\n\n try {\n await navigator.clipboard.writeText(messageContent);\n setCopied(true);\n onCopy?.(messageContent);\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.error('Failed to copy:', err);\n }\n }, [messageContent, onCopy]);\n\n const handleFeedbackClick = useCallback((type: 'positive' | 'negative') => {\n // If clicking the same feedback type that's already selected, toggle it off\n if ((type === 'positive' && feedbackState === 'positive') ||\n (type === 'negative' && feedbackState === 'negative')) {\n // Optionally could allow removal - for now we just open modal to change comment\n }\n\n setPendingFeedbackType(type);\n setModalOpen(true);\n }, [feedbackState]);\n\n const handleModalSubmit = useCallback(async (comment: string) => {\n setIsSubmitting(true);\n try {\n const isPositive = pendingFeedbackType === 'positive';\n await onFeedback?.(messageId, isPositive, comment || undefined);\n setFeedbackState(isPositive ? 'positive' : 'negative');\n setModalOpen(false);\n } catch (err) {\n console.error('Failed to submit feedback:', err);\n } finally {\n setIsSubmitting(false);\n }\n }, [messageId, pendingFeedbackType, onFeedback]);\n\n return (\n <>\n <div className=\"devic-message-actions\" style={containerStyle}>\n {showCopy && messageContent && (\n <button\n type=\"button\"\n className={`devic-action-btn ${copied ? 'devic-action-btn--active' : ''}`}\n onClick={handleCopy}\n disabled={disabled}\n title={t('Copy to clipboard')}\n aria-label={t('Copy to clipboard')}\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n </button>\n )}\n\n {showFeedback && (\n <>\n <button\n type=\"button\"\n className={`devic-action-btn ${feedbackState === 'positive' ? 'devic-action-btn--active devic-action-btn--positive' : ''}`}\n onClick={() => handleFeedbackClick('positive')}\n disabled={disabled}\n title={t('Good response')}\n aria-label={t('Good response')}\n >\n <ThumbsUpIcon filled={feedbackState === 'positive'} />\n </button>\n\n <button\n type=\"button\"\n className={`devic-action-btn ${feedbackState === 'negative' ? 'devic-action-btn--active devic-action-btn--negative' : ''}`}\n onClick={() => handleFeedbackClick('negative')}\n disabled={disabled}\n title={t('Bad response')}\n aria-label={t('Bad response')}\n >\n <ThumbsDownIcon filled={feedbackState === 'negative'} />\n </button>\n </>\n )}\n </div>\n\n <FeedbackModal\n isOpen={modalOpen}\n onClose={() => setModalOpen(false)}\n onSubmit={handleModalSubmit}\n feedbackType={pendingFeedbackType}\n isSubmitting={isSubmitting}\n theme={theme}\n />\n </>\n );\n}\n\n/* ── Icons ── */\n\nfunction CopyIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n );\n}\n\nfunction CheckIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20,6 9,17 4,12\" />\n </svg>\n );\n}\n\nfunction ThumbsUpIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ filled = false }: { filled?: boolean }): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={filled ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 14V2M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectFieldsForm.js","sources":["../../../../src/components/IntegrationsModal/ConnectFieldsForm.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type {\n Integration,\n IntegrationAuthField,\n IntegrationAuthScheme,\n} from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\n\nexport interface ConnectFieldsFormProps {\n integration: Integration;\n /** Every way this app can be connected, from `GET /:app/auth`. */\n schemes: IntegrationAuthScheme[];\n /** The scheme to start on, when the server named one. */\n initialScheme?: string;\n /** Only these are asked for, when the server named what was missing. */\n onlyFields?: IntegrationAuthField[];\n submitting?: boolean;\n /** Failure from the last attempt, shown above the fields. */\n error?: string | null;\n /** Passed down from the dialog that opened this one. */\n theme?: DevicTheme;\n onCancel: () => void;\n onSubmit: (values: {\n authScheme: string;\n accountFields: Record<string, string>;\n }) => void;\n}\n\n/** Whether a field belongs in the form, given the server's shortlist. */\nfunction wanted(\n field: IntegrationAuthField,\n only?: IntegrationAuthField[]\n): boolean {\n return !only?.length || only.some((f) => f.name === field.name);\n}\n\n/**\n * The credentials an app asks the end user for.\n *\n * Most apps do not authenticate with credentials Devic holds — they take an\n * API key the user creates at the provider — so without this they cannot be\n * connected at all, no matter how many popups are opened.\n *\n * A dialog of its own rather than something inside the app's card: a provider\n * explains where to find a key in a paragraph, and three of those do not fit\n * in one column of a grid without turning into a stack of clipped text. The\n * fields are rendered from what the server describes, never from anything\n * per-app — there are hundreds of them.\n *\n * Values go straight into the connection and are never kept here.\n */\nexport function ConnectFieldsForm({\n integration,\n schemes,\n initialScheme,\n onlyFields,\n submitting,\n error,\n theme,\n onCancel,\n onSubmit,\n}: ConnectFieldsFormProps): JSX.Element | null {\n const t = useTranslations();\n const [mode, setMode] = useState(initialScheme ?? schemes[0]?.mode ?? \"\");\n const [values, setValues] = useState<Record<string, string>>({});\n const [touched, setTouched] = useState(false);\n\n const scheme = useMemo(\n () => schemes.find((s) => s.mode === mode) ?? schemes[0],\n [schemes, mode]\n );\n\n const fields = useMemo(\n () => (scheme?.accountFields ?? []).filter((f) => wanted(f, onlyFields)),\n [scheme, onlyFields]\n );\n\n if (!scheme) return null;\n\n const valueOf = (field: IntegrationAuthField) =>\n values[field.name] ?? field.default ?? \"\";\n\n const missing = fields.filter((f) => f.required && !valueOf(f).trim());\n\n const handleSubmit = (event: FormEvent) => {\n event.preventDefault();\n setTouched(true);\n if (missing.length || submitting) return;\n\n // Only what this scheme declares, trimmed. A value left blank is left out\n // rather than sent empty: the provider would store it and fail later.\n const accountFields: Record<string, string> = {};\n for (const field of scheme.accountFields) {\n const value = valueOf(field).trim();\n if (value) accountFields[field.name] = value;\n }\n onSubmit({ authScheme: scheme.mode, accountFields });\n };\n\n return createPortal(\n <div\n className=\"devic-int-overlay devic-int-overlay-stacked\"\n style={themeVars(theme)}\n data-dark={isDarkTheme(theme)}\n onClick={onCancel}\n >\n <form\n className=\"devic-int-connect-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t(\"Connect {app}\", { app: integration.name })}\n onClick={(e) => e.stopPropagation()}\n onSubmit={handleSubmit}\n >\n <div className=\"devic-int-connect-header\">\n <IntegrationLogo integration={integration} />\n <div className=\"devic-int-connect-heading\">\n <strong>{t(\"Connect {app}\", { app: integration.name })}</strong>\n <span>\n {scheme.redirect\n ? t(\"You'll be sent to {app} to finish authorising.\", {\n app: integration.name,\n })\n : t(\n \"Your credentials go straight to the app — only you can use this account.\"\n )}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label={t(\"Close\")}\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-int-connect-body\">\n {error && <div className=\"devic-int-error\">{error}</div>}\n\n {/* Only when there is a real choice — a picker with one option is\n just another thing to read before typing a key. */}\n {schemes.length > 1 && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Connect with\")}\n </span>\n <select\n value={scheme.mode}\n onChange={(e) => {\n setMode(e.target.value);\n setValues({});\n setTouched(false);\n }}\n >\n {schemes.map((s) => (\n <option key={s.mode} value={s.mode}>\n {s.mode}\n </option>\n ))}\n </select>\n </label>\n )}\n\n {fields.map((field, index) => {\n const invalid = touched && field.required && !valueOf(field).trim();\n return (\n <label key={field.name} className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {field.label}\n {!field.required && <em> {t(\"(optional)\")}</em>}\n </span>\n <input\n autoFocus={index === 0}\n type={field.secret ? \"password\" : \"text\"}\n value={valueOf(field)}\n onChange={(e) =>\n setValues((prev) => ({\n ...prev,\n [field.name]: e.target.value,\n }))\n }\n placeholder={field.default}\n autoComplete={field.secret ? \"new-password\" : \"off\"}\n spellCheck={false}\n aria-invalid={invalid || undefined}\n data-invalid={invalid || undefined}\n />\n {field.description && (\n <small className=\"devic-int-connect-hint\">\n {field.description}\n </small>\n )}\n </label>\n );\n })}\n\n {scheme.guideUrl && (\n <a\n className=\"devic-int-connect-guide\"\n href={scheme.guideUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {t(\"Where do I find this?\")}\n </a>\n )}\n </div>\n\n <div className=\"devic-int-connect-actions\">\n <button\n type=\"button\"\n className=\"devic-int-btn\"\n onClick={onCancel}\n disabled={submitting}\n >\n {t(\"Cancel\")}\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting\n ? t(\"Connecting…\")\n : scheme.redirect\n ? t(\"Continue\")\n : t(\"Connect\")}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"ConnectFieldsForm.js","sources":["../../../../src/components/IntegrationsModal/ConnectFieldsForm.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type {\n Integration,\n IntegrationAuthField,\n IntegrationAuthScheme,\n} from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\n\nexport interface ConnectFieldsFormProps {\n integration: Integration;\n /** Every way this app can be connected, from `GET /:app/auth`. */\n schemes: IntegrationAuthScheme[];\n /** The scheme to start on, when the server named one. */\n initialScheme?: string;\n /** Only these are asked for, when the server named what was missing. */\n onlyFields?: IntegrationAuthField[];\n submitting?: boolean;\n /** Failure from the last attempt, shown above the fields. */\n error?: string | null;\n /** Passed down from the dialog that opened this one. */\n theme?: DevicTheme;\n onCancel: () => void;\n onSubmit: (values: {\n authScheme: string;\n accountFields: Record<string, string>;\n }) => void;\n}\n\n/** Whether a field belongs in the form, given the server's shortlist. */\nfunction wanted(\n field: IntegrationAuthField,\n only?: IntegrationAuthField[]\n): boolean {\n return !only?.length || only.some((f) => f.name === field.name);\n}\n\n/**\n * The credentials an app asks the end user for.\n *\n * Most apps do not authenticate with credentials Devic holds — they take an\n * API key the user creates at the provider — so without this they cannot be\n * connected at all, no matter how many popups are opened.\n *\n * A dialog of its own rather than something inside the app's card: a provider\n * explains where to find a key in a paragraph, and three of those do not fit\n * in one column of a grid without turning into a stack of clipped text. The\n * fields are rendered from what the server describes, never from anything\n * per-app — there are hundreds of them.\n *\n * Values go straight into the connection and are never kept here.\n */\nexport function ConnectFieldsForm({\n integration,\n schemes,\n initialScheme,\n onlyFields,\n submitting,\n error,\n theme,\n onCancel,\n onSubmit,\n}: ConnectFieldsFormProps): JSX.Element | null {\n const t = useTranslations();\n const [mode, setMode] = useState(initialScheme ?? schemes[0]?.mode ?? \"\");\n const [values, setValues] = useState<Record<string, string>>({});\n const [touched, setTouched] = useState(false);\n\n const scheme = useMemo(\n () => schemes.find((s) => s.mode === mode) ?? schemes[0],\n [schemes, mode]\n );\n\n const fields = useMemo(\n () => (scheme?.accountFields ?? []).filter((f) => wanted(f, onlyFields)),\n [scheme, onlyFields]\n );\n\n if (!scheme) return null;\n\n const valueOf = (field: IntegrationAuthField) =>\n values[field.name] ?? field.default ?? \"\";\n\n const missing = fields.filter((f) => f.required && !valueOf(f).trim());\n\n const handleSubmit = (event: FormEvent) => {\n event.preventDefault();\n setTouched(true);\n if (missing.length || submitting) return;\n\n // Only what this scheme declares, trimmed. A value left blank is left out\n // rather than sent empty: the provider would store it and fail later.\n const accountFields: Record<string, string> = {};\n for (const field of scheme.accountFields) {\n const value = valueOf(field).trim();\n if (value) accountFields[field.name] = value;\n }\n onSubmit({ authScheme: scheme.mode, accountFields });\n };\n\n return createPortal(\n <div\n className=\"devic-int-overlay devic-int-overlay-stacked\"\n style={themeVars(theme)}\n data-dark={isDarkTheme(theme)}\n onClick={onCancel}\n >\n <form\n className=\"devic-int-connect-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t(\"Connect {app}\", { app: integration.name })}\n onClick={(e) => e.stopPropagation()}\n onSubmit={handleSubmit}\n >\n <div className=\"devic-int-connect-header\">\n <IntegrationLogo integration={integration} />\n <div className=\"devic-int-connect-heading\">\n <strong>{t(\"Connect {app}\", { app: integration.name })}</strong>\n <span>\n {scheme.redirect\n ? t(\"You'll be sent to {app} to finish authorising.\", {\n app: integration.name,\n })\n : t(\n \"Your credentials go straight to the app — only you can use this account.\"\n )}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label={t(\"Close\")}\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-int-connect-body\">\n {error && <div className=\"devic-int-error\">{error}</div>}\n\n {/* Only when there is a real choice — a picker with one option is\n just another thing to read before typing a key. */}\n {schemes.length > 1 && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Connect with\")}\n </span>\n <select\n value={scheme.mode}\n onChange={(e) => {\n setMode(e.target.value);\n setValues({});\n setTouched(false);\n }}\n >\n {schemes.map((s) => (\n <option key={s.mode} value={s.mode}>\n {s.mode}\n </option>\n ))}\n </select>\n </label>\n )}\n\n {fields.map((field, index) => {\n const invalid = touched && field.required && !valueOf(field).trim();\n return (\n <label key={field.name} className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {field.label}\n {!field.required && <em> {t(\"(optional)\")}</em>}\n </span>\n <input\n autoFocus={index === 0}\n type={field.secret ? \"password\" : \"text\"}\n value={valueOf(field)}\n onChange={(e) =>\n setValues((prev) => ({\n ...prev,\n [field.name]: e.target.value,\n }))\n }\n placeholder={field.default}\n autoComplete={field.secret ? \"new-password\" : \"off\"}\n spellCheck={false}\n aria-invalid={invalid || undefined}\n data-invalid={invalid || undefined}\n />\n {field.description && (\n <small className=\"devic-int-connect-hint\">\n {field.description}\n </small>\n )}\n </label>\n );\n })}\n\n {scheme.guideUrl && (\n <a\n className=\"devic-int-connect-guide\"\n href={scheme.guideUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {t(\"Where do I find this?\")}\n </a>\n )}\n </div>\n\n <div className=\"devic-int-connect-actions\">\n <button\n type=\"button\"\n className=\"devic-int-btn\"\n onClick={onCancel}\n disabled={submitting}\n >\n {t(\"Cancel\")}\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting\n ? t(\"Connecting…\")\n : scheme.redirect\n ? t(\"Continue\")\n : t(\"Connect\")}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationLogo.js","sources":["../../../../src/components/IntegrationsModal/IntegrationLogo.tsx"],"sourcesContent":["import { useState, type JSX } from \"react\";\nimport type { Integration } from \"../../api/types\";\n\nexport interface IntegrationLogoProps {\n integration: Integration;\n className?: string;\n}\n\n/**\n * An app's real logo, with its initial as the fallback.\n *\n * The URL comes from the provider and is fetched from whatever host it names,\n * so a broken one is a matter of when, not if — and a broken-image icon in\n * someone else's product looks like our bug. The failed state is kept per logo\n * rather than in the list, so one dead URL never blanks the rest.\n */\nexport function IntegrationLogo({\n integration,\n className = \"\",\n}: IntegrationLogoProps): JSX.Element {\n const [broken, setBroken] = useState(false);\n\n if (!integration.logo || broken) {\n return (\n <span\n className={`devic-int-logo devic-int-logo-fallback ${className}`.trim()}\n aria-hidden=\"true\"\n >\n {(integration.name || integration.app).charAt(0).toUpperCase()}\n </span>\n );\n }\n\n return (\n <img\n className={`devic-int-logo ${className}`.trim()}\n src={integration.logo}\n alt=\"\"\n aria-hidden=\"true\"\n onError={() => setBroken(true)}\n />\n );\n}\n\nexport default IntegrationLogo;\n"],"names":["_jsx"],"mappings":";;;AAQA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,GAAG,EAAE,GACO,EAAA;IACrB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;AAE3C,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,EAAE;AAC/B,QAAA,QACEA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,0CAA0C,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE,EAAA,aAAA,EAC3D,MAAM,EAAA,QAAA,EAEjB,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAA,CACzD;IAEX;AAEA,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,kBAAkB,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE,EAC/C,GAAG,EAAE,WAAW,CAAC,IAAI,EACrB,GAAG,EAAC,EAAE,EAAA,aAAA,EACM,MAAM,EAClB,OAAO,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,EAAA,CAC9B;AAEN;;"}
1
+ {"version":3,"file":"IntegrationLogo.js","sources":["../../../../src/components/IntegrationsModal/IntegrationLogo.tsx"],"sourcesContent":["import { useState, type JSX } from \"react\";\nimport type { Integration } from \"../../api/types\";\n\nexport interface IntegrationLogoProps {\n integration: Integration;\n className?: string;\n}\n\n/**\n * An app's real logo, with its initial as the fallback.\n *\n * The URL comes from the provider and is fetched from whatever host it names,\n * so a broken one is a matter of when, not if — and a broken-image icon in\n * someone else's product looks like our bug. The failed state is kept per logo\n * rather than in the list, so one dead URL never blanks the rest.\n */\nexport function IntegrationLogo({\n integration,\n className = \"\",\n}: IntegrationLogoProps): JSX.Element {\n const [broken, setBroken] = useState(false);\n\n if (!integration.logo || broken) {\n return (\n <span\n className={`devic-int-logo devic-int-logo-fallback ${className}`.trim()}\n aria-hidden=\"true\"\n >\n {(integration.name || integration.app).charAt(0).toUpperCase()}\n </span>\n );\n }\n\n return (\n <img\n className={`devic-int-logo ${className}`.trim()}\n src={integration.logo}\n alt=\"\"\n aria-hidden=\"true\"\n onError={() => setBroken(true)}\n />\n );\n}\n\nexport default IntegrationLogo;\n"],"names":["_jsx"],"mappings":";;;AAQA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,GAAG,EAAE,GACO,EAAA;IACrB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;AAE3C,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,EAAE;AAC/B,QAAA,QACEA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,0CAA0C,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE,EAAA,aAAA,EAC3D,MAAM,EAAA,QAAA,EAEjB,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAA,CACzD;IAEX;AAEA,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,kBAAkB,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE,EAC/C,GAAG,EAAE,WAAW,CAAC,IAAI,EACrB,GAAG,EAAC,EAAE,EAAA,aAAA,EACM,MAAM,EAClB,OAAO,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,EAAA,CAC9B;AAEN;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationsHint.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsHint.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n} from \"react\";\nimport type { Integration } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport \"./IntegrationsModal.css\";\n\nexport interface IntegrationsHintProps {\n /** Shared listing (see `useIntegrations`). */\n state: IntegrationsState;\n /** Opens the connected-apps modal. */\n onOpen: () => void;\n /**\n * Text on the left. Defaults to \"Connect your apps\", or \"Explore connected\n * apps\" once at least one is connected — an invitation the user has already\n * accepted stops being an invitation.\n */\n label?: string;\n /** Most logos to show before the `+N` box. @default 6 */\n maxLogos?: number;\n /**\n * Identity the dismissal is remembered against, so closing it for one end\n * user does not close it for the next one on the same browser.\n */\n storageKey?: string;\n /** Where the bar flies to when closed — the header control. */\n flyToSelector?: string;\n /** Light chips for a dark surface. */\n dark?: boolean;\n className?: string;\n}\n\nconst STORAGE_PREFIX = \"devic:apps-hint-dismissed:\";\n\n/** Reading storage throws in Safari's private mode and in sandboxed frames. */\nfunction wasDismissed(key?: string): boolean {\n if (!key) return false;\n try {\n return localStorage.getItem(STORAGE_PREFIX + key) === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction rememberDismissal(key?: string): void {\n if (!key) return;\n try {\n localStorage.setItem(STORAGE_PREFIX + key, \"1\");\n } catch {\n // Then it comes back next session. Not worth a word to the user.\n }\n}\n\n/** Connected first, same order as the header control. */\nfunction order(integrations: Integration[]): Integration[] {\n return [...integrations].sort((a, b) => {\n if (a.connected !== b.connected) return a.connected ? -1 : 1;\n return 0;\n });\n}\n\n/**\n * How many 24px chips fit once the label and the close button are paid for.\n *\n * The label is the whole reason this row exists, so it gets its space first:\n * a strip reading \"Connect your …\" beside seven logos has lost the argument.\n * Never fewer than three chips, below which the row stops reading as a set of\n * apps at all.\n */\nfunction boxesThatFit(width: number, max: number): number {\n if (!width) return max + 1;\n const LABEL_AND_CLOSE = 195;\n return Math.max(3, Math.min(max + 1, Math.floor((width - LABEL_AND_CLOSE) / 30)));\n}\n\n/** Whether an element is on screen right now. */\nfunction isVisible(el: Element): boolean {\n const r = el.getBoundingClientRect();\n if (!r.width || !r.height) return false;\n return (\n r.bottom > 0 &&\n r.right > 0 &&\n r.top < window.innerHeight &&\n r.left < window.innerWidth\n );\n}\n\n/**\n * The header control this bar belongs to.\n *\n * Scoped to its own drawer first — a page can hold several, each with its own\n * header — and only then to the nearest visible one anywhere. The class name is\n * checked against both the drawer's own container and the legacy one, because\n * getting this wrong is silent: `closest` returns null, the search widens to the\n * whole document, and the copy flies to whichever control happens to be first\n * in the DOM.\n */\nfunction flyTarget(bar: Element, selector: string): HTMLElement | null {\n if (!selector) return null;\n\n const drawer = bar.closest(\".devic-chat-drawer, .devic-drawer\");\n const own = drawer?.querySelector(selector) as HTMLElement | null;\n if (own && isVisible(own)) return own;\n\n const barRect = bar.getBoundingClientRect();\n const candidates = [...document.querySelectorAll(selector)].filter(isVisible);\n if (!candidates.length) return null;\n\n // Nearest to the bar, so a page with several drawers still animates towards\n // the one the user is looking at.\n return candidates.reduce((best, el) => {\n const d = (e: Element) => {\n const r = e.getBoundingClientRect();\n return Math.hypot(r.left - barRect.left, r.top - barRect.top);\n };\n return d(el) < d(best) ? el : best;\n }) as HTMLElement;\n}\n\n/**\n * The strip above the composer that tells the end user their own apps can be\n * connected here.\n *\n * It exists because the header control is a row of small logos and nothing\n * else: discoverable once you know what it is, invisible until then. This says\n * it in words, once, and then gets out of the way for good — the dismissal is\n * remembered per end user, and closing it animates towards the header so that\n * what was dismissed is understood to still be there.\n */\nexport function IntegrationsHint({\n state,\n onOpen,\n label,\n maxLogos = 6,\n storageKey,\n flyToSelector = \".devic-int-launcher\",\n dark = false,\n className = \"\",\n}: IntegrationsHintProps): JSX.Element | null {\n const t = useTranslations();\n const sorted = useMemo(() => order(state.integrations), [state.integrations]);\n const ref = useRef<HTMLDivElement>(null);\n const [boxes, setBoxes] = useState(maxLogos + 1);\n const [dismissed, setDismissed] = useState(() => wasDismissed(storageKey));\n const [leaving, setLeaving] = useState(false);\n\n // A different end user has their own answer to whether this was dismissed.\n useEffect(() => {\n setDismissed(wasDismissed(storageKey));\n setLeaving(false);\n }, [storageKey]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const measure = () =>\n setBoxes(boxesThatFit(el.getBoundingClientRect().width, maxLogos));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(el);\n return () => observer.disconnect();\n }, [maxLogos, state.offered, dismissed]);\n\n /**\n * Sends a copy of the logos from the bar to the header control.\n *\n * A dismissal that simply blanks the row reads as \"that is gone now\". The\n * flight is the sentence \"it moved up there\" said without words — so if it\n * cannot be drawn (reduced motion, no target, no Web Animations API), the bar\n * just closes and nothing is lost.\n */\n const flyToLauncher = useCallback(() => {\n const bar = ref.current;\n if (!bar || typeof document === \"undefined\") return;\n if (window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const target = flyTarget(bar, flyToSelector);\n // Nothing to fly to, or nothing the user can see: the bar just closes.\n // A copy sailing off towards something 2000px down the page says the\n // opposite of what this animation is for.\n if (!target) return;\n\n const logos = bar.querySelector(\".devic-int-hint-logos\");\n const from = (logos ?? bar).getBoundingClientRect();\n const to = target.getBoundingClientRect();\n\n const ghost = (logos ?? bar).cloneNode(true) as HTMLElement;\n ghost.classList.add(\"devic-int-hint-ghost\");\n ghost.style.left = `${from.left}px`;\n ghost.style.top = `${from.top}px`;\n ghost.style.width = `${from.width}px`;\n ghost.style.height = `${from.height}px`;\n document.body.appendChild(ghost);\n\n const dx = to.left + to.width / 2 - (from.left + from.width / 2);\n const dy = to.top + to.height / 2 - (from.top + from.height / 2);\n const scale = from.width ? Math.max(0.4, to.width / from.width) : 0.6;\n\n // An arc, not a straight line. The bow is perpendicular to the path, which\n // is what actually reads as a curve: this trip is nearly vertical, and\n // pulling only on Y just makes it accelerate. It opens away from the\n // target's side so the copy swings out and comes back in, and both terms\n // scale with the distance — a short hop should not loop — and are capped so\n // a long page does not throw it off screen.\n const len = Math.hypot(dx, dy) || 1;\n const bow = Math.min(48, Math.max(16, len * 0.18));\n const side = dx <= 0 ? 1 : -1;\n const bowX = ((-dy / len) * bow) * side;\n const bowY = ((dx / len) * bow) * side;\n const lift = Math.min(70, Math.max(18, len * 0.18));\n\n // The curve the copy travels, as a quadratic Bézier through that midpoint.\n // The control point is derived so the curve actually passes through it —\n // a Bézier does not go through its control.\n const midX = dx * 0.45 + bowX;\n const midY = dy * 0.5 - lift + bowY;\n const ctrlX = 2 * midX - dx / 2;\n const ctrlY = 2 * midY - dy / 2;\n\n // Sampled into many linear steps rather than three keyframes with an easing\n // each. Per-keyframe easings ease OUT into the midpoint and IN out of it,\n // which is a stop halfway — the shape was right and the motion was two\n // hops. Here the keyframes carry only the shape, and one global easing\n // carries the pace: speed builds to a peak at the centre and settles into\n // the target.\n const STEPS = 24;\n // Above the 1.2 of a plain keyframe: this rides on top of the shrink\n // towards the target's size, so the two partly cancel at the apex.\n const PEAK = 1.35;\n const frames = Array.from({ length: STEPS + 1 }, (_, i) => {\n const t = i / STEPS;\n const u = 1 - t;\n return {\n offset: t,\n transform:\n `translate(${2 * u * t * ctrlX + t * t * dx}px, ` +\n `${2 * u * t * ctrlY + t * t * dy}px) ` +\n // Swells on the way and shrinks into the header, so the eye follows\n // the thing rather than the fade.\n `scale(${1 + (scale - 1) * t + (PEAK - 1) * Math.sin(Math.PI * t)})`,\n opacity: t <= 0.5 ? 1 : 1 - Math.pow((t - 0.5) * 2, 1.6),\n easing: \"linear\",\n };\n });\n\n const animation = ghost.animate?.(frames, {\n duration: 780,\n easing: \"cubic-bezier(0.5, 0, 0.5, 1)\",\n fill: \"forwards\",\n });\n\n const remove = () => ghost.remove();\n if (animation) animation.onfinish = remove;\n else remove();\n // Belt and braces: a tab backgrounded mid-flight never fires onfinish.\n window.setTimeout(remove, 2000);\n }, [flyToSelector]);\n\n const dismiss = () => {\n flyToLauncher();\n rememberDismissal(storageKey);\n setLeaving(true);\n window.setTimeout(() => setDismissed(true), 220);\n };\n\n if (dismissed || !state.offered || sorted.length === 0) return null;\n\n const text =\n label ??\n (sorted.some((i) => i.connected)\n ? t(\"Explore connected apps\")\n : t(\"Connect your apps\"));\n\n // When some apps will not fit, the `+N` box takes one of the slots itself.\n const capacity = Math.max(1, boxes);\n const shown = sorted.slice(\n 0,\n sorted.length > capacity ? capacity - 1 : capacity\n );\n const extra = sorted.length - shown.length;\n\n return (\n <div\n ref={ref}\n className={`devic-int-hint ${className}`.trim()}\n data-dark={dark}\n data-leaving={leaving}\n >\n <button\n type=\"button\"\n className=\"devic-int-hint-main\"\n onClick={onOpen}\n >\n <span className=\"devic-int-hint-label\">{text}</span>\n <span className=\"devic-int-hint-logos\" aria-hidden=\"true\">\n {shown.map((integration) => (\n <span\n key={integration.app}\n className=\"devic-int-hint-item\"\n data-connected={integration.connected}\n >\n <IntegrationLogo\n integration={integration}\n className=\"devic-int-hint-logo\"\n />\n </span>\n ))}\n {extra > 0 && (\n <span className=\"devic-int-hint-item devic-int-hint-more\">\n +{extra}\n </span>\n )}\n </span>\n </button>\n <button\n type=\"button\"\n className=\"devic-int-hint-close\"\n onClick={dismiss}\n aria-label={t(\"Hide this\")}\n title={t(\"Hide this. Your apps stay in the header.\")}\n >\n ×\n </button>\n </div>\n );\n}\n\nexport default IntegrationsHint;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"IntegrationsHint.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsHint.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n} from \"react\";\nimport type { Integration } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport \"./IntegrationsModal.css\";\n\nexport interface IntegrationsHintProps {\n /** Shared listing (see `useIntegrations`). */\n state: IntegrationsState;\n /** Opens the connected-apps modal. */\n onOpen: () => void;\n /**\n * Text on the left. Defaults to \"Connect your apps\", or \"Explore connected\n * apps\" once at least one is connected — an invitation the user has already\n * accepted stops being an invitation.\n */\n label?: string;\n /** Most logos to show before the `+N` box. @default 6 */\n maxLogos?: number;\n /**\n * Identity the dismissal is remembered against, so closing it for one end\n * user does not close it for the next one on the same browser.\n */\n storageKey?: string;\n /** Where the bar flies to when closed — the header control. */\n flyToSelector?: string;\n /** Light chips for a dark surface. */\n dark?: boolean;\n className?: string;\n}\n\nconst STORAGE_PREFIX = \"devic:apps-hint-dismissed:\";\n\n/** Reading storage throws in Safari's private mode and in sandboxed frames. */\nfunction wasDismissed(key?: string): boolean {\n if (!key) return false;\n try {\n return localStorage.getItem(STORAGE_PREFIX + key) === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction rememberDismissal(key?: string): void {\n if (!key) return;\n try {\n localStorage.setItem(STORAGE_PREFIX + key, \"1\");\n } catch {\n // Then it comes back next session. Not worth a word to the user.\n }\n}\n\n/** Connected first, same order as the header control. */\nfunction order(integrations: Integration[]): Integration[] {\n return [...integrations].sort((a, b) => {\n if (a.connected !== b.connected) return a.connected ? -1 : 1;\n return 0;\n });\n}\n\n/**\n * How many 24px chips fit once the label and the close button are paid for.\n *\n * The label is the whole reason this row exists, so it gets its space first:\n * a strip reading \"Connect your …\" beside seven logos has lost the argument.\n * Never fewer than three chips, below which the row stops reading as a set of\n * apps at all.\n */\nfunction boxesThatFit(width: number, max: number): number {\n if (!width) return max + 1;\n const LABEL_AND_CLOSE = 195;\n return Math.max(3, Math.min(max + 1, Math.floor((width - LABEL_AND_CLOSE) / 30)));\n}\n\n/** Whether an element is on screen right now. */\nfunction isVisible(el: Element): boolean {\n const r = el.getBoundingClientRect();\n if (!r.width || !r.height) return false;\n return (\n r.bottom > 0 &&\n r.right > 0 &&\n r.top < window.innerHeight &&\n r.left < window.innerWidth\n );\n}\n\n/**\n * The header control this bar belongs to.\n *\n * Scoped to its own drawer first — a page can hold several, each with its own\n * header — and only then to the nearest visible one anywhere. The class name is\n * checked against both the drawer's own container and the legacy one, because\n * getting this wrong is silent: `closest` returns null, the search widens to the\n * whole document, and the copy flies to whichever control happens to be first\n * in the DOM.\n */\nfunction flyTarget(bar: Element, selector: string): HTMLElement | null {\n if (!selector) return null;\n\n const drawer = bar.closest(\".devic-chat-drawer, .devic-drawer\");\n const own = drawer?.querySelector(selector) as HTMLElement | null;\n if (own && isVisible(own)) return own;\n\n const barRect = bar.getBoundingClientRect();\n const candidates = [...document.querySelectorAll(selector)].filter(isVisible);\n if (!candidates.length) return null;\n\n // Nearest to the bar, so a page with several drawers still animates towards\n // the one the user is looking at.\n return candidates.reduce((best, el) => {\n const d = (e: Element) => {\n const r = e.getBoundingClientRect();\n return Math.hypot(r.left - barRect.left, r.top - barRect.top);\n };\n return d(el) < d(best) ? el : best;\n }) as HTMLElement;\n}\n\n/**\n * The strip above the composer that tells the end user their own apps can be\n * connected here.\n *\n * It exists because the header control is a row of small logos and nothing\n * else: discoverable once you know what it is, invisible until then. This says\n * it in words, once, and then gets out of the way for good — the dismissal is\n * remembered per end user, and closing it animates towards the header so that\n * what was dismissed is understood to still be there.\n */\nexport function IntegrationsHint({\n state,\n onOpen,\n label,\n maxLogos = 6,\n storageKey,\n flyToSelector = \".devic-int-launcher\",\n dark = false,\n className = \"\",\n}: IntegrationsHintProps): JSX.Element | null {\n const t = useTranslations();\n const sorted = useMemo(() => order(state.integrations), [state.integrations]);\n const ref = useRef<HTMLDivElement>(null);\n const [boxes, setBoxes] = useState(maxLogos + 1);\n const [dismissed, setDismissed] = useState(() => wasDismissed(storageKey));\n const [leaving, setLeaving] = useState(false);\n\n // A different end user has their own answer to whether this was dismissed.\n useEffect(() => {\n setDismissed(wasDismissed(storageKey));\n setLeaving(false);\n }, [storageKey]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const measure = () =>\n setBoxes(boxesThatFit(el.getBoundingClientRect().width, maxLogos));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(el);\n return () => observer.disconnect();\n }, [maxLogos, state.offered, dismissed]);\n\n /**\n * Sends a copy of the logos from the bar to the header control.\n *\n * A dismissal that simply blanks the row reads as \"that is gone now\". The\n * flight is the sentence \"it moved up there\" said without words — so if it\n * cannot be drawn (reduced motion, no target, no Web Animations API), the bar\n * just closes and nothing is lost.\n */\n const flyToLauncher = useCallback(() => {\n const bar = ref.current;\n if (!bar || typeof document === \"undefined\") return;\n if (window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const target = flyTarget(bar, flyToSelector);\n // Nothing to fly to, or nothing the user can see: the bar just closes.\n // A copy sailing off towards something 2000px down the page says the\n // opposite of what this animation is for.\n if (!target) return;\n\n const logos = bar.querySelector(\".devic-int-hint-logos\");\n const from = (logos ?? bar).getBoundingClientRect();\n const to = target.getBoundingClientRect();\n\n const ghost = (logos ?? bar).cloneNode(true) as HTMLElement;\n ghost.classList.add(\"devic-int-hint-ghost\");\n ghost.style.left = `${from.left}px`;\n ghost.style.top = `${from.top}px`;\n ghost.style.width = `${from.width}px`;\n ghost.style.height = `${from.height}px`;\n document.body.appendChild(ghost);\n\n const dx = to.left + to.width / 2 - (from.left + from.width / 2);\n const dy = to.top + to.height / 2 - (from.top + from.height / 2);\n const scale = from.width ? Math.max(0.4, to.width / from.width) : 0.6;\n\n // An arc, not a straight line. The bow is perpendicular to the path, which\n // is what actually reads as a curve: this trip is nearly vertical, and\n // pulling only on Y just makes it accelerate. It opens away from the\n // target's side so the copy swings out and comes back in, and both terms\n // scale with the distance — a short hop should not loop — and are capped so\n // a long page does not throw it off screen.\n const len = Math.hypot(dx, dy) || 1;\n const bow = Math.min(48, Math.max(16, len * 0.18));\n const side = dx <= 0 ? 1 : -1;\n const bowX = ((-dy / len) * bow) * side;\n const bowY = ((dx / len) * bow) * side;\n const lift = Math.min(70, Math.max(18, len * 0.18));\n\n // The curve the copy travels, as a quadratic Bézier through that midpoint.\n // The control point is derived so the curve actually passes through it —\n // a Bézier does not go through its control.\n const midX = dx * 0.45 + bowX;\n const midY = dy * 0.5 - lift + bowY;\n const ctrlX = 2 * midX - dx / 2;\n const ctrlY = 2 * midY - dy / 2;\n\n // Sampled into many linear steps rather than three keyframes with an easing\n // each. Per-keyframe easings ease OUT into the midpoint and IN out of it,\n // which is a stop halfway — the shape was right and the motion was two\n // hops. Here the keyframes carry only the shape, and one global easing\n // carries the pace: speed builds to a peak at the centre and settles into\n // the target.\n const STEPS = 24;\n // Above the 1.2 of a plain keyframe: this rides on top of the shrink\n // towards the target's size, so the two partly cancel at the apex.\n const PEAK = 1.35;\n const frames = Array.from({ length: STEPS + 1 }, (_, i) => {\n const t = i / STEPS;\n const u = 1 - t;\n return {\n offset: t,\n transform:\n `translate(${2 * u * t * ctrlX + t * t * dx}px, ` +\n `${2 * u * t * ctrlY + t * t * dy}px) ` +\n // Swells on the way and shrinks into the header, so the eye follows\n // the thing rather than the fade.\n `scale(${1 + (scale - 1) * t + (PEAK - 1) * Math.sin(Math.PI * t)})`,\n opacity: t <= 0.5 ? 1 : 1 - Math.pow((t - 0.5) * 2, 1.6),\n easing: \"linear\",\n };\n });\n\n const animation = ghost.animate?.(frames, {\n duration: 780,\n easing: \"cubic-bezier(0.5, 0, 0.5, 1)\",\n fill: \"forwards\",\n });\n\n const remove = () => ghost.remove();\n if (animation) animation.onfinish = remove;\n else remove();\n // Belt and braces: a tab backgrounded mid-flight never fires onfinish.\n window.setTimeout(remove, 2000);\n }, [flyToSelector]);\n\n const dismiss = () => {\n flyToLauncher();\n rememberDismissal(storageKey);\n setLeaving(true);\n window.setTimeout(() => setDismissed(true), 220);\n };\n\n if (dismissed || !state.offered || sorted.length === 0) return null;\n\n const text =\n label ??\n (sorted.some((i) => i.connected)\n ? t(\"Explore connected apps\")\n : t(\"Connect your apps\"));\n\n // When some apps will not fit, the `+N` box takes one of the slots itself.\n const capacity = Math.max(1, boxes);\n const shown = sorted.slice(\n 0,\n sorted.length > capacity ? capacity - 1 : capacity\n );\n const extra = sorted.length - shown.length;\n\n return (\n <div\n ref={ref}\n className={`devic-int-hint ${className}`.trim()}\n data-dark={dark}\n data-leaving={leaving}\n >\n <button\n type=\"button\"\n className=\"devic-int-hint-main\"\n onClick={onOpen}\n >\n <span className=\"devic-int-hint-label\">{text}</span>\n <span className=\"devic-int-hint-logos\" aria-hidden=\"true\">\n {shown.map((integration) => (\n <span\n key={integration.app}\n className=\"devic-int-hint-item\"\n data-connected={integration.connected}\n >\n <IntegrationLogo\n integration={integration}\n className=\"devic-int-hint-logo\"\n />\n </span>\n ))}\n {extra > 0 && (\n <span className=\"devic-int-hint-item devic-int-hint-more\">\n +{extra}\n </span>\n )}\n </span>\n </button>\n <button\n type=\"button\"\n className=\"devic-int-hint-close\"\n onClick={dismiss}\n aria-label={t(\"Hide this\")}\n title={t(\"Hide this. Your apps stay in the header.\")}\n >\n ×\n </button>\n </div>\n );\n}\n\nexport default IntegrationsHint;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationsLauncher.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsLauncher.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type JSX } from \"react\";\nimport type { Integration, TenantMcpServer } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport type { TenantMcpState } from \"./useTenantMcp\";\nimport \"./IntegrationsModal.css\";\n\n/** How many logos fit before the rest are counted instead. */\nexport const DEFAULT_MAX_LOGOS = 6;\n\nexport interface IntegrationsLauncherProps {\n /** Shared listing, so this and the modal load the catalogue once. */\n state: IntegrationsState;\n /**\n * The tenant's own MCP servers, when offered. Counted here because an\n * assistant may offer nothing but those, and a control that only knew about\n * apps would leave that panel with no way in.\n */\n mcp?: TenantMcpState;\n onClick: () => void;\n /** Tooltip and accessible name. @default \"Connected apps\" */\n label?: string;\n /** Logos shown before the `+N` box. @default 6 */\n maxLogos?: number;\n /**\n * Whether the surrounding surface is dark, so the logo chips go light. Many\n * app logos are solid black on transparency and vanish otherwise.\n */\n dark?: boolean;\n /**\n * How many placeholder chips to hold while the listing is on its way.\n *\n * Only pass a number when the apps are known to exist — the assistant having\n * said so — never on the chance that they might. A placeholder is a promise\n * that something is coming, and one that resolves to nothing is worse than\n * the gap it filled.\n */\n placeholders?: number;\n /**\n * Whether the requests that decide if this control exists are still running.\n *\n * Unlike `placeholders`, this makes no claim about what will arrive — only\n * that the answer is not in yet. It holds a single chip, which is the\n * smallest promise that can be made, and is the difference between a header\n * that fills in and one that appears to change its mind.\n */\n loading?: boolean;\n className?: string;\n}\n\n/** Connected first: with more apps than fit, those are the ones worth showing. */\nfunction order(integrations: Integration[]): Integration[] {\n return [...integrations].sort((a, b) => {\n if (a.connected !== b.connected) return a.connected ? -1 : 1;\n return 0;\n });\n}\n\n/**\n * Logos a row this wide can afford.\n *\n * The stack shares the drawer header with a title, the conversation picker and\n * two more buttons, and a 400px drawer is the common case. Six logos there push\n * the picker down to a stub — so on a narrow header the count drops and the\n * `+N` box absorbs the difference, which is what it is for.\n */\nfunction logosThatFit(hostWidth: number, max: number): number {\n if (!hostWidth) return max;\n if (hostWidth >= 520) return max;\n if (hostWidth >= 460) return Math.min(max, 5);\n return Math.min(max, 4);\n}\n\n/**\n * The header control that opens the connected-apps modal, drawn as the real\n * logos of the apps on offer.\n *\n * It renders nothing until the server has confirmed the assistant offers apps\n * to its tenants. A button that opens an empty dialog is worse than no button:\n * it promises the end user something the assistant was never configured to\n * give them.\n */\nexport function IntegrationsLauncher({\n state,\n mcp,\n onClick,\n label,\n maxLogos = DEFAULT_MAX_LOGOS,\n dark = false,\n placeholders = 0,\n loading = false,\n className = \"\",\n}: IntegrationsLauncherProps): JSX.Element | null {\n const t = useTranslations();\n // The host's own label wins; with none, the default goes through the\n // dictionary like every other text the launcher renders.\n const text = label ?? t(\"Connected apps\");\n const sorted = useMemo(() => order(state.integrations), [state.integrations]);\n // Only the servers this tenant actually connected: an offer they have not\n // taken up is not a logo, it is an invitation, and the modal is where\n // invitations belong.\n const mcpConnected = useMemo(\n () => (mcp?.offered ? (mcp.servers ?? []).filter((s) => s.connection) : []),\n [mcp?.offered, mcp?.servers]\n );\n const ref = useRef<HTMLButtonElement>(null);\n const [fit, setFit] = useState(maxLogos);\n\n useEffect(() => {\n const host =\n ref.current?.closest(\".devic-drawer-header\") ??\n ref.current?.parentElement;\n if (!host || typeof ResizeObserver === \"undefined\") return;\n const measure = () =>\n setFit(logosThatFit(host.getBoundingClientRect().width, maxLogos));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(host);\n return () => observer.disconnect();\n }, [maxLogos, state.offered]);\n\n // Nothing yet, but the assistant has already said there will be — or the\n // answer is simply not in. Hold the shape rather than let the header reflow\n // when the logos land.\n const holding = placeholders > 0 ? placeholders : loading ? 1 : 0;\n if (sorted.length === 0 && mcpConnected.length === 0 && holding > 0) {\n return (\n <span\n className={`devic-int-launcher devic-int-launcher-loading ${className}`.trim()}\n data-dark={dark}\n aria-busy=\"true\"\n aria-label={t(\"{label} (loading)\", { label: text })}\n >\n {Array.from({ length: Math.min(holding, Math.max(1, fit)) }).map(\n (_, i) => (\n <span\n key={i}\n className=\"devic-int-launcher-item devic-int-launcher-skeleton\"\n />\n )\n )}\n </span>\n );\n }\n\n const offersApps = state.offered && sorted.length > 0;\n if (!offersApps && mcpConnected.length === 0) return null;\n\n const appsShown = offersApps ? sorted.slice(0, Math.max(1, fit)) : [];\n // Servers fill whatever room the apps left, so an assistant with both does\n // not push the header wider than it was.\n const mcpShown = mcpConnected.slice(\n 0,\n Math.max(offersApps ? 0 : 1, Math.max(1, fit) - appsShown.length)\n );\n const total = sorted.length + mcpConnected.length;\n const extra = total - appsShown.length - mcpShown.length;\n const connected =\n sorted.filter((i) => i.connected).length +\n mcpConnected.filter((s) => s.connection?.status === \"active\").length;\n\n return (\n <button\n type=\"button\"\n ref={ref}\n className={`devic-int-launcher ${className}`.trim()}\n data-dark={dark}\n onClick={onClick}\n title={text}\n aria-label={t(\"{label} ({connected}/{total} connected)\", {\n label: text,\n connected,\n total,\n })}\n >\n {appsShown.map((integration) => (\n <span\n key={integration.app}\n className=\"devic-int-launcher-item\"\n // Dimmed until connected, so the stack doubles as the status: the\n // end user can see at a glance which of their apps are set up.\n data-connected={integration.connected}\n title={integration.name}\n >\n <IntegrationLogo\n integration={integration}\n className=\"devic-int-launcher-logo\"\n />\n </span>\n ))}\n {mcpShown.map((server) => (\n <span\n key={server.connection!.id}\n className=\"devic-int-launcher-item\"\n data-connected={server.connection!.status === \"active\"}\n title={server.name}\n >\n {server.logoUrl ? (\n <img\n className=\"devic-int-launcher-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : (\n <span className=\"devic-int-launcher-logo devic-int-launcher-initial\">\n {server.name.slice(0, 1).toUpperCase()}\n </span>\n )}\n </span>\n ))}\n {extra > 0 && (\n <span className=\"devic-int-launcher-item devic-int-launcher-more\">\n +{extra}\n </span>\n )}\n </button>\n );\n}\n\nexport default IntegrationsLauncher;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"IntegrationsLauncher.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsLauncher.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type JSX } from \"react\";\nimport type { Integration, TenantMcpServer } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useTranslations } from \"../../i18n\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport type { TenantMcpState } from \"./useTenantMcp\";\nimport \"./IntegrationsModal.css\";\n\n/** How many logos fit before the rest are counted instead. */\nexport const DEFAULT_MAX_LOGOS = 6;\n\nexport interface IntegrationsLauncherProps {\n /** Shared listing, so this and the modal load the catalogue once. */\n state: IntegrationsState;\n /**\n * The tenant's own MCP servers, when offered. Counted here because an\n * assistant may offer nothing but those, and a control that only knew about\n * apps would leave that panel with no way in.\n */\n mcp?: TenantMcpState;\n onClick: () => void;\n /** Tooltip and accessible name. @default \"Connected apps\" */\n label?: string;\n /** Logos shown before the `+N` box. @default 6 */\n maxLogos?: number;\n /**\n * Whether the surrounding surface is dark, so the logo chips go light. Many\n * app logos are solid black on transparency and vanish otherwise.\n */\n dark?: boolean;\n /**\n * How many placeholder chips to hold while the listing is on its way.\n *\n * Only pass a number when the apps are known to exist — the assistant having\n * said so — never on the chance that they might. A placeholder is a promise\n * that something is coming, and one that resolves to nothing is worse than\n * the gap it filled.\n */\n placeholders?: number;\n /**\n * Whether the requests that decide if this control exists are still running.\n *\n * Unlike `placeholders`, this makes no claim about what will arrive — only\n * that the answer is not in yet. It holds a single chip, which is the\n * smallest promise that can be made, and is the difference between a header\n * that fills in and one that appears to change its mind.\n */\n loading?: boolean;\n className?: string;\n}\n\n/** Connected first: with more apps than fit, those are the ones worth showing. */\nfunction order(integrations: Integration[]): Integration[] {\n return [...integrations].sort((a, b) => {\n if (a.connected !== b.connected) return a.connected ? -1 : 1;\n return 0;\n });\n}\n\n/**\n * Logos a row this wide can afford.\n *\n * The stack shares the drawer header with a title, the conversation picker and\n * two more buttons, and a 400px drawer is the common case. Six logos there push\n * the picker down to a stub — so on a narrow header the count drops and the\n * `+N` box absorbs the difference, which is what it is for.\n */\nfunction logosThatFit(hostWidth: number, max: number): number {\n if (!hostWidth) return max;\n if (hostWidth >= 520) return max;\n if (hostWidth >= 460) return Math.min(max, 5);\n return Math.min(max, 4);\n}\n\n/**\n * The header control that opens the connected-apps modal, drawn as the real\n * logos of the apps on offer.\n *\n * It renders nothing until the server has confirmed the assistant offers apps\n * to its tenants. A button that opens an empty dialog is worse than no button:\n * it promises the end user something the assistant was never configured to\n * give them.\n */\nexport function IntegrationsLauncher({\n state,\n mcp,\n onClick,\n label,\n maxLogos = DEFAULT_MAX_LOGOS,\n dark = false,\n placeholders = 0,\n loading = false,\n className = \"\",\n}: IntegrationsLauncherProps): JSX.Element | null {\n const t = useTranslations();\n // The host's own label wins; with none, the default goes through the\n // dictionary like every other text the launcher renders.\n const text = label ?? t(\"Connected apps\");\n const sorted = useMemo(() => order(state.integrations), [state.integrations]);\n // Only the servers this tenant actually connected: an offer they have not\n // taken up is not a logo, it is an invitation, and the modal is where\n // invitations belong.\n const mcpConnected = useMemo(\n () => (mcp?.offered ? (mcp.servers ?? []).filter((s) => s.connection) : []),\n [mcp?.offered, mcp?.servers]\n );\n const ref = useRef<HTMLButtonElement>(null);\n const [fit, setFit] = useState(maxLogos);\n\n useEffect(() => {\n const host =\n ref.current?.closest(\".devic-drawer-header\") ??\n ref.current?.parentElement;\n if (!host || typeof ResizeObserver === \"undefined\") return;\n const measure = () =>\n setFit(logosThatFit(host.getBoundingClientRect().width, maxLogos));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(host);\n return () => observer.disconnect();\n }, [maxLogos, state.offered]);\n\n // Nothing yet, but the assistant has already said there will be — or the\n // answer is simply not in. Hold the shape rather than let the header reflow\n // when the logos land.\n const holding = placeholders > 0 ? placeholders : loading ? 1 : 0;\n if (sorted.length === 0 && mcpConnected.length === 0 && holding > 0) {\n return (\n <span\n className={`devic-int-launcher devic-int-launcher-loading ${className}`.trim()}\n data-dark={dark}\n aria-busy=\"true\"\n aria-label={t(\"{label} (loading)\", { label: text })}\n >\n {Array.from({ length: Math.min(holding, Math.max(1, fit)) }).map(\n (_, i) => (\n <span\n key={i}\n className=\"devic-int-launcher-item devic-int-launcher-skeleton\"\n />\n )\n )}\n </span>\n );\n }\n\n const offersApps = state.offered && sorted.length > 0;\n if (!offersApps && mcpConnected.length === 0) return null;\n\n const appsShown = offersApps ? sorted.slice(0, Math.max(1, fit)) : [];\n // Servers fill whatever room the apps left, so an assistant with both does\n // not push the header wider than it was.\n const mcpShown = mcpConnected.slice(\n 0,\n Math.max(offersApps ? 0 : 1, Math.max(1, fit) - appsShown.length)\n );\n const total = sorted.length + mcpConnected.length;\n const extra = total - appsShown.length - mcpShown.length;\n const connected =\n sorted.filter((i) => i.connected).length +\n mcpConnected.filter((s) => s.connection?.status === \"active\").length;\n\n return (\n <button\n type=\"button\"\n ref={ref}\n className={`devic-int-launcher ${className}`.trim()}\n data-dark={dark}\n onClick={onClick}\n title={text}\n aria-label={t(\"{label} ({connected}/{total} connected)\", {\n label: text,\n connected,\n total,\n })}\n >\n {appsShown.map((integration) => (\n <span\n key={integration.app}\n className=\"devic-int-launcher-item\"\n // Dimmed until connected, so the stack doubles as the status: the\n // end user can see at a glance which of their apps are set up.\n data-connected={integration.connected}\n title={integration.name}\n >\n <IntegrationLogo\n integration={integration}\n className=\"devic-int-launcher-logo\"\n />\n </span>\n ))}\n {mcpShown.map((server) => (\n <span\n key={server.connection!.id}\n className=\"devic-int-launcher-item\"\n data-connected={server.connection!.status === \"active\"}\n title={server.name}\n >\n {server.logoUrl ? (\n <img\n className=\"devic-int-launcher-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : (\n <span className=\"devic-int-launcher-logo devic-int-launcher-initial\">\n {server.name.slice(0, 1).toUpperCase()}\n </span>\n )}\n </span>\n ))}\n {extra > 0 && (\n <span className=\"devic-int-launcher-item devic-int-launcher-more\">\n +{extra}\n </span>\n )}\n </button>\n );\n}\n\nexport default IntegrationsLauncher;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationsModal.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsModal.tsx"],"sourcesContent":["import type { JSX } from \"react\";\nimport { IntegrationsPanel, type IntegrationsPanelProps } from \"./IntegrationsPanel\";\n\nexport interface IntegrationsModalProps\n extends Omit<IntegrationsPanelProps, \"active\" | \"dialog\" | \"onClose\"> {\n /** Whether the modal is visible. */\n isOpen: boolean;\n onClose: () => void;\n}\n\n/**\n * Modal where the END USER of an application manages their *own* third-party\n * accounts: the apps the developer offered to tenants of this assistant, each\n * with the accounts this tenant has connected, and the buttons to add or\n * remove one.\n *\n * The dialog shape of `IntegrationsPanel`, which is where all of it lives —\n * see there for what it does and why. Use that one directly to put the same\n * content in a page instead of over it.\n *\n * Closed, it stays mounted and simply renders nothing: what it has already\n * loaded survives, so reopening costs a re-read rather than the whole listing\n * again.\n */\nexport function IntegrationsModal({\n isOpen,\n ...rest\n}: IntegrationsModalProps): JSX.Element | null {\n return <IntegrationsPanel {...rest} active={isOpen} dialog />;\n}\n\nexport default IntegrationsModal;\n"],"names":["_jsx"],"mappings":";;;AAUA;;;;;;;;;;;;;AAaG;AACG,SAAU,iBAAiB,CAAC,EAChC,MAAM,EACN,GAAG,IAAI,EACgB,EAAA;IACvB,OAAOA,GAAA,CAAC,iBAAiB,EAAA,EAAA,GAAK,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAA,IAAA,EAAA,CAAG;AAC/D;;"}
1
+ {"version":3,"file":"IntegrationsModal.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsModal.tsx"],"sourcesContent":["import type { JSX } from \"react\";\nimport { IntegrationsPanel, type IntegrationsPanelProps } from \"./IntegrationsPanel\";\n\nexport interface IntegrationsModalProps\n extends Omit<IntegrationsPanelProps, \"active\" | \"dialog\" | \"onClose\"> {\n /** Whether the modal is visible. */\n isOpen: boolean;\n onClose: () => void;\n}\n\n/**\n * Modal where the END USER of an application manages their *own* third-party\n * accounts: the apps the developer offered to tenants of this assistant, each\n * with the accounts this tenant has connected, and the buttons to add or\n * remove one.\n *\n * The dialog shape of `IntegrationsPanel`, which is where all of it lives —\n * see there for what it does and why. Use that one directly to put the same\n * content in a page instead of over it.\n *\n * Closed, it stays mounted and simply renders nothing: what it has already\n * loaded survives, so reopening costs a re-read rather than the whole listing\n * again.\n */\nexport function IntegrationsModal({\n isOpen,\n ...rest\n}: IntegrationsModalProps): JSX.Element | null {\n return <IntegrationsPanel {...rest} active={isOpen} dialog />;\n}\n\nexport default IntegrationsModal;\n"],"names":["_jsx"],"mappings":";;;AAUA;;;;;;;;;;;;;AAaG;AACG,SAAU,iBAAiB,CAAC,EAChC,MAAM,EACN,GAAG,IAAI,EACgB,EAAA;IACvB,OAAOA,GAAA,CAAC,iBAAiB,EAAA,EAAA,GAAK,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAA,IAAA,EAAA,CAAG;AAC/D;;;;"}