@devicai/ui 0.47.1 → 0.50.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 (134) hide show
  1. package/dist/cjs/api/assistantInfo.js.map +1 -1
  2. package/dist/cjs/api/client.js +56 -0
  3. package/dist/cjs/api/client.js.map +1 -1
  4. package/dist/cjs/api/types.js.map +1 -1
  5. package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -1
  6. package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
  7. package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
  8. package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
  9. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  10. package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
  11. package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  12. package/dist/cjs/components/ChatDrawer/ChatDrawer.js +50 -4
  13. package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
  14. package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
  15. package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
  16. package/dist/cjs/components/ChatDrawer/ConversationSelector.js.map +1 -1
  17. package/dist/cjs/components/ChatDrawer/ErrorBoundary.js.map +1 -1
  18. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js +2 -1
  19. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  20. package/dist/cjs/components/ChatDrawer/LimitBanner.js.map +1 -1
  21. package/dist/cjs/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
  22. package/dist/cjs/components/ChatDrawer/ReferenceChip.js.map +1 -1
  23. package/dist/cjs/components/ChatDrawer/ToolTimeline.js.map +1 -1
  24. package/dist/cjs/components/ChatDrawer/UsageBar.js.map +1 -1
  25. package/dist/cjs/components/ChatDrawer/pastedText.js.map +1 -1
  26. package/dist/cjs/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
  27. package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -1
  28. package/dist/cjs/components/Feedback/MessageActions.js.map +1 -1
  29. package/dist/cjs/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
  30. package/dist/cjs/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
  31. package/dist/cjs/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
  32. package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js +22 -10
  33. package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
  34. package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js +36 -5
  35. package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
  36. package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js +43 -18
  37. package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
  38. package/dist/cjs/components/IntegrationsModal/McpConnectForm.js +79 -0
  39. package/dist/cjs/components/IntegrationsModal/McpConnectForm.js.map +1 -0
  40. package/dist/cjs/components/IntegrationsModal/McpServersSection.js +234 -0
  41. package/dist/cjs/components/IntegrationsModal/McpServersSection.js.map +1 -0
  42. package/dist/cjs/components/IntegrationsModal/useIntegrations.js.map +1 -1
  43. package/dist/cjs/components/IntegrationsModal/useTenantMcp.js +107 -0
  44. package/dist/cjs/components/IntegrationsModal/useTenantMcp.js.map +1 -0
  45. package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  46. package/dist/cjs/components/theme.js.map +1 -1
  47. package/dist/cjs/hooks/useDevicChat.js.map +1 -1
  48. package/dist/cjs/hooks/useModelInterface.js.map +1 -1
  49. package/dist/cjs/hooks/usePolling.js.map +1 -1
  50. package/dist/cjs/hooks/useSpeechRecording.js.map +1 -1
  51. package/dist/cjs/index.js +6 -0
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/provider/DevicContext.js.map +1 -1
  54. package/dist/cjs/provider/DevicProvider.js.map +1 -1
  55. package/dist/cjs/provider/sharedSession.js.map +1 -1
  56. package/dist/cjs/styles.css +1 -1
  57. package/dist/cjs/utils/avatar.js +60 -0
  58. package/dist/cjs/utils/avatar.js.map +1 -0
  59. package/dist/cjs/utils/index.js.map +1 -1
  60. package/dist/cjs/utils/logger.js.map +1 -1
  61. package/dist/cjs/utils/toolGroups.js.map +1 -1
  62. package/dist/esm/api/assistantInfo.js.map +1 -1
  63. package/dist/esm/api/client.d.ts +41 -1
  64. package/dist/esm/api/client.js +56 -0
  65. package/dist/esm/api/client.js.map +1 -1
  66. package/dist/esm/api/types.d.ts +103 -0
  67. package/dist/esm/api/types.js.map +1 -1
  68. package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -1
  69. package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
  70. package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
  71. package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
  72. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
  73. package/dist/esm/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
  74. package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
  75. package/dist/esm/components/ChatDrawer/ChatDrawer.js +50 -4
  76. package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
  77. package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
  78. package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
  79. package/dist/esm/components/ChatDrawer/ConversationSelector.js.map +1 -1
  80. package/dist/esm/components/ChatDrawer/ErrorBoundary.js.map +1 -1
  81. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js +2 -1
  82. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  83. package/dist/esm/components/ChatDrawer/LimitBanner.js.map +1 -1
  84. package/dist/esm/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
  85. package/dist/esm/components/ChatDrawer/ReferenceChip.js.map +1 -1
  86. package/dist/esm/components/ChatDrawer/ToolTimeline.js.map +1 -1
  87. package/dist/esm/components/ChatDrawer/UsageBar.js.map +1 -1
  88. package/dist/esm/components/ChatDrawer/pastedText.js.map +1 -1
  89. package/dist/esm/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
  90. package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -1
  91. package/dist/esm/components/Feedback/MessageActions.js.map +1 -1
  92. package/dist/esm/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
  93. package/dist/esm/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
  94. package/dist/esm/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
  95. package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.d.ts +17 -1
  96. package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js +22 -10
  97. package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
  98. package/dist/esm/components/IntegrationsModal/IntegrationsModal.d.ts +8 -1
  99. package/dist/esm/components/IntegrationsModal/IntegrationsModal.js +36 -5
  100. package/dist/esm/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
  101. package/dist/esm/components/IntegrationsModal/IntegrationsToggle.d.ts +22 -3
  102. package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js +44 -19
  103. package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
  104. package/dist/esm/components/IntegrationsModal/McpConnectForm.d.ts +42 -0
  105. package/dist/esm/components/IntegrationsModal/McpConnectForm.js +77 -0
  106. package/dist/esm/components/IntegrationsModal/McpConnectForm.js.map +1 -0
  107. package/dist/esm/components/IntegrationsModal/McpServersSection.d.ts +25 -0
  108. package/dist/esm/components/IntegrationsModal/McpServersSection.js +232 -0
  109. package/dist/esm/components/IntegrationsModal/McpServersSection.js.map +1 -0
  110. package/dist/esm/components/IntegrationsModal/index.d.ts +6 -0
  111. package/dist/esm/components/IntegrationsModal/useIntegrations.js.map +1 -1
  112. package/dist/esm/components/IntegrationsModal/useTenantMcp.d.ts +40 -0
  113. package/dist/esm/components/IntegrationsModal/useTenantMcp.js +105 -0
  114. package/dist/esm/components/IntegrationsModal/useTenantMcp.js.map +1 -0
  115. package/dist/esm/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  116. package/dist/esm/components/theme.js.map +1 -1
  117. package/dist/esm/hooks/useDevicChat.js.map +1 -1
  118. package/dist/esm/hooks/useModelInterface.js.map +1 -1
  119. package/dist/esm/hooks/usePolling.js.map +1 -1
  120. package/dist/esm/hooks/useSpeechRecording.js.map +1 -1
  121. package/dist/esm/index.d.ts +3 -3
  122. package/dist/esm/index.js +3 -0
  123. package/dist/esm/index.js.map +1 -1
  124. package/dist/esm/provider/DevicContext.js.map +1 -1
  125. package/dist/esm/provider/DevicProvider.js.map +1 -1
  126. package/dist/esm/provider/sharedSession.js.map +1 -1
  127. package/dist/esm/styles.css +1 -1
  128. package/dist/esm/utils/avatar.d.ts +18 -0
  129. package/dist/esm/utils/avatar.js +58 -0
  130. package/dist/esm/utils/avatar.js.map +1 -0
  131. package/dist/esm/utils/index.js.map +1 -1
  132. package/dist/esm/utils/logger.js.map +1 -1
  133. package/dist/esm/utils/toolGroups.js.map +1 -1
  134. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"RecalledMemoriesWidget.js","sources":["../../../../../src/components/ChatDrawer/RecalledMemoriesWidget.tsx"],"sourcesContent":["import { useState, type JSX, type ReactNode } from \"react\";\nimport type {\n RecalledMemoryEntity,\n RecalledMemoryFact,\n RecalledMemoryRecord,\n} from \"../../api/types\";\n\n/** Data handed to a custom recalled-memories renderer. */\nexport interface RecalledMemoriesRendererProps {\n /** The recall events anchored at this point of the conversation. */\n records: RecalledMemoryRecord[];\n /** True while the run that produced (or is producing) them is in flight. */\n isLoading: boolean;\n}\n\n/** Replaces the built-in \"Recalled memories\" strip. Return null to hide it. */\nexport type RecalledMemoriesRenderer = (\n props: RecalledMemoriesRendererProps\n) => ReactNode;\n\nconst SOURCE_LABELS: Record<RecalledMemoryRecord[\"source\"], string> = {\n conversation_start: \"Automatic recall\",\n search_memory: \"Memory search\",\n search_memory_nodes: \"Graph node search\",\n explore_memory_graph: \"Graph exploration\",\n};\n\nfunction dedupeFacts(records: RecalledMemoryRecord[]): RecalledMemoryFact[] {\n const seen = new Set<string>();\n const facts: RecalledMemoryFact[] = [];\n for (const record of records) {\n for (const fact of record.facts ?? []) {\n const key = (fact.fact ?? \"\").trim().toLowerCase();\n if (!key || seen.has(key)) continue;\n seen.add(key);\n facts.push(fact);\n }\n }\n return facts;\n}\n\nfunction dedupeEntities(\n records: RecalledMemoryRecord[]\n): RecalledMemoryEntity[] {\n const seen = new Set<string>();\n return records\n .flatMap((r) => r.entities ?? [])\n .filter((e) => {\n const key = (e.name ?? \"\").trim().toLowerCase();\n if (!key || seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nfunction BrainIcon(): JSX.Element {\n return (\n <svg\n className=\"devic-recall-icon\"\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 aria-hidden=\"true\"\n >\n <path d=\"M9.5 2a2.5 2.5 0 0 0-2.45 2A3.5 3.5 0 0 0 4.6 8.6 3.5 3.5 0 0 0 3 11.5c0 1.1.5 2.08 1.29 2.73A3.5 3.5 0 0 0 7 20a3 3 0 0 0 5-2.24V4.5A2.5 2.5 0 0 0 9.5 2Z\" />\n <path d=\"M14.5 2a2.5 2.5 0 0 1 2.45 2 3.5 3.5 0 0 1 2.45 4.6A3.5 3.5 0 0 1 21 11.5a3.49 3.49 0 0 1-1.29 2.73A3.5 3.5 0 0 1 17 20a3 3 0 0 1-5-2.24V4.5A2.5 2.5 0 0 1 14.5 2Z\" />\n </svg>\n );\n}\n\nfunction ChevronIcon({ up }: { up: boolean }): JSX.Element {\n return (\n <svg\n className=\"devic-recall-chevron\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n style={{ transform: up ? \"rotate(180deg)\" : undefined }}\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n );\n}\n\nexport interface RecalledMemoriesWidgetProps {\n records: RecalledMemoryRecord[];\n /** True while the run that produced the records is still processing. */\n isLoading?: boolean;\n /** Custom renderer replacing the built-in strip. */\n renderer?: RecalledMemoriesRenderer;\n}\n\n/**\n * Collapsible \"Recalled memories\" strip interleaved in the chat with the\n * message that brought long-term memories into the conversation. Collapsed it\n * summarizes (\"N facts · M entities\"); expanded it lists the facts (with\n * their graph endpoints), the entities and any previous-session context.\n * Renders nothing when the records carry no content.\n */\nexport function RecalledMemoriesWidget({\n records,\n isLoading = false,\n renderer,\n}: RecalledMemoriesWidgetProps): JSX.Element | null {\n const [expanded, setExpanded] = useState(false);\n\n if (renderer) {\n return <>{renderer({ records, isLoading })}</>;\n }\n\n const facts = dedupeFacts(records);\n const entities = dedupeEntities(records);\n const turns = records\n .filter((r) => r.source === \"conversation_start\")\n .flatMap((r) => r.turns ?? []);\n const sources = [...new Set(records.map((r) => SOURCE_LABELS[r.source]))];\n\n if (!facts.length && !entities.length && !turns.length) return null;\n\n const summaryParts: string[] = [];\n if (facts.length) {\n summaryParts.push(`${facts.length} fact${facts.length > 1 ? \"s\" : \"\"}`);\n }\n if (entities.length) {\n summaryParts.push(\n `${entities.length} entit${entities.length > 1 ? \"ies\" : \"y\"}`\n );\n }\n\n return (\n <div className=\"devic-recall\">\n <button\n type=\"button\"\n className=\"devic-recall-header\"\n onClick={() => setExpanded((v) => !v)}\n aria-expanded={expanded}\n >\n <BrainIcon />\n <span className=\"devic-recall-title\">Recalled memories</span>\n {summaryParts.length > 0 && (\n <span className=\"devic-recall-summary\">\n {summaryParts.join(\" · \")}\n </span>\n )}\n <span className=\"devic-recall-spacer\" />\n {sources.map((s) => (\n <span key={s} className=\"devic-recall-source\">\n {s}\n </span>\n ))}\n <ChevronIcon up={expanded} />\n </button>\n\n {expanded && (\n <div className=\"devic-recall-body\">\n {facts.length > 0 && (\n <div className=\"devic-recall-facts\">\n {facts.map((fact, i) => (\n <div key={i} className=\"devic-recall-fact\">\n <span>{fact.fact}</span>\n <span className=\"devic-recall-fact-meta\">\n {fact.source && fact.target\n ? `${fact.source} → ${fact.relation} → ${fact.target}`\n : fact.relation}\n {fact.validAt ? ` · since ${fact.validAt.slice(0, 10)}` : \"\"}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {entities.length > 0 && (\n <div className=\"devic-recall-entities\">\n {entities.map((entity) => (\n <span\n key={entity.id}\n className=\"devic-recall-entity\"\n title={entity.summary || undefined}\n >\n {entity.name}\n <span className=\"devic-recall-entity-type\">\n {\" \"}\n · {entity.type}\n </span>\n </span>\n ))}\n </div>\n )}\n\n {turns.length > 0 && (\n <div className=\"devic-recall-turns\">\n <span className=\"devic-recall-turns-title\">\n Previous session context\n </span>\n {turns.map((turn, i) => (\n <span key={i} className=\"devic-recall-turn\">\n <span className=\"devic-recall-turn-role\">{turn.role}:</span>{\" \"}\n {turn.content}\n </span>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default RecalledMemoriesWidget;\n"],"names":["_jsxs","_jsx","useState","_Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"RecalledMemoriesWidget.js","sources":["../../../../../src/components/ChatDrawer/RecalledMemoriesWidget.tsx"],"sourcesContent":["import { useState, type JSX, type ReactNode } from \"react\";\nimport type {\n RecalledMemoryEntity,\n RecalledMemoryFact,\n RecalledMemoryRecord,\n} from \"../../api/types\";\n\n/** Data handed to a custom recalled-memories renderer. */\nexport interface RecalledMemoriesRendererProps {\n /** The recall events anchored at this point of the conversation. */\n records: RecalledMemoryRecord[];\n /** True while the run that produced (or is producing) them is in flight. */\n isLoading: boolean;\n}\n\n/** Replaces the built-in \"Recalled memories\" strip. Return null to hide it. */\nexport type RecalledMemoriesRenderer = (\n props: RecalledMemoriesRendererProps\n) => ReactNode;\n\nconst SOURCE_LABELS: Record<RecalledMemoryRecord[\"source\"], string> = {\n conversation_start: \"Automatic recall\",\n search_memory: \"Memory search\",\n search_memory_nodes: \"Graph node search\",\n explore_memory_graph: \"Graph exploration\",\n};\n\nfunction dedupeFacts(records: RecalledMemoryRecord[]): RecalledMemoryFact[] {\n const seen = new Set<string>();\n const facts: RecalledMemoryFact[] = [];\n for (const record of records) {\n for (const fact of record.facts ?? []) {\n const key = (fact.fact ?? \"\").trim().toLowerCase();\n if (!key || seen.has(key)) continue;\n seen.add(key);\n facts.push(fact);\n }\n }\n return facts;\n}\n\nfunction dedupeEntities(\n records: RecalledMemoryRecord[]\n): RecalledMemoryEntity[] {\n const seen = new Set<string>();\n return records\n .flatMap((r) => r.entities ?? [])\n .filter((e) => {\n const key = (e.name ?? \"\").trim().toLowerCase();\n if (!key || seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nfunction BrainIcon(): JSX.Element {\n return (\n <svg\n className=\"devic-recall-icon\"\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 aria-hidden=\"true\"\n >\n <path d=\"M9.5 2a2.5 2.5 0 0 0-2.45 2A3.5 3.5 0 0 0 4.6 8.6 3.5 3.5 0 0 0 3 11.5c0 1.1.5 2.08 1.29 2.73A3.5 3.5 0 0 0 7 20a3 3 0 0 0 5-2.24V4.5A2.5 2.5 0 0 0 9.5 2Z\" />\n <path d=\"M14.5 2a2.5 2.5 0 0 1 2.45 2 3.5 3.5 0 0 1 2.45 4.6A3.5 3.5 0 0 1 21 11.5a3.49 3.49 0 0 1-1.29 2.73A3.5 3.5 0 0 1 17 20a3 3 0 0 1-5-2.24V4.5A2.5 2.5 0 0 1 14.5 2Z\" />\n </svg>\n );\n}\n\nfunction ChevronIcon({ up }: { up: boolean }): JSX.Element {\n return (\n <svg\n className=\"devic-recall-chevron\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n style={{ transform: up ? \"rotate(180deg)\" : undefined }}\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n );\n}\n\nexport interface RecalledMemoriesWidgetProps {\n records: RecalledMemoryRecord[];\n /** True while the run that produced the records is still processing. */\n isLoading?: boolean;\n /** Custom renderer replacing the built-in strip. */\n renderer?: RecalledMemoriesRenderer;\n}\n\n/**\n * Collapsible \"Recalled memories\" strip interleaved in the chat with the\n * message that brought long-term memories into the conversation. Collapsed it\n * summarizes (\"N facts · M entities\"); expanded it lists the facts (with\n * their graph endpoints), the entities and any previous-session context.\n * Renders nothing when the records carry no content.\n */\nexport function RecalledMemoriesWidget({\n records,\n isLoading = false,\n renderer,\n}: RecalledMemoriesWidgetProps): JSX.Element | null {\n const [expanded, setExpanded] = useState(false);\n\n if (renderer) {\n return <>{renderer({ records, isLoading })}</>;\n }\n\n const facts = dedupeFacts(records);\n const entities = dedupeEntities(records);\n const turns = records\n .filter((r) => r.source === \"conversation_start\")\n .flatMap((r) => r.turns ?? []);\n const sources = [...new Set(records.map((r) => SOURCE_LABELS[r.source]))];\n\n if (!facts.length && !entities.length && !turns.length) return null;\n\n const summaryParts: string[] = [];\n if (facts.length) {\n summaryParts.push(`${facts.length} fact${facts.length > 1 ? \"s\" : \"\"}`);\n }\n if (entities.length) {\n summaryParts.push(\n `${entities.length} entit${entities.length > 1 ? \"ies\" : \"y\"}`\n );\n }\n\n return (\n <div className=\"devic-recall\">\n <button\n type=\"button\"\n className=\"devic-recall-header\"\n onClick={() => setExpanded((v) => !v)}\n aria-expanded={expanded}\n >\n <BrainIcon />\n <span className=\"devic-recall-title\">Recalled memories</span>\n {summaryParts.length > 0 && (\n <span className=\"devic-recall-summary\">\n {summaryParts.join(\" · \")}\n </span>\n )}\n <span className=\"devic-recall-spacer\" />\n {sources.map((s) => (\n <span key={s} className=\"devic-recall-source\">\n {s}\n </span>\n ))}\n <ChevronIcon up={expanded} />\n </button>\n\n {expanded && (\n <div className=\"devic-recall-body\">\n {facts.length > 0 && (\n <div className=\"devic-recall-facts\">\n {facts.map((fact, i) => (\n <div key={i} className=\"devic-recall-fact\">\n <span>{fact.fact}</span>\n <span className=\"devic-recall-fact-meta\">\n {fact.source && fact.target\n ? `${fact.source} → ${fact.relation} → ${fact.target}`\n : fact.relation}\n {fact.validAt ? ` · since ${fact.validAt.slice(0, 10)}` : \"\"}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {entities.length > 0 && (\n <div className=\"devic-recall-entities\">\n {entities.map((entity) => (\n <span\n key={entity.id}\n className=\"devic-recall-entity\"\n title={entity.summary || undefined}\n >\n {entity.name}\n <span className=\"devic-recall-entity-type\">\n {\" \"}\n · {entity.type}\n </span>\n </span>\n ))}\n </div>\n )}\n\n {turns.length > 0 && (\n <div className=\"devic-recall-turns\">\n <span className=\"devic-recall-turns-title\">\n Previous session context\n </span>\n {turns.map((turn, i) => (\n <span key={i} className=\"devic-recall-turn\">\n <span className=\"devic-recall-turn-role\">{turn.role}:</span>{\" \"}\n {turn.content}\n </span>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default RecalledMemoriesWidget;\n"],"names":["_jsxs","_jsx","useState","_Fragment"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReferenceChip.js","sources":["../../../../../src/components/ChatDrawer/ReferenceChip.tsx"],"sourcesContent":["import React from \"react\";\n\n/**\n * Visual context a {@link ReferenceChip} is rendered in:\n * - `input`: inside the prompt box, as an editable, removable chip.\n * - `message`: above a sent user bubble, as a read-only chip.\n */\nexport type ReferenceChipVariant = \"input\" | \"message\";\n\nexport interface ReferenceChipProps {\n /** Short label shown inside the chip (quoted automatically). */\n label: string;\n /**\n * When provided, renders a remove (×) button that invokes this handler.\n * Only meaningful for the `input` variant.\n */\n onRemove?: () => void;\n /** Visual context. Defaults to `input`. */\n variant?: ReferenceChipVariant;\n /** Optional custom icon replacing the default reference arrow. */\n icon?: React.ReactNode;\n /** Extra className appended to the chip root. */\n className?: string;\n}\n\n/**\n * Reusable chip node for an AIElementWrapper reference. Shared by the\n * ChatInput (active references in the prompt box) and ChatMessages\n * (references parsed out of a sent user message), so both render the\n * exact same chip markup and styling.\n */\nexport function ReferenceChip({\n label,\n onRemove,\n variant = \"input\",\n icon,\n className,\n}: ReferenceChipProps): JSX.Element {\n const base =\n variant === \"message\"\n ? \"devic-message-reference-chip\"\n : \"devic-reference-chip\";\n\n return (\n <div className={className ? `${base} ${className}` : base} title={label}>\n {icon ?? <ReferenceArrowIcon />}\n <span className={`${base}-label`}>\"{label}\"</span>\n {onRemove && (\n <button\n type=\"button\"\n className=\"devic-reference-chip-remove\"\n onClick={onRemove}\n aria-label=\"Quitar referencia\"\n >\n &times;\n </button>\n )}\n </div>\n );\n}\n\n/** Corner-down-right arrow used as the default reference glyph. */\nfunction ReferenceArrowIcon(): JSX.Element {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"15 10 20 15 15 20\" />\n <path d=\"M4 4v7a4 4 0 0 0 4 4h12\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_jsx"],"mappings":";;;;AAyBA;;;;;AAKG;AACG,SAAU,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,OAAO,GAAG,OAAO,EACjB,IAAI,EACJ,SAAS,GACU,EAAA;AACnB,IAAA,MAAM,IAAI,GACR,OAAO,KAAK;AACV,UAAE;UACA,sBAAsB;IAE5B,QACEA,eAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,SAAS,GAAG,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAA,QAAA,EAAA,CACpE,IAAI,IAAIC,cAAA,CAAC,kBAAkB,EAAA,EAAA,CAAG,EAC/BD,eAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,CAAA,EAAG,IAAI,CAAA,MAAA,CAAQ,EAAA,QAAA,EAAA,CAAA,IAAA,EAAI,KAAK,EAAA,IAAA,CAAA,EAAA,CAAS,EACjD,QAAQ,KACPC,cAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,6BAA6B,EACvC,OAAO,EAAE,QAAQ,EAAA,YAAA,EACN,mBAAmB,EAAA,QAAA,EAAA,QAAA,EAAA,CAGvB,CACV,CAAA,EAAA,CACG;AAEV;AAEA;AACA,SAAS,kBAAkB,GAAA;IACzB,QACED,yBACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,aAAA,EACV,MAAM,EAAA,QAAA,EAAA,CAElBC,cAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,mBAAmB,EAAA,CAAG,EACvCA,cAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,yBAAyB,EAAA,CAAG,CAAA,EAAA,CAChC;AAEV;;;;"}
1
+ {"version":3,"file":"ReferenceChip.js","sources":["../../../../../src/components/ChatDrawer/ReferenceChip.tsx"],"sourcesContent":["import React from \"react\";\n\n/**\n * Visual context a {@link ReferenceChip} is rendered in:\n * - `input`: inside the prompt box, as an editable, removable chip.\n * - `message`: above a sent user bubble, as a read-only chip.\n */\nexport type ReferenceChipVariant = \"input\" | \"message\";\n\nexport interface ReferenceChipProps {\n /** Short label shown inside the chip (quoted automatically). */\n label: string;\n /**\n * When provided, renders a remove (×) button that invokes this handler.\n * Only meaningful for the `input` variant.\n */\n onRemove?: () => void;\n /** Visual context. Defaults to `input`. */\n variant?: ReferenceChipVariant;\n /** Optional custom icon replacing the default reference arrow. */\n icon?: React.ReactNode;\n /** Extra className appended to the chip root. */\n className?: string;\n}\n\n/**\n * Reusable chip node for an AIElementWrapper reference. Shared by the\n * ChatInput (active references in the prompt box) and ChatMessages\n * (references parsed out of a sent user message), so both render the\n * exact same chip markup and styling.\n */\nexport function ReferenceChip({\n label,\n onRemove,\n variant = \"input\",\n icon,\n className,\n}: ReferenceChipProps): JSX.Element {\n const base =\n variant === \"message\"\n ? \"devic-message-reference-chip\"\n : \"devic-reference-chip\";\n\n return (\n <div className={className ? `${base} ${className}` : base} title={label}>\n {icon ?? <ReferenceArrowIcon />}\n <span className={`${base}-label`}>\"{label}\"</span>\n {onRemove && (\n <button\n type=\"button\"\n className=\"devic-reference-chip-remove\"\n onClick={onRemove}\n aria-label=\"Quitar referencia\"\n >\n &times;\n </button>\n )}\n </div>\n );\n}\n\n/** Corner-down-right arrow used as the default reference glyph. */\nfunction ReferenceArrowIcon(): JSX.Element {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"15 10 20 15 15 20\" />\n <path d=\"M4 4v7a4 4 0 0 0 4 4h12\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_jsx"],"mappings":";;;;AAyBA;;;;;AAKG;AACG,SAAU,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,OAAO,GAAG,OAAO,EACjB,IAAI,EACJ,SAAS,GACU,EAAA;AACnB,IAAA,MAAM,IAAI,GACR,OAAO,KAAK;AACV,UAAE;UACA,sBAAsB;IAE5B,QACEA,eAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,SAAS,GAAG,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAA,QAAA,EAAA,CACpE,IAAI,IAAIC,cAAA,CAAC,kBAAkB,EAAA,EAAA,CAAG,EAC/BD,eAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,CAAA,EAAG,IAAI,CAAA,MAAA,CAAQ,EAAA,QAAA,EAAA,CAAA,IAAA,EAAI,KAAK,EAAA,IAAA,CAAA,EAAA,CAAS,EACjD,QAAQ,KACPC,cAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,6BAA6B,EACvC,OAAO,EAAE,QAAQ,EAAA,YAAA,EACN,mBAAmB,EAAA,QAAA,EAAA,QAAA,EAAA,CAGvB,CACV,CAAA,EAAA,CACG;AAEV;AAEA;AACA,SAAS,kBAAkB,GAAA;IACzB,QACED,yBACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,aAAA,EACV,MAAM,EAAA,QAAA,EAAA,CAElBC,cAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,mBAAmB,EAAA,CAAG,EACvCA,cAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,yBAAyB,EAAA,CAAG,CAAA,EAAA,CAChC;AAEV;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ToolTimeline.js","sources":["../../../../../src/components/ChatDrawer/ToolTimeline.tsx"],"sourcesContent":["import React from 'react';\nimport type { ToolTimelineProps } from './ChatDrawer.types';\n\n/**\n * Tool execution timeline component\n */\nexport function ToolTimeline({ toolCalls }: ToolTimelineProps): JSX.Element | null {\n if (toolCalls.length === 0) {\n return null;\n }\n\n return (\n <div className=\"devic-tool-timeline\">\n {toolCalls.map((tool) => (\n <div key={tool.id} className=\"devic-tool-item\">\n <span className=\"devic-tool-status\" data-status={tool.status} />\n <span className=\"devic-tool-name\">{formatToolName(tool.name)}</span>\n {tool.status === 'error' && tool.error && (\n <span className=\"devic-tool-error\">{tool.error}</span>\n )}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Format tool name for display\n */\nfunction formatToolName(name: string): string {\n // Convert snake_case or camelCase to Title Case with spaces\n return name\n .replace(/_/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n"],"names":["_jsx","_jsxs"],"mappings":";;;;AAGA;;AAEG;AACG,SAAU,YAAY,CAAC,EAAE,SAAS,EAAqB,EAAA;AAC3D,IAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1B,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,QACEA,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,qBAAqB,EAAA,QAAA,EACjC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,MAClBC,eAAA,CAAA,KAAA,EAAA,EAAmB,SAAS,EAAC,iBAAiB,EAAA,QAAA,EAAA,CAC5CD,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,mBAAmB,EAAA,aAAA,EAAc,IAAI,CAAC,MAAM,GAAI,EAChEA,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,iBAAiB,EAAA,QAAA,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAA,CAAQ,EACnE,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,KACpCA,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,kBAAkB,EAAA,QAAA,EAAE,IAAI,CAAC,KAAK,GAAQ,CACvD,CAAA,EAAA,EALO,IAAI,CAAC,EAAE,CAMX,CACP,CAAC,EAAA,CACE;AAEV;AAEA;;AAEG;AACH,SAAS,cAAc,CAAC,IAAY,EAAA;;AAElC,IAAA,OAAO;AACJ,SAAA,OAAO,CAAC,IAAI,EAAE,GAAG;AACjB,SAAA,OAAO,CAAC,iBAAiB,EAAE,OAAO;AAClC,SAAA,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;AACnD;;;;"}
1
+ {"version":3,"file":"ToolTimeline.js","sources":["../../../../../src/components/ChatDrawer/ToolTimeline.tsx"],"sourcesContent":["import React from 'react';\nimport type { ToolTimelineProps } from './ChatDrawer.types';\n\n/**\n * Tool execution timeline component\n */\nexport function ToolTimeline({ toolCalls }: ToolTimelineProps): JSX.Element | null {\n if (toolCalls.length === 0) {\n return null;\n }\n\n return (\n <div className=\"devic-tool-timeline\">\n {toolCalls.map((tool) => (\n <div key={tool.id} className=\"devic-tool-item\">\n <span className=\"devic-tool-status\" data-status={tool.status} />\n <span className=\"devic-tool-name\">{formatToolName(tool.name)}</span>\n {tool.status === 'error' && tool.error && (\n <span className=\"devic-tool-error\">{tool.error}</span>\n )}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Format tool name for display\n */\nfunction formatToolName(name: string): string {\n // Convert snake_case or camelCase to Title Case with spaces\n return name\n .replace(/_/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n"],"names":["_jsx","_jsxs"],"mappings":";;;;AAGA;;AAEG;AACG,SAAU,YAAY,CAAC,EAAE,SAAS,EAAqB,EAAA;AAC3D,IAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1B,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,QACEA,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,qBAAqB,EAAA,QAAA,EACjC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,MAClBC,eAAA,CAAA,KAAA,EAAA,EAAmB,SAAS,EAAC,iBAAiB,EAAA,QAAA,EAAA,CAC5CD,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,mBAAmB,EAAA,aAAA,EAAc,IAAI,CAAC,MAAM,GAAI,EAChEA,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,iBAAiB,EAAA,QAAA,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAA,CAAQ,EACnE,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,KACpCA,cAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,kBAAkB,EAAA,QAAA,EAAE,IAAI,CAAC,KAAK,GAAQ,CACvD,CAAA,EAAA,EALO,IAAI,CAAC,EAAE,CAMX,CACP,CAAC,EAAA,CACE;AAEV;AAEA;;AAEG;AACH,SAAS,cAAc,CAAC,IAAY,EAAA;;AAElC,IAAA,OAAO;AACJ,SAAA,OAAO,CAAC,IAAI,EAAE,GAAG;AACjB,SAAA,OAAO,CAAC,iBAAiB,EAAE,OAAO;AAClC,SAAA,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;AACnD;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"UsageBar.js","sources":["../../../../../src/components/ChatDrawer/UsageBar.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useOptionalDevicContext } from '../../provider';\nimport { DevicApiClient } from '../../api/client';\nimport type { TenantUsage, TenantUsageRule } from '../../api/types';\n\n/**\n * Controls what each usage row renders. All flags compose, so a developer can,\n * for example, show absolute values together with the percentage and the tier.\n */\nexport interface UsageBarDisplay {\n /**\n * Show the absolute amount of the active window: `current / limit metric`\n * (e.g. `1,200 / 5,000 tokens`).\n * @default false\n */\n showValues?: boolean;\n /**\n * Show the utilization percentage (e.g. `24%`).\n * @default true\n */\n showPercent?: boolean;\n /**\n * Show the temporal dimension of the window (e.g. `per 3h`, `every 3 hours`).\n * @default true\n */\n showWindow?: boolean;\n /**\n * Show the tier (plan) the limits come from, as a chip above the bar(s).\n * @default false\n */\n showTier?: boolean;\n /**\n * When more than one limit rule applies, render every rule (`true`) or only\n * the single most restrictive one (`false`, highest utilization). The\n * per-row presentation still follows the flags above.\n * @default true\n */\n showAllRules?: boolean;\n}\n\nconst DEFAULT_DISPLAY: Required<UsageBarDisplay> = {\n showValues: false,\n showPercent: true,\n showWindow: true,\n showTier: false,\n showAllRules: true,\n};\n\n/**\n * Usage data handed to a `customUsageBar` renderer so a developer can build\n * their own component with the live consumption / limits.\n */\nexport interface UsageBarData {\n /** Tenant the usage belongs to. */\n tenantId?: string;\n /** Subtenant the usage is scoped to, if any. */\n subtenantId?: string;\n /** Tier (plan) the limits come from, if any. */\n tierId?: string;\n /**\n * Applicable usage rules (filtered by `metric` when set), sorted by\n * utilization descending — the most restrictive first.\n */\n rules: TenantUsageRule[];\n /** `true` until the first fetch resolves. */\n loading: boolean;\n}\n\n/**\n * Props for the UsageBar component.\n */\nexport interface UsageBarProps {\n /** API key (resolved from provider by the ChatDrawer). */\n apiKey?: string;\n /** Base URL (resolved from provider by the ChatDrawer). */\n baseUrl?: string;\n /** Tenant whose usage is displayed. Required — without it the bar renders nothing. */\n tenantId?: string;\n /** Optional subtenant to scope the usage to a specific end user. */\n subtenantId?: string;\n /**\n * Display mode:\n * - 'always': the bar is always visible.\n * - 'onDemand': a small toggle button is shown; the bar appears on click.\n */\n mode?: 'always' | 'onDemand';\n /** Restrict the bar to a single metric. When omitted, all applicable rules are considered. */\n metric?: 'tokens' | 'cost';\n /**\n * What to render per rule (values / percentage / window / tier) and whether\n * to render all applicable rules or only the most restrictive one.\n */\n display?: UsageBarDisplay;\n /**\n * Render your own component instead of the built-in bar(s). Receives the live\n * usage data (rules + tier + loading) and renders in the same slot, replacing\n * the default UI entirely. When set, fetching/polling is still handled here;\n * `mode`/`display` only affect the default UI and are ignored.\n */\n customUsageBar?: (data: UsageBarData) => React.ReactNode;\n /** Primary color for the bar fill (below the warning threshold). */\n color?: string;\n /**\n * Changes to this value trigger a refetch (e.g. the message count, so the bar\n * refreshes after each assistant turn).\n */\n refreshKey?: number | string;\n /** Enable debug logging of fetch errors to the console. */\n debug?: boolean;\n}\n\nconst WARN_THRESHOLD = 90;\n\nfunction fillColor(percent: number, base: string): string {\n if (percent >= 100) return '#ff4d4f';\n if (percent >= WARN_THRESHOLD) return '#faad14';\n return base;\n}\n\nfunction formatAmount(value: number, metric: 'tokens' | 'cost'): string {\n if (metric === 'cost') {\n return `$${value.toFixed(value < 1 ? 4 : 2)}`;\n }\n return Math.round(value).toLocaleString();\n}\n\nfunction windowLabel(unit: string, every: number): string {\n const u = every > 1 ? `${unit}s` : unit;\n return every > 1 ? `every ${every} ${u}` : `per ${unit}`;\n}\n\nfunction resetLabel(resetsAt?: number): string {\n if (!resetsAt) return '';\n const ms = resetsAt - Date.now();\n if (ms <= 0) return 'resets now';\n const mins = Math.round(ms / 60000);\n if (mins < 60) return `resets in ${mins}m`;\n const hours = Math.round(mins / 60);\n if (hours < 48) return `resets in ${hours}h`;\n const days = Math.round(hours / 24);\n return `resets in ${days}d`;\n}\n\n/**\n * A thin usage bar showing the current tenant/subtenant usage window(s).\n * Rendered above the chat input. Antd-free — styled via `.devic-usage-*` classes.\n * Renders nothing when there are no usage limits configured for the tenant.\n */\nexport function UsageBar({\n apiKey,\n baseUrl,\n tenantId,\n subtenantId,\n mode = 'always',\n metric,\n display,\n customUsageBar,\n color = '#1890ff',\n refreshKey,\n debug = false,\n}: UsageBarProps): JSX.Element | null {\n const cfg = useMemo(() => ({ ...DEFAULT_DISPLAY, ...display }), [display]);\n\n const devicContext = useOptionalDevicContext();\n const getTenantSession = devicContext?.getTenantSession;\n const onSessionExpired = devicContext?.onSessionExpired;\n const client = useMemo(() => {\n if (!apiKey && !getTenantSession) return null;\n return new DevicApiClient({\n apiKey,\n baseUrl: baseUrl || 'https://api.devic.ai',\n getTenantSession,\n onSessionExpired,\n });\n }, [apiKey, getTenantSession, baseUrl]);\n\n const [usage, setUsage] = useState<TenantUsage | null>(null);\n const [loaded, setLoaded] = useState(false);\n // onDemand starts hidden; 'always' is effectively visible from the start.\n const [visible, setVisible] = useState(mode !== 'onDemand');\n\n const fetchUsage = useCallback(async () => {\n if (!client || !tenantId) return;\n try {\n const data = await client.getTenantUsage(tenantId, subtenantId);\n setUsage(data || null);\n } catch (err) {\n if (debug) console.warn('[UsageBar] getTenantUsage failed:', err);\n setUsage(null);\n } finally {\n setLoaded(true);\n }\n }, [client, tenantId, subtenantId, debug]);\n\n // A custom renderer always wants the data, regardless of the onDemand toggle.\n const active = !!customUsageBar || visible;\n\n useEffect(() => {\n if (!active) return;\n void fetchUsage();\n }, [active, fetchUsage, refreshKey]);\n\n // Applicable rules: filter by metric, sort most-restrictive first.\n const filteredSorted = useMemo(() => {\n const pool = (usage?.usage || []).filter(\n (r) => !metric || r.metric === metric,\n );\n return [...pool].sort((a, b) => b.percent - a.percent);\n }, [usage, metric]);\n\n // Default UI honours showAllRules; custom renderers always get the full set.\n const rules = useMemo(\n () => (cfg.showAllRules ? filteredSorted : filteredSorted.slice(0, 1)),\n [filteredSorted, cfg.showAllRules],\n );\n\n const tierId = usage?.tierId || filteredSorted[0]?.tierId;\n\n if (!tenantId || !client) return null;\n\n // Custom renderer fully replaces the default UI; hand it the live data.\n if (customUsageBar) {\n return (\n <>\n {customUsageBar({\n tenantId,\n subtenantId,\n tierId,\n rules: filteredSorted,\n loading: !loaded,\n })}\n </>\n );\n }\n\n // onDemand, collapsed: show a small toggle button.\n if (mode === 'onDemand' && !visible) {\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode=\"onDemand\">\n <button\n type=\"button\"\n className=\"devic-usage-toggle\"\n onClick={() => setVisible(true)}\n title=\"Show usage\"\n >\n <GaugeIcon />\n <span>Usage</span>\n </button>\n </div>\n );\n }\n\n // No configured limits (or not loaded yet with nothing to show): render nothing\n // in 'always' mode; in expanded onDemand, show a subtle note so the toggle isn't\n // confusing.\n if (rules.length === 0) {\n if (mode === 'onDemand' && loaded) {\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode=\"onDemand\">\n <div className=\"devic-usage-bar\" data-empty=\"true\">\n <span className=\"devic-usage-bar-label\">No usage limits</span>\n <button\n type=\"button\"\n className=\"devic-usage-collapse\"\n onClick={() => setVisible(false)}\n title=\"Hide usage\"\n aria-label=\"Hide usage\"\n >\n &times;\n </button>\n </div>\n </div>\n );\n }\n return null;\n }\n\n const showTierChip = cfg.showTier && !!tierId;\n const multi = rules.length > 1;\n // A header carries the tier chip and/or the onDemand collapse control, so the\n // collapse \"×\" has a stable home regardless of how many rows render below.\n const header =\n showTierChip || mode === 'onDemand' ? (\n <div className=\"devic-usage-bar-head\">\n {showTierChip ? (\n <span className=\"devic-usage-tier\" title={`Tier: ${tierId}`}>\n {tierId}\n </span>\n ) : (\n <span />\n )}\n {mode === 'onDemand' && (\n <button\n type=\"button\"\n className=\"devic-usage-collapse\"\n onClick={() => setVisible(false)}\n title=\"Hide usage\"\n aria-label=\"Hide usage\"\n >\n &times;\n </button>\n )}\n </div>\n ) : null;\n\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode={mode}>\n {header}\n {/* All rows share a single container by default. */}\n <div className=\"devic-usage-bar-group\">\n {rules.map((rule, i) => {\n const pct = Math.min(100, Math.round(rule.percent));\n const fill = fillColor(pct, color);\n const valuesText = `${formatAmount(rule.current, rule.metric)} / ${formatAmount(\n rule.limit,\n rule.metric,\n )} ${rule.metric}`;\n // Distinguish rows by metric when several render; otherwise use the\n // scope-aware label so single-rule mode reads \"Usage 24%\".\n const prefix = multi\n ? rule.metric === 'cost'\n ? 'Cost'\n : 'Tokens'\n : rule.scope === 'subtenant'\n ? 'Your usage'\n : 'Usage';\n const main: string[] = [];\n if (cfg.showPercent) main.push(`${pct}%`);\n if (cfg.showValues) main.push(valuesText);\n if (main.length === 0) main.push(`${pct}%`);\n const label = `${prefix} ${main.join(' · ')}`.trim();\n const reset = resetLabel(rule.resetsAt);\n const win = windowLabel(rule.windowUnit, rule.windowEvery);\n const title = `${valuesText} · ${win}${reset ? ` · ${reset}` : ''}`;\n\n return (\n <div\n key={`${rule.metric}-${rule.windowUnit}-${rule.windowEvery}-${rule.scope}-${i}`}\n className=\"devic-usage-bar\"\n title={title}\n data-state={pct >= 100 ? 'exceeded' : 'ok'}\n >\n <span className=\"devic-usage-bar-label\">{label}</span>\n <div className=\"devic-usage-bar-track\">\n <div\n className=\"devic-usage-bar-fill\"\n style={{ width: `${pct}%`, backgroundColor: fill }}\n />\n </div>\n {cfg.showWindow && (\n <span className=\"devic-usage-bar-window\">{win}</span>\n )}\n {reset && <span className=\"devic-usage-bar-reset\">{reset}</span>}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n/** Small gauge icon for the on-demand toggle. */\nfunction GaugeIcon(): 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 <path d=\"M12 14a4 4 0 1 0-4-4\" />\n <path d=\"M3 18a9 9 0 0 1 18 0\" />\n <line x1=\"12\" y1=\"14\" x2=\"15\" y2=\"11\" />\n </svg>\n );\n}\n"],"names":["useMemo","useOptionalDevicContext","client","DevicApiClient","useState","useCallback","useEffect","_jsx","_Fragment","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"UsageBar.js","sources":["../../../../../src/components/ChatDrawer/UsageBar.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useOptionalDevicContext } from '../../provider';\nimport { DevicApiClient } from '../../api/client';\nimport type { TenantUsage, TenantUsageRule } from '../../api/types';\n\n/**\n * Controls what each usage row renders. All flags compose, so a developer can,\n * for example, show absolute values together with the percentage and the tier.\n */\nexport interface UsageBarDisplay {\n /**\n * Show the absolute amount of the active window: `current / limit metric`\n * (e.g. `1,200 / 5,000 tokens`).\n * @default false\n */\n showValues?: boolean;\n /**\n * Show the utilization percentage (e.g. `24%`).\n * @default true\n */\n showPercent?: boolean;\n /**\n * Show the temporal dimension of the window (e.g. `per 3h`, `every 3 hours`).\n * @default true\n */\n showWindow?: boolean;\n /**\n * Show the tier (plan) the limits come from, as a chip above the bar(s).\n * @default false\n */\n showTier?: boolean;\n /**\n * When more than one limit rule applies, render every rule (`true`) or only\n * the single most restrictive one (`false`, highest utilization). The\n * per-row presentation still follows the flags above.\n * @default true\n */\n showAllRules?: boolean;\n}\n\nconst DEFAULT_DISPLAY: Required<UsageBarDisplay> = {\n showValues: false,\n showPercent: true,\n showWindow: true,\n showTier: false,\n showAllRules: true,\n};\n\n/**\n * Usage data handed to a `customUsageBar` renderer so a developer can build\n * their own component with the live consumption / limits.\n */\nexport interface UsageBarData {\n /** Tenant the usage belongs to. */\n tenantId?: string;\n /** Subtenant the usage is scoped to, if any. */\n subtenantId?: string;\n /** Tier (plan) the limits come from, if any. */\n tierId?: string;\n /**\n * Applicable usage rules (filtered by `metric` when set), sorted by\n * utilization descending — the most restrictive first.\n */\n rules: TenantUsageRule[];\n /** `true` until the first fetch resolves. */\n loading: boolean;\n}\n\n/**\n * Props for the UsageBar component.\n */\nexport interface UsageBarProps {\n /** API key (resolved from provider by the ChatDrawer). */\n apiKey?: string;\n /** Base URL (resolved from provider by the ChatDrawer). */\n baseUrl?: string;\n /** Tenant whose usage is displayed. Required — without it the bar renders nothing. */\n tenantId?: string;\n /** Optional subtenant to scope the usage to a specific end user. */\n subtenantId?: string;\n /**\n * Display mode:\n * - 'always': the bar is always visible.\n * - 'onDemand': a small toggle button is shown; the bar appears on click.\n */\n mode?: 'always' | 'onDemand';\n /** Restrict the bar to a single metric. When omitted, all applicable rules are considered. */\n metric?: 'tokens' | 'cost';\n /**\n * What to render per rule (values / percentage / window / tier) and whether\n * to render all applicable rules or only the most restrictive one.\n */\n display?: UsageBarDisplay;\n /**\n * Render your own component instead of the built-in bar(s). Receives the live\n * usage data (rules + tier + loading) and renders in the same slot, replacing\n * the default UI entirely. When set, fetching/polling is still handled here;\n * `mode`/`display` only affect the default UI and are ignored.\n */\n customUsageBar?: (data: UsageBarData) => React.ReactNode;\n /** Primary color for the bar fill (below the warning threshold). */\n color?: string;\n /**\n * Changes to this value trigger a refetch (e.g. the message count, so the bar\n * refreshes after each assistant turn).\n */\n refreshKey?: number | string;\n /** Enable debug logging of fetch errors to the console. */\n debug?: boolean;\n}\n\nconst WARN_THRESHOLD = 90;\n\nfunction fillColor(percent: number, base: string): string {\n if (percent >= 100) return '#ff4d4f';\n if (percent >= WARN_THRESHOLD) return '#faad14';\n return base;\n}\n\nfunction formatAmount(value: number, metric: 'tokens' | 'cost'): string {\n if (metric === 'cost') {\n return `$${value.toFixed(value < 1 ? 4 : 2)}`;\n }\n return Math.round(value).toLocaleString();\n}\n\nfunction windowLabel(unit: string, every: number): string {\n const u = every > 1 ? `${unit}s` : unit;\n return every > 1 ? `every ${every} ${u}` : `per ${unit}`;\n}\n\nfunction resetLabel(resetsAt?: number): string {\n if (!resetsAt) return '';\n const ms = resetsAt - Date.now();\n if (ms <= 0) return 'resets now';\n const mins = Math.round(ms / 60000);\n if (mins < 60) return `resets in ${mins}m`;\n const hours = Math.round(mins / 60);\n if (hours < 48) return `resets in ${hours}h`;\n const days = Math.round(hours / 24);\n return `resets in ${days}d`;\n}\n\n/**\n * A thin usage bar showing the current tenant/subtenant usage window(s).\n * Rendered above the chat input. Antd-free — styled via `.devic-usage-*` classes.\n * Renders nothing when there are no usage limits configured for the tenant.\n */\nexport function UsageBar({\n apiKey,\n baseUrl,\n tenantId,\n subtenantId,\n mode = 'always',\n metric,\n display,\n customUsageBar,\n color = '#1890ff',\n refreshKey,\n debug = false,\n}: UsageBarProps): JSX.Element | null {\n const cfg = useMemo(() => ({ ...DEFAULT_DISPLAY, ...display }), [display]);\n\n const devicContext = useOptionalDevicContext();\n const getTenantSession = devicContext?.getTenantSession;\n const onSessionExpired = devicContext?.onSessionExpired;\n const client = useMemo(() => {\n if (!apiKey && !getTenantSession) return null;\n return new DevicApiClient({\n apiKey,\n baseUrl: baseUrl || 'https://api.devic.ai',\n getTenantSession,\n onSessionExpired,\n });\n }, [apiKey, getTenantSession, baseUrl]);\n\n const [usage, setUsage] = useState<TenantUsage | null>(null);\n const [loaded, setLoaded] = useState(false);\n // onDemand starts hidden; 'always' is effectively visible from the start.\n const [visible, setVisible] = useState(mode !== 'onDemand');\n\n const fetchUsage = useCallback(async () => {\n if (!client || !tenantId) return;\n try {\n const data = await client.getTenantUsage(tenantId, subtenantId);\n setUsage(data || null);\n } catch (err) {\n if (debug) console.warn('[UsageBar] getTenantUsage failed:', err);\n setUsage(null);\n } finally {\n setLoaded(true);\n }\n }, [client, tenantId, subtenantId, debug]);\n\n // A custom renderer always wants the data, regardless of the onDemand toggle.\n const active = !!customUsageBar || visible;\n\n useEffect(() => {\n if (!active) return;\n void fetchUsage();\n }, [active, fetchUsage, refreshKey]);\n\n // Applicable rules: filter by metric, sort most-restrictive first.\n const filteredSorted = useMemo(() => {\n const pool = (usage?.usage || []).filter(\n (r) => !metric || r.metric === metric,\n );\n return [...pool].sort((a, b) => b.percent - a.percent);\n }, [usage, metric]);\n\n // Default UI honours showAllRules; custom renderers always get the full set.\n const rules = useMemo(\n () => (cfg.showAllRules ? filteredSorted : filteredSorted.slice(0, 1)),\n [filteredSorted, cfg.showAllRules],\n );\n\n const tierId = usage?.tierId || filteredSorted[0]?.tierId;\n\n if (!tenantId || !client) return null;\n\n // Custom renderer fully replaces the default UI; hand it the live data.\n if (customUsageBar) {\n return (\n <>\n {customUsageBar({\n tenantId,\n subtenantId,\n tierId,\n rules: filteredSorted,\n loading: !loaded,\n })}\n </>\n );\n }\n\n // onDemand, collapsed: show a small toggle button.\n if (mode === 'onDemand' && !visible) {\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode=\"onDemand\">\n <button\n type=\"button\"\n className=\"devic-usage-toggle\"\n onClick={() => setVisible(true)}\n title=\"Show usage\"\n >\n <GaugeIcon />\n <span>Usage</span>\n </button>\n </div>\n );\n }\n\n // No configured limits (or not loaded yet with nothing to show): render nothing\n // in 'always' mode; in expanded onDemand, show a subtle note so the toggle isn't\n // confusing.\n if (rules.length === 0) {\n if (mode === 'onDemand' && loaded) {\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode=\"onDemand\">\n <div className=\"devic-usage-bar\" data-empty=\"true\">\n <span className=\"devic-usage-bar-label\">No usage limits</span>\n <button\n type=\"button\"\n className=\"devic-usage-collapse\"\n onClick={() => setVisible(false)}\n title=\"Hide usage\"\n aria-label=\"Hide usage\"\n >\n &times;\n </button>\n </div>\n </div>\n );\n }\n return null;\n }\n\n const showTierChip = cfg.showTier && !!tierId;\n const multi = rules.length > 1;\n // A header carries the tier chip and/or the onDemand collapse control, so the\n // collapse \"×\" has a stable home regardless of how many rows render below.\n const header =\n showTierChip || mode === 'onDemand' ? (\n <div className=\"devic-usage-bar-head\">\n {showTierChip ? (\n <span className=\"devic-usage-tier\" title={`Tier: ${tierId}`}>\n {tierId}\n </span>\n ) : (\n <span />\n )}\n {mode === 'onDemand' && (\n <button\n type=\"button\"\n className=\"devic-usage-collapse\"\n onClick={() => setVisible(false)}\n title=\"Hide usage\"\n aria-label=\"Hide usage\"\n >\n &times;\n </button>\n )}\n </div>\n ) : null;\n\n return (\n <div className=\"devic-usage-bar-wrap\" data-mode={mode}>\n {header}\n {/* All rows share a single container by default. */}\n <div className=\"devic-usage-bar-group\">\n {rules.map((rule, i) => {\n const pct = Math.min(100, Math.round(rule.percent));\n const fill = fillColor(pct, color);\n const valuesText = `${formatAmount(rule.current, rule.metric)} / ${formatAmount(\n rule.limit,\n rule.metric,\n )} ${rule.metric}`;\n // Distinguish rows by metric when several render; otherwise use the\n // scope-aware label so single-rule mode reads \"Usage 24%\".\n const prefix = multi\n ? rule.metric === 'cost'\n ? 'Cost'\n : 'Tokens'\n : rule.scope === 'subtenant'\n ? 'Your usage'\n : 'Usage';\n const main: string[] = [];\n if (cfg.showPercent) main.push(`${pct}%`);\n if (cfg.showValues) main.push(valuesText);\n if (main.length === 0) main.push(`${pct}%`);\n const label = `${prefix} ${main.join(' · ')}`.trim();\n const reset = resetLabel(rule.resetsAt);\n const win = windowLabel(rule.windowUnit, rule.windowEvery);\n const title = `${valuesText} · ${win}${reset ? ` · ${reset}` : ''}`;\n\n return (\n <div\n key={`${rule.metric}-${rule.windowUnit}-${rule.windowEvery}-${rule.scope}-${i}`}\n className=\"devic-usage-bar\"\n title={title}\n data-state={pct >= 100 ? 'exceeded' : 'ok'}\n >\n <span className=\"devic-usage-bar-label\">{label}</span>\n <div className=\"devic-usage-bar-track\">\n <div\n className=\"devic-usage-bar-fill\"\n style={{ width: `${pct}%`, backgroundColor: fill }}\n />\n </div>\n {cfg.showWindow && (\n <span className=\"devic-usage-bar-window\">{win}</span>\n )}\n {reset && <span className=\"devic-usage-bar-reset\">{reset}</span>}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n/** Small gauge icon for the on-demand toggle. */\nfunction GaugeIcon(): 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 <path d=\"M12 14a4 4 0 1 0-4-4\" />\n <path d=\"M3 18a9 9 0 0 1 18 0\" />\n <line x1=\"12\" y1=\"14\" x2=\"15\" y2=\"11\" />\n </svg>\n );\n}\n"],"names":["useMemo","useOptionalDevicContext","client","DevicApiClient","useState","useCallback","useEffect","_jsx","_Fragment","_jsxs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pastedText.js","sources":["../../../../../src/components/ChatDrawer/pastedText.ts"],"sourcesContent":["/**\n * Shared contract for long blocks of pasted text.\n *\n * A long paste is shown in the prompt box as an attachment card instead of\n * flooding the textarea, but its content still reaches the model in full: on\n * send it is prepended to the message inside a <pasted-text> block. The thread\n * parses those blocks back out so the bubble shows a card too, rather than\n * thousands of lines of raw text.\n *\n * Both sides of that round-trip live here so they can never drift apart.\n */\n\nexport interface PastedText {\n id: string;\n text: string;\n}\n\n/**\n * A pasted block could itself contain the literal closing tag (pasting this\n * very file, say), which would truncate the block when parsed back. Escaping it\n * on the way out keeps the round-trip exact and still reads as the intended\n * text to the model.\n */\nconst CLOSING_TAG = '</pasted-text>';\nconst ESCAPED_CLOSING_TAG = '<\\\\/pasted-text>';\n\nconst PASTED_BLOCK_RE = /<pasted-text id=\"([^\"]*)\">\\n([\\s\\S]*?)\\n<\\/pasted-text>/g;\n\n/** Serialize a pasted block for the message sent to the assistant. */\nexport function toPastedBlock({ id, text }: PastedText): string {\n const escaped = text.split(CLOSING_TAG).join(ESCAPED_CLOSING_TAG);\n return `<pasted-text id=\"${id}\">\\n${escaped}\\n${CLOSING_TAG}`;\n}\n\n/**\n * Pull the pasted blocks out of a user message, returning them alongside the\n * message with those blocks removed. Returns null when there are none, so\n * callers can keep rendering untouched messages exactly as before.\n */\nexport function parsePastedBlocks(\n content: string\n): { blocks: PastedText[]; cleanContent: string } | null {\n const blocks: PastedText[] = [];\n const cleanContent = content\n .replace(PASTED_BLOCK_RE, (_match, id: string, text: string) => {\n blocks.push({\n id,\n text: text.split(ESCAPED_CLOSING_TAG).join(CLOSING_TAG),\n });\n return '';\n })\n .trim();\n\n if (blocks.length === 0) return null;\n return { blocks, cleanContent };\n}\n\n/** Short, single-line-ish excerpt shown on the collapsed card. */\nexport function pastedPreview(text: string, maxChars = 160): string {\n const collapsed = text.replace(/\\s+/g, ' ').trim();\n return collapsed.length > maxChars\n ? `${collapsed.slice(0, maxChars)}…`\n : collapsed;\n}\n\n/** Line count shown on the card, mirroring how editors report a selection. */\nexport function pastedLineCount(text: string): number {\n return text.split('\\n').length;\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;AAUG;AAOH;;;;;AAKG;AACH,MAAM,WAAW,GAAG,gBAAgB;AACpC,MAAM,mBAAmB,GAAG,kBAAkB;AAE9C,MAAM,eAAe,GAAG,0DAA0D;AAElF;SACgB,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAc,EAAA;AACpD,IAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;AACjE,IAAA,OAAO,oBAAoB,EAAE,CAAA,IAAA,EAAO,OAAO,CAAA,EAAA,EAAK,WAAW,EAAE;AAC/D;AAEA;;;;AAIG;AACG,SAAU,iBAAiB,CAC/B,OAAe,EAAA;IAEf,MAAM,MAAM,GAAiB,EAAE;IAC/B,MAAM,YAAY,GAAG;SAClB,OAAO,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,EAAU,EAAE,IAAY,KAAI;QAC7D,MAAM,CAAC,IAAI,CAAC;YACV,EAAE;YACF,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;AACxD,SAAA,CAAC;AACF,QAAA,OAAO,EAAE;AACX,IAAA,CAAC;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;AACpC,IAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE;AACjC;AAEA;SACgB,aAAa,CAAC,IAAY,EAAE,QAAQ,GAAG,GAAG,EAAA;AACxD,IAAA,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;AAClD,IAAA,OAAO,SAAS,CAAC,MAAM,GAAG;UACtB,CAAA,EAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA,CAAA;UAC/B,SAAS;AACf;AAEA;AACM,SAAU,eAAe,CAAC,IAAY,EAAA;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM;AAChC;;;;;;;"}
1
+ {"version":3,"file":"pastedText.js","sources":["../../../../../src/components/ChatDrawer/pastedText.ts"],"sourcesContent":["/**\n * Shared contract for long blocks of pasted text.\n *\n * A long paste is shown in the prompt box as an attachment card instead of\n * flooding the textarea, but its content still reaches the model in full: on\n * send it is prepended to the message inside a <pasted-text> block. The thread\n * parses those blocks back out so the bubble shows a card too, rather than\n * thousands of lines of raw text.\n *\n * Both sides of that round-trip live here so they can never drift apart.\n */\n\nexport interface PastedText {\n id: string;\n text: string;\n}\n\n/**\n * A pasted block could itself contain the literal closing tag (pasting this\n * very file, say), which would truncate the block when parsed back. Escaping it\n * on the way out keeps the round-trip exact and still reads as the intended\n * text to the model.\n */\nconst CLOSING_TAG = '</pasted-text>';\nconst ESCAPED_CLOSING_TAG = '<\\\\/pasted-text>';\n\nconst PASTED_BLOCK_RE = /<pasted-text id=\"([^\"]*)\">\\n([\\s\\S]*?)\\n<\\/pasted-text>/g;\n\n/** Serialize a pasted block for the message sent to the assistant. */\nexport function toPastedBlock({ id, text }: PastedText): string {\n const escaped = text.split(CLOSING_TAG).join(ESCAPED_CLOSING_TAG);\n return `<pasted-text id=\"${id}\">\\n${escaped}\\n${CLOSING_TAG}`;\n}\n\n/**\n * Pull the pasted blocks out of a user message, returning them alongside the\n * message with those blocks removed. Returns null when there are none, so\n * callers can keep rendering untouched messages exactly as before.\n */\nexport function parsePastedBlocks(\n content: string\n): { blocks: PastedText[]; cleanContent: string } | null {\n const blocks: PastedText[] = [];\n const cleanContent = content\n .replace(PASTED_BLOCK_RE, (_match, id: string, text: string) => {\n blocks.push({\n id,\n text: text.split(ESCAPED_CLOSING_TAG).join(CLOSING_TAG),\n });\n return '';\n })\n .trim();\n\n if (blocks.length === 0) return null;\n return { blocks, cleanContent };\n}\n\n/** Short, single-line-ish excerpt shown on the collapsed card. */\nexport function pastedPreview(text: string, maxChars = 160): string {\n const collapsed = text.replace(/\\s+/g, ' ').trim();\n return collapsed.length > maxChars\n ? `${collapsed.slice(0, maxChars)}…`\n : collapsed;\n}\n\n/** Line count shown on the card, mirroring how editors report a selection. */\nexport function pastedLineCount(text: string): number {\n return text.split('\\n').length;\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;AAUG;AAOH;;;;;AAKG;AACH,MAAM,WAAW,GAAG,gBAAgB;AACpC,MAAM,mBAAmB,GAAG,kBAAkB;AAE9C,MAAM,eAAe,GAAG,0DAA0D;AAElF;SACgB,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAc,EAAA;AACpD,IAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;AACjE,IAAA,OAAO,oBAAoB,EAAE,CAAA,IAAA,EAAO,OAAO,CAAA,EAAA,EAAK,WAAW,EAAE;AAC/D;AAEA;;;;AAIG;AACG,SAAU,iBAAiB,CAC/B,OAAe,EAAA;IAEf,MAAM,MAAM,GAAiB,EAAE;IAC/B,MAAM,YAAY,GAAG;SAClB,OAAO,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,EAAU,EAAE,IAAY,KAAI;QAC7D,MAAM,CAAC,IAAI,CAAC;YACV,EAAE;YACF,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;AACxD,SAAA,CAAC;AACF,QAAA,OAAO,EAAE;AACX,IAAA,CAAC;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;AACpC,IAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE;AACjC;AAEA;SACgB,aAAa,CAAC,IAAY,EAAE,QAAQ,GAAG,GAAG,EAAA;AACxD,IAAA,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;AAClD,IAAA,OAAO,SAAS,CAAC,MAAM,GAAG;UACtB,CAAA,EAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA,CAAA;UAC/B,SAAS;AACf;AAEA;AACM,SAAU,eAAe,CAAC,IAAY,EAAA;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM;AAChC;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoreMemoryModal.js","sources":["../../../../../src/components/CoreMemoryModal/CoreMemoryModal.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { DevicApiClient } from \"../../api/client\";\nimport type { CoreMemoryEntry, CoreMemoryLimits } from \"../../api/types\";\nimport { useOptionalDevicContext } from \"../../provider\";\nimport { themeVars, type DevicTheme } from \"../theme\";\nimport \"./CoreMemoryModal.css\";\n\n/** Canonical sections of the core tier, in display order. */\nconst SECTIONS = [\"persona\", \"instructions\", \"decisions\", \"profile\"] as const;\n\nexport interface CoreMemoryModalProps {\n /** Whether the modal is visible. */\n isOpen: boolean;\n onClose: () => void;\n /** Assistant whose memory bucket is shown. */\n assistantId: string;\n /** Tenant of the end user (falls back to the provider's tenantId). */\n tenantId?: string;\n /** Subtenant of the end user (falls back to the provider's subtenantId). */\n subtenantId?: string;\n /** API key override (falls back to the provider's). */\n apiKey?: string;\n /** Base URL override (falls back to the provider's). */\n baseUrl?: string;\n /**\n * Let the user add, edit and remove entries. Set false for a read-only\n * view of what the assistant remembers.\n * @default true\n */\n editable?: boolean;\n /** Modal title. @default \"Assistant memory\" */\n title?: string;\n /**\n * Colours and font. Same names as the drawer's style options, and the drawer\n * passes its own down — a dialog opening in the default light palette over a\n * themed application is the one thing this must not do.\n */\n theme?: DevicTheme;\n}\n\nfunction BrainIcon(): 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 aria-hidden=\"true\"\n >\n <path d=\"M9.5 2a2.5 2.5 0 0 0-2.45 2A3.5 3.5 0 0 0 4.6 8.6 3.5 3.5 0 0 0 3 11.5c0 1.1.5 2.08 1.29 2.73A3.5 3.5 0 0 0 7 20a3 3 0 0 0 5-2.24V4.5A2.5 2.5 0 0 0 9.5 2Z\" />\n <path d=\"M14.5 2a2.5 2.5 0 0 1 2.45 2 3.5 3.5 0 0 1 2.45 4.6A3.5 3.5 0 0 1 21 11.5a3.49 3.49 0 0 1-1.29 2.73A3.5 3.5 0 0 1 17 20a3 3 0 0 1-5-2.24V4.5A2.5 2.5 0 0 1 14.5 2Z\" />\n </svg>\n );\n}\n\nfunction PinIcon(): JSX.Element {\n return (\n <svg\n width=\"11\"\n height=\"11\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M16 3a1 1 0 0 1 .97 1.243l-1.2 4.8 3.937 3.938A1 1 0 0 1 19 14.688V15a1 1 0 0 1-1 1h-5v6l-1 1-1-1v-6H6a1 1 0 0 1-1-1v-.312a1 1 0 0 1 .293-.707L9.23 9.043l-1.2-4.8A1 1 0 0 1 9 3h7Z\" />\n </svg>\n );\n}\n\n/**\n * Modal showing — and, when `editable`, managing — the core memory of an\n * assistant for a tenant/subtenant combination: the standing entries\n * (persona, instructions, decisions, profile) injected into every prompt of\n * the bucket the assistant resolves there. Lets the end user see and control\n * what the assistant permanently remembers about them.\n *\n * Backed by the public memory API (`/api/v1/memory/assistants/:id/core`);\n * the API key needs that endpoint allowed.\n */\nexport function CoreMemoryModal({\n isOpen,\n onClose,\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n editable = true,\n title = \"Assistant memory\",\n theme,\n}: CoreMemoryModalProps): JSX.Element | null {\n const context = useOptionalDevicContext();\n const resolvedBaseUrl = baseUrl || context?.baseUrl || \"https://api.devic.ai\";\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedSubtenantId = subtenantId || context?.subtenantId;\n\n const resolvedApiKey = apiKey || context?.apiKey;\n const resolvedTenantSession = context?.getTenantSession;\n const onSessionExpired = context?.onSessionExpired;\n\n const client = useMemo(\n () =>\n resolvedApiKey || resolvedTenantSession\n ? new DevicApiClient({\n apiKey: resolvedApiKey,\n baseUrl: resolvedBaseUrl,\n getTenantSession: resolvedTenantSession,\n onSessionExpired,\n })\n : null,\n [resolvedApiKey, resolvedTenantSession, resolvedBaseUrl]\n );\n\n const [loading, setLoading] = useState(false);\n const [enabled, setEnabled] = useState(true);\n const [entries, setEntries] = useState<CoreMemoryEntry[]>([]);\n const [limits, setLimits] = useState<CoreMemoryLimits | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n\n // Inline composer / editor state\n const [composerOpen, setComposerOpen] = useState(false);\n const [composerSection, setComposerSection] = useState<string>(\"profile\");\n const [composerText, setComposerText] = useState(\"\");\n const [editingId, setEditingId] = useState<number | null>(null);\n const [editingText, setEditingText] = useState(\"\");\n\n const scopeOptions = useMemo(\n () => ({\n tenantId: resolvedTenantId || undefined,\n subtenantId: resolvedSubtenantId || undefined,\n }),\n [resolvedTenantId, resolvedSubtenantId]\n );\n\n const refresh = useCallback(async () => {\n if (!client) {\n setError(\"API key not configured\");\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const list = await client.getCoreMemory(assistantId, scopeOptions);\n setEnabled(list.enabled);\n setEntries(list.entries);\n setLimits(list.limits ?? null);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n }, [client, assistantId, scopeOptions]);\n\n // Fetch on every open (memory may have changed since the last look).\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (isOpen && !wasOpenRef.current) {\n setComposerOpen(false);\n setComposerText(\"\");\n setEditingId(null);\n void refresh();\n }\n wasOpenRef.current = isOpen;\n }, [isOpen, refresh]);\n\n // Escape closes\n useEffect(() => {\n if (!isOpen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [isOpen, onClose]);\n\n const runWrite = useCallback(\n async (write: () => Promise<unknown>) => {\n setSaving(true);\n setError(null);\n try {\n await write();\n await refresh();\n return true;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return false;\n } finally {\n setSaving(false);\n }\n },\n [refresh]\n );\n\n const handleAdd = async () => {\n if (!client || !composerText.trim()) return;\n const ok = await runWrite(() =>\n client.addCoreMemoryEntry(\n assistantId,\n { content: composerText.trim(), section: composerSection },\n scopeOptions\n )\n );\n if (ok) {\n setComposerOpen(false);\n setComposerText(\"\");\n }\n };\n\n const handleSaveEdit = async (entry: CoreMemoryEntry) => {\n if (!client || !editingText.trim()) return;\n const ok = await runWrite(() =>\n client.updateCoreMemoryEntry(\n assistantId,\n entry.id,\n { content: editingText.trim() },\n scopeOptions\n )\n );\n if (ok) setEditingId(null);\n };\n\n const handleRemove = async (entry: CoreMemoryEntry) => {\n if (!client) return;\n await runWrite(() =>\n client.deleteCoreMemoryEntry(assistantId, entry.id, scopeOptions)\n );\n };\n\n if (!isOpen) return null;\n\n // Group entries by section, canonical sections first.\n const sections: Array<{ name: string; entries: CoreMemoryEntry[] }> = [];\n const bySection = new Map<string, CoreMemoryEntry[]>();\n for (const entry of entries) {\n const list = bySection.get(entry.section);\n if (list) list.push(entry);\n else bySection.set(entry.section, [entry]);\n }\n for (const name of SECTIONS) {\n const list = bySection.get(name);\n if (list) {\n sections.push({ name, entries: list });\n bySection.delete(name);\n }\n }\n for (const [name, list] of bySection) sections.push({ name, entries: list });\n\n return createPortal(\n // The variables go on the overlay: a portal inherits nothing from the\n // drawer that opened it, so without this the dialog ignores the theme.\n <div\n className=\"devic-cm-overlay\"\n style={themeVars(theme)}\n onClick={onClose}\n >\n <div\n className=\"devic-cm-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"devic-cm-header\">\n <h3 className=\"devic-cm-title\">\n <BrainIcon />\n {title}\n </h3>\n <button\n className=\"devic-cm-close\"\n onClick={onClose}\n type=\"button\"\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-cm-body\">\n {error && <div className=\"devic-cm-error\">{error}</div>}\n\n {loading ? (\n <div className=\"devic-cm-loading\">Loading memory…</div>\n ) : !enabled ? (\n <div className=\"devic-cm-empty\">\n Memory is not enabled for this assistant.\n </div>\n ) : entries.length === 0 && !composerOpen ? (\n <div className=\"devic-cm-empty\">\n Nothing remembered yet. What the assistant learns and is told to\n keep will appear here.\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.name} className=\"devic-cm-section\">\n <div className=\"devic-cm-section-title\">{section.name}</div>\n {section.entries.map((entry) => (\n <div key={entry.id} className=\"devic-cm-entry\">\n {editingId === entry.id ? (\n <div className=\"devic-cm-editor\">\n <textarea\n className=\"devic-cm-textarea\"\n value={editingText}\n maxLength={limits?.maxEntryChars || undefined}\n onChange={(e) => setEditingText(e.target.value)}\n disabled={saving}\n autoFocus\n />\n <div className=\"devic-cm-editor-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-primary\"\n onClick={() => handleSaveEdit(entry)}\n disabled={saving || !editingText.trim()}\n >\n Save\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => setEditingId(null)}\n disabled={saving}\n >\n Cancel\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"devic-cm-entry-content\">\n {entry.pinned && (\n <span\n className=\"devic-cm-pin\"\n title=\"Pinned: always kept, never changed by the assistant\"\n >\n <PinIcon />\n </span>\n )}\n <span>{entry.content}</span>\n </div>\n {editable && (\n <div className=\"devic-cm-entry-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-small\"\n onClick={() => {\n setEditingId(entry.id);\n setEditingText(entry.content);\n }}\n disabled={saving}\n >\n Edit\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-small devic-cm-btn-danger\"\n onClick={() => handleRemove(entry)}\n disabled={saving}\n >\n Remove\n </button>\n </div>\n )}\n </>\n )}\n </div>\n ))}\n </div>\n ))\n )}\n\n {editable && enabled && !loading && (\n <div className=\"devic-cm-composer\">\n {composerOpen ? (\n <>\n <div className=\"devic-cm-composer-row\">\n <select\n className=\"devic-cm-select\"\n value={composerSection}\n onChange={(e) => setComposerSection(e.target.value)}\n disabled={saving}\n >\n {SECTIONS.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n <textarea\n className=\"devic-cm-textarea\"\n placeholder=\"Something the assistant should always remember…\"\n value={composerText}\n maxLength={limits?.maxEntryChars || undefined}\n onChange={(e) => setComposerText(e.target.value)}\n disabled={saving}\n autoFocus\n />\n <div className=\"devic-cm-editor-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-primary\"\n onClick={handleAdd}\n disabled={saving || !composerText.trim()}\n >\n Add\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => {\n setComposerOpen(false);\n setComposerText(\"\");\n }}\n disabled={saving}\n >\n Cancel\n </button>\n </div>\n </>\n ) : (\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => setComposerOpen(true)}\n disabled={saving}\n >\n + Add memory\n </button>\n )}\n </div>\n )}\n </div>\n\n {limits && enabled && (\n <div className=\"devic-cm-footer\">\n {entries.length}/{limits.maxEntries} entries · up to{\" \"}\n {limits.maxEntryChars} characters each\n </div>\n )}\n </div>\n </div>,\n document.body\n );\n}\n\nexport default CoreMemoryModal;\n"],"names":["_jsxs","_jsx","theme","useOptionalDevicContext","client","useMemo","DevicApiClient","useState","useCallback","useRef","useEffect","createPortal","themeVars","_Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"CoreMemoryModal.js","sources":["../../../../../src/components/CoreMemoryModal/CoreMemoryModal.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { DevicApiClient } from \"../../api/client\";\nimport type { CoreMemoryEntry, CoreMemoryLimits } from \"../../api/types\";\nimport { useOptionalDevicContext } from \"../../provider\";\nimport { themeVars, type DevicTheme } from \"../theme\";\nimport \"./CoreMemoryModal.css\";\n\n/** Canonical sections of the core tier, in display order. */\nconst SECTIONS = [\"persona\", \"instructions\", \"decisions\", \"profile\"] as const;\n\nexport interface CoreMemoryModalProps {\n /** Whether the modal is visible. */\n isOpen: boolean;\n onClose: () => void;\n /** Assistant whose memory bucket is shown. */\n assistantId: string;\n /** Tenant of the end user (falls back to the provider's tenantId). */\n tenantId?: string;\n /** Subtenant of the end user (falls back to the provider's subtenantId). */\n subtenantId?: string;\n /** API key override (falls back to the provider's). */\n apiKey?: string;\n /** Base URL override (falls back to the provider's). */\n baseUrl?: string;\n /**\n * Let the user add, edit and remove entries. Set false for a read-only\n * view of what the assistant remembers.\n * @default true\n */\n editable?: boolean;\n /** Modal title. @default \"Assistant memory\" */\n title?: string;\n /**\n * Colours and font. Same names as the drawer's style options, and the drawer\n * passes its own down — a dialog opening in the default light palette over a\n * themed application is the one thing this must not do.\n */\n theme?: DevicTheme;\n}\n\nfunction BrainIcon(): 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 aria-hidden=\"true\"\n >\n <path d=\"M9.5 2a2.5 2.5 0 0 0-2.45 2A3.5 3.5 0 0 0 4.6 8.6 3.5 3.5 0 0 0 3 11.5c0 1.1.5 2.08 1.29 2.73A3.5 3.5 0 0 0 7 20a3 3 0 0 0 5-2.24V4.5A2.5 2.5 0 0 0 9.5 2Z\" />\n <path d=\"M14.5 2a2.5 2.5 0 0 1 2.45 2 3.5 3.5 0 0 1 2.45 4.6A3.5 3.5 0 0 1 21 11.5a3.49 3.49 0 0 1-1.29 2.73A3.5 3.5 0 0 1 17 20a3 3 0 0 1-5-2.24V4.5A2.5 2.5 0 0 1 14.5 2Z\" />\n </svg>\n );\n}\n\nfunction PinIcon(): JSX.Element {\n return (\n <svg\n width=\"11\"\n height=\"11\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M16 3a1 1 0 0 1 .97 1.243l-1.2 4.8 3.937 3.938A1 1 0 0 1 19 14.688V15a1 1 0 0 1-1 1h-5v6l-1 1-1-1v-6H6a1 1 0 0 1-1-1v-.312a1 1 0 0 1 .293-.707L9.23 9.043l-1.2-4.8A1 1 0 0 1 9 3h7Z\" />\n </svg>\n );\n}\n\n/**\n * Modal showing — and, when `editable`, managing — the core memory of an\n * assistant for a tenant/subtenant combination: the standing entries\n * (persona, instructions, decisions, profile) injected into every prompt of\n * the bucket the assistant resolves there. Lets the end user see and control\n * what the assistant permanently remembers about them.\n *\n * Backed by the public memory API (`/api/v1/memory/assistants/:id/core`);\n * the API key needs that endpoint allowed.\n */\nexport function CoreMemoryModal({\n isOpen,\n onClose,\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n editable = true,\n title = \"Assistant memory\",\n theme,\n}: CoreMemoryModalProps): JSX.Element | null {\n const context = useOptionalDevicContext();\n const resolvedBaseUrl = baseUrl || context?.baseUrl || \"https://api.devic.ai\";\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedSubtenantId = subtenantId || context?.subtenantId;\n\n const resolvedApiKey = apiKey || context?.apiKey;\n const resolvedTenantSession = context?.getTenantSession;\n const onSessionExpired = context?.onSessionExpired;\n\n const client = useMemo(\n () =>\n resolvedApiKey || resolvedTenantSession\n ? new DevicApiClient({\n apiKey: resolvedApiKey,\n baseUrl: resolvedBaseUrl,\n getTenantSession: resolvedTenantSession,\n onSessionExpired,\n })\n : null,\n [resolvedApiKey, resolvedTenantSession, resolvedBaseUrl]\n );\n\n const [loading, setLoading] = useState(false);\n const [enabled, setEnabled] = useState(true);\n const [entries, setEntries] = useState<CoreMemoryEntry[]>([]);\n const [limits, setLimits] = useState<CoreMemoryLimits | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n\n // Inline composer / editor state\n const [composerOpen, setComposerOpen] = useState(false);\n const [composerSection, setComposerSection] = useState<string>(\"profile\");\n const [composerText, setComposerText] = useState(\"\");\n const [editingId, setEditingId] = useState<number | null>(null);\n const [editingText, setEditingText] = useState(\"\");\n\n const scopeOptions = useMemo(\n () => ({\n tenantId: resolvedTenantId || undefined,\n subtenantId: resolvedSubtenantId || undefined,\n }),\n [resolvedTenantId, resolvedSubtenantId]\n );\n\n const refresh = useCallback(async () => {\n if (!client) {\n setError(\"API key not configured\");\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const list = await client.getCoreMemory(assistantId, scopeOptions);\n setEnabled(list.enabled);\n setEntries(list.entries);\n setLimits(list.limits ?? null);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n }, [client, assistantId, scopeOptions]);\n\n // Fetch on every open (memory may have changed since the last look).\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (isOpen && !wasOpenRef.current) {\n setComposerOpen(false);\n setComposerText(\"\");\n setEditingId(null);\n void refresh();\n }\n wasOpenRef.current = isOpen;\n }, [isOpen, refresh]);\n\n // Escape closes\n useEffect(() => {\n if (!isOpen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [isOpen, onClose]);\n\n const runWrite = useCallback(\n async (write: () => Promise<unknown>) => {\n setSaving(true);\n setError(null);\n try {\n await write();\n await refresh();\n return true;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return false;\n } finally {\n setSaving(false);\n }\n },\n [refresh]\n );\n\n const handleAdd = async () => {\n if (!client || !composerText.trim()) return;\n const ok = await runWrite(() =>\n client.addCoreMemoryEntry(\n assistantId,\n { content: composerText.trim(), section: composerSection },\n scopeOptions\n )\n );\n if (ok) {\n setComposerOpen(false);\n setComposerText(\"\");\n }\n };\n\n const handleSaveEdit = async (entry: CoreMemoryEntry) => {\n if (!client || !editingText.trim()) return;\n const ok = await runWrite(() =>\n client.updateCoreMemoryEntry(\n assistantId,\n entry.id,\n { content: editingText.trim() },\n scopeOptions\n )\n );\n if (ok) setEditingId(null);\n };\n\n const handleRemove = async (entry: CoreMemoryEntry) => {\n if (!client) return;\n await runWrite(() =>\n client.deleteCoreMemoryEntry(assistantId, entry.id, scopeOptions)\n );\n };\n\n if (!isOpen) return null;\n\n // Group entries by section, canonical sections first.\n const sections: Array<{ name: string; entries: CoreMemoryEntry[] }> = [];\n const bySection = new Map<string, CoreMemoryEntry[]>();\n for (const entry of entries) {\n const list = bySection.get(entry.section);\n if (list) list.push(entry);\n else bySection.set(entry.section, [entry]);\n }\n for (const name of SECTIONS) {\n const list = bySection.get(name);\n if (list) {\n sections.push({ name, entries: list });\n bySection.delete(name);\n }\n }\n for (const [name, list] of bySection) sections.push({ name, entries: list });\n\n return createPortal(\n // The variables go on the overlay: a portal inherits nothing from the\n // drawer that opened it, so without this the dialog ignores the theme.\n <div\n className=\"devic-cm-overlay\"\n style={themeVars(theme)}\n onClick={onClose}\n >\n <div\n className=\"devic-cm-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"devic-cm-header\">\n <h3 className=\"devic-cm-title\">\n <BrainIcon />\n {title}\n </h3>\n <button\n className=\"devic-cm-close\"\n onClick={onClose}\n type=\"button\"\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-cm-body\">\n {error && <div className=\"devic-cm-error\">{error}</div>}\n\n {loading ? (\n <div className=\"devic-cm-loading\">Loading memory…</div>\n ) : !enabled ? (\n <div className=\"devic-cm-empty\">\n Memory is not enabled for this assistant.\n </div>\n ) : entries.length === 0 && !composerOpen ? (\n <div className=\"devic-cm-empty\">\n Nothing remembered yet. What the assistant learns and is told to\n keep will appear here.\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.name} className=\"devic-cm-section\">\n <div className=\"devic-cm-section-title\">{section.name}</div>\n {section.entries.map((entry) => (\n <div key={entry.id} className=\"devic-cm-entry\">\n {editingId === entry.id ? (\n <div className=\"devic-cm-editor\">\n <textarea\n className=\"devic-cm-textarea\"\n value={editingText}\n maxLength={limits?.maxEntryChars || undefined}\n onChange={(e) => setEditingText(e.target.value)}\n disabled={saving}\n autoFocus\n />\n <div className=\"devic-cm-editor-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-primary\"\n onClick={() => handleSaveEdit(entry)}\n disabled={saving || !editingText.trim()}\n >\n Save\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => setEditingId(null)}\n disabled={saving}\n >\n Cancel\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"devic-cm-entry-content\">\n {entry.pinned && (\n <span\n className=\"devic-cm-pin\"\n title=\"Pinned: always kept, never changed by the assistant\"\n >\n <PinIcon />\n </span>\n )}\n <span>{entry.content}</span>\n </div>\n {editable && (\n <div className=\"devic-cm-entry-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-small\"\n onClick={() => {\n setEditingId(entry.id);\n setEditingText(entry.content);\n }}\n disabled={saving}\n >\n Edit\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-small devic-cm-btn-danger\"\n onClick={() => handleRemove(entry)}\n disabled={saving}\n >\n Remove\n </button>\n </div>\n )}\n </>\n )}\n </div>\n ))}\n </div>\n ))\n )}\n\n {editable && enabled && !loading && (\n <div className=\"devic-cm-composer\">\n {composerOpen ? (\n <>\n <div className=\"devic-cm-composer-row\">\n <select\n className=\"devic-cm-select\"\n value={composerSection}\n onChange={(e) => setComposerSection(e.target.value)}\n disabled={saving}\n >\n {SECTIONS.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n <textarea\n className=\"devic-cm-textarea\"\n placeholder=\"Something the assistant should always remember…\"\n value={composerText}\n maxLength={limits?.maxEntryChars || undefined}\n onChange={(e) => setComposerText(e.target.value)}\n disabled={saving}\n autoFocus\n />\n <div className=\"devic-cm-editor-actions\">\n <button\n type=\"button\"\n className=\"devic-cm-btn devic-cm-btn-primary\"\n onClick={handleAdd}\n disabled={saving || !composerText.trim()}\n >\n Add\n </button>\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => {\n setComposerOpen(false);\n setComposerText(\"\");\n }}\n disabled={saving}\n >\n Cancel\n </button>\n </div>\n </>\n ) : (\n <button\n type=\"button\"\n className=\"devic-cm-btn\"\n onClick={() => setComposerOpen(true)}\n disabled={saving}\n >\n + Add memory\n </button>\n )}\n </div>\n )}\n </div>\n\n {limits && enabled && (\n <div className=\"devic-cm-footer\">\n {entries.length}/{limits.maxEntries} entries · up to{\" \"}\n {limits.maxEntryChars} characters each\n </div>\n )}\n </div>\n </div>,\n document.body\n );\n}\n\nexport default CoreMemoryModal;\n"],"names":["_jsxs","_jsx","theme","useOptionalDevicContext","client","useMemo","DevicApiClient","useState","useCallback","useRef","useEffect","createPortal","themeVars","_Fragment"],"mappings":"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"}
@@ -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';\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 [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' ? 'What did you like?' : 'What could be improved?'}\n </span>\n <button\n type=\"button\"\n className=\"devic-feedback-modal-close\"\n onClick={onClose}\n aria-label=\"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=\"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 Cancel\n </button>\n <button\n type=\"submit\"\n className=\"devic-feedback-btn devic-feedback-btn--primary\"\n disabled={isSubmitting}\n >\n {isSubmitting ? 'Sending...' : '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":["useState","useRef","useMemo","useEffect","_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';\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 [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' ? 'What did you like?' : 'What could be improved?'}\n </span>\n <button\n type=\"button\"\n className=\"devic-feedback-modal-close\"\n onClick={onClose}\n aria-label=\"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=\"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 Cancel\n </button>\n <button\n type=\"submit\"\n className=\"devic-feedback-btn devic-feedback-btn--primary\"\n disabled={isSubmitting}\n >\n {isSubmitting ? 'Sending...' : '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":["useState","useRef","useMemo","useEffect","_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';\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 [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=\"Copy to clipboard\"\n aria-label=\"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=\"Good response\"\n aria-label=\"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=\"Bad response\"\n aria-label=\"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":["useState","useMemo","useCallback","_jsxs","_jsx","_Fragment","FeedbackModal"],"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';\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 [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=\"Copy to clipboard\"\n aria-label=\"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=\"Good response\"\n aria-label=\"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=\"Bad response\"\n aria-label=\"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":["useState","useMemo","useCallback","_jsxs","_jsx","_Fragment","FeedbackModal"],"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\";\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 [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={`Connect ${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>Connect {integration.name}</strong>\n <span>\n {scheme.redirect\n ? `You'll be sent to ${integration.name} to finish authorising.`\n : \"Your credentials go straight to the app — only you can use this account.\"}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label=\"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\">Connect with</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> (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 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 Cancel\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting\n ? \"Connecting…\"\n : scheme.redirect\n ? \"Continue\"\n : \"Connect\"}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n"],"names":["theme","useState","useMemo","createPortal","_jsx","themeVars","isDarkTheme","_jsxs","IntegrationLogo"],"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\";\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 [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={`Connect ${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>Connect {integration.name}</strong>\n <span>\n {scheme.redirect\n ? `You'll be sent to ${integration.name} to finish authorising.`\n : \"Your credentials go straight to the app — only you can use this account.\"}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label=\"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\">Connect with</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> (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 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 Cancel\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting\n ? \"Connecting…\"\n : scheme.redirect\n ? \"Continue\"\n : \"Connect\"}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n"],"names":["theme","useState","useMemo","createPortal","_jsx","themeVars","isDarkTheme","_jsxs","IntegrationLogo"],"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":["useState","_jsx"],"mappings":";;;;;AAQA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,GAAG,EAAE,GACO,EAAA;IACrB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAGA,cAAQ,CAAC,KAAK,CAAC;AAE3C,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,EAAE;AAC/B,QAAA,QACEC,cAAA,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,cAAA,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":["useState","_jsx"],"mappings":";;;;;AAQA;;;;;;;AAOG;AACG,SAAU,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,GAAG,EAAE,GACO,EAAA;IACrB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAGA,cAAQ,CAAC,KAAK,CAAC;AAE3C,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,EAAE;AAC/B,QAAA,QACEC,cAAA,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,cAAA,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;;"}