getkova 2026.4.21 → 2026.4.22

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 (102) hide show
  1. package/dist/control-ui/apple-touch-icon.png +0 -0
  2. package/dist/control-ui/assets/agents-CLtFQRXd.js +918 -0
  3. package/dist/control-ui/assets/agents-CLtFQRXd.js.map +1 -0
  4. package/dist/control-ui/assets/anthropic-CFEPAL-v.js +37 -0
  5. package/dist/control-ui/assets/anthropic-CFEPAL-v.js.map +1 -0
  6. package/dist/control-ui/assets/azure-openai-responses-CxiWQLmZ.js +2 -0
  7. package/dist/control-ui/assets/azure-openai-responses-CxiWQLmZ.js.map +1 -0
  8. package/dist/control-ui/assets/briefing-DS4VWpUL.js +121 -0
  9. package/dist/control-ui/assets/briefing-DS4VWpUL.js.map +1 -0
  10. package/dist/control-ui/assets/canvas-BfC_2Nqy.js +17 -0
  11. package/dist/control-ui/assets/canvas-BfC_2Nqy.js.map +1 -0
  12. package/dist/control-ui/assets/channel-config-extras-YNNd-4PG.js +2 -0
  13. package/dist/control-ui/assets/channel-config-extras-YNNd-4PG.js.map +1 -0
  14. package/dist/control-ui/assets/channels-BKdM7i5r.js +471 -0
  15. package/dist/control-ui/assets/channels-BKdM7i5r.js.map +1 -0
  16. package/dist/control-ui/assets/cron-C11m3yJi.js +928 -0
  17. package/dist/control-ui/assets/cron-C11m3yJi.js.map +1 -0
  18. package/dist/control-ui/assets/de-rLAkQOBc.js +2 -0
  19. package/dist/control-ui/assets/de-rLAkQOBc.js.map +1 -0
  20. package/dist/control-ui/assets/debug-DFf2qCcM.js +94 -0
  21. package/dist/control-ui/assets/debug-DFf2qCcM.js.map +1 -0
  22. package/dist/control-ui/assets/dist-D8DZLmCF.js +18 -0
  23. package/dist/control-ui/assets/dist-D8DZLmCF.js.map +1 -0
  24. package/dist/control-ui/assets/employees-DV-5FV4K.js +104 -0
  25. package/dist/control-ui/assets/employees-DV-5FV4K.js.map +1 -0
  26. package/dist/control-ui/assets/es-CIeD3O54.js +2 -0
  27. package/dist/control-ui/assets/es-CIeD3O54.js.map +1 -0
  28. package/dist/control-ui/assets/event-stream-B8X6sYaV.js +2 -0
  29. package/dist/control-ui/assets/event-stream-B8X6sYaV.js.map +1 -0
  30. package/dist/control-ui/assets/format-BahKhiOC.js +2 -0
  31. package/dist/control-ui/assets/format-BahKhiOC.js.map +1 -0
  32. package/dist/control-ui/assets/github-copilot-headers-CrI0CIJ7.js +2 -0
  33. package/dist/control-ui/assets/github-copilot-headers-CrI0CIJ7.js.map +1 -0
  34. package/dist/control-ui/assets/google-BT0bmsh5.js +2 -0
  35. package/dist/control-ui/assets/google-BT0bmsh5.js.map +1 -0
  36. package/dist/control-ui/assets/google-gemini-cli-BpxbH95Q.js +3 -0
  37. package/dist/control-ui/assets/google-gemini-cli-BpxbH95Q.js.map +1 -0
  38. package/dist/control-ui/assets/google-shared-CbPHVnPr.js +12 -0
  39. package/dist/control-ui/assets/google-shared-CbPHVnPr.js.map +1 -0
  40. package/dist/control-ui/assets/google-vertex-lQwbjEII.js +2 -0
  41. package/dist/control-ui/assets/google-vertex-lQwbjEII.js.map +1 -0
  42. package/dist/control-ui/assets/hash-Bt1aVMQ3.js +2 -0
  43. package/dist/control-ui/assets/hash-Bt1aVMQ3.js.map +1 -0
  44. package/dist/control-ui/assets/inbox-C4tOnlJr.js +100 -0
  45. package/dist/control-ui/assets/inbox-C4tOnlJr.js.map +1 -0
  46. package/dist/control-ui/assets/index-DYMuTfvX.css +1 -0
  47. package/dist/control-ui/assets/index-XGDpaFxG.js +5482 -0
  48. package/dist/control-ui/assets/index-XGDpaFxG.js.map +1 -0
  49. package/dist/control-ui/assets/instances-Cyr-tbN6.js +57 -0
  50. package/dist/control-ui/assets/instances-Cyr-tbN6.js.map +1 -0
  51. package/dist/control-ui/assets/kova-logo.png +0 -0
  52. package/dist/control-ui/assets/lit-zdTgzAJI.js +3 -0
  53. package/dist/control-ui/assets/lit-zdTgzAJI.js.map +1 -0
  54. package/dist/control-ui/assets/local-storage-D3baoRWx.js +2 -0
  55. package/dist/control-ui/assets/local-storage-D3baoRWx.js.map +1 -0
  56. package/dist/control-ui/assets/logs-B7--7dYP.js +74 -0
  57. package/dist/control-ui/assets/logs-B7--7dYP.js.map +1 -0
  58. package/dist/control-ui/assets/meetings-DSqn6s7n.js +185 -0
  59. package/dist/control-ui/assets/meetings-DSqn6s7n.js.map +1 -0
  60. package/dist/control-ui/assets/mistral-CBrDC_Gv.js +8 -0
  61. package/dist/control-ui/assets/mistral-CBrDC_Gv.js.map +1 -0
  62. package/dist/control-ui/assets/nodes-Cvq_sAqT.js +430 -0
  63. package/dist/control-ui/assets/nodes-Cvq_sAqT.js.map +1 -0
  64. package/dist/control-ui/assets/openai-Cn7eGqwa.js +17 -0
  65. package/dist/control-ui/assets/openai-Cn7eGqwa.js.map +1 -0
  66. package/dist/control-ui/assets/openai-codex-responses-DuhESMYF.js +8 -0
  67. package/dist/control-ui/assets/openai-codex-responses-DuhESMYF.js.map +1 -0
  68. package/dist/control-ui/assets/openai-completions-Bv33lqKL.js +6 -0
  69. package/dist/control-ui/assets/openai-completions-Bv33lqKL.js.map +1 -0
  70. package/dist/control-ui/assets/openai-responses-BPxpapOg.js +2 -0
  71. package/dist/control-ui/assets/openai-responses-BPxpapOg.js.map +1 -0
  72. package/dist/control-ui/assets/openai-responses-shared-8nKH8ywL.js +11 -0
  73. package/dist/control-ui/assets/openai-responses-shared-8nKH8ywL.js.map +1 -0
  74. package/dist/control-ui/assets/pdf-BwYFZMZM.js +57 -0
  75. package/dist/control-ui/assets/pdf-BwYFZMZM.js.map +1 -0
  76. package/dist/control-ui/assets/pdf.worker.min-BmpgcBpm.js +2 -0
  77. package/dist/control-ui/assets/pdf.worker.min-BmpgcBpm.js.map +1 -0
  78. package/dist/control-ui/assets/pdf.worker.min-C8PGFc0r.mjs +28 -0
  79. package/dist/control-ui/assets/preload-helper-Chd9yIcd.js +1 -0
  80. package/dist/control-ui/assets/pt-BR-lSsBb08k.js +2 -0
  81. package/dist/control-ui/assets/pt-BR-lSsBb08k.js.map +1 -0
  82. package/dist/control-ui/assets/routing-DizI_FiJ.js +157 -0
  83. package/dist/control-ui/assets/routing-DizI_FiJ.js.map +1 -0
  84. package/dist/control-ui/assets/sessions-N9rgJP2R.js +236 -0
  85. package/dist/control-ui/assets/sessions-N9rgJP2R.js.map +1 -0
  86. package/dist/control-ui/assets/skills-D1vP4MkL.js +280 -0
  87. package/dist/control-ui/assets/skills-D1vP4MkL.js.map +1 -0
  88. package/dist/control-ui/assets/skills-shared-Bg0Qcnkp.js +11 -0
  89. package/dist/control-ui/assets/skills-shared-Bg0Qcnkp.js.map +1 -0
  90. package/dist/control-ui/assets/transform-messages-XKqwKV3D.js +2 -0
  91. package/dist/control-ui/assets/transform-messages-XKqwKV3D.js.map +1 -0
  92. package/dist/control-ui/assets/zh-CN-C5tPG8Eu.js +2 -0
  93. package/dist/control-ui/assets/zh-CN-C5tPG8Eu.js.map +1 -0
  94. package/dist/control-ui/assets/zh-TW-CPSoC7Wz.js +2 -0
  95. package/dist/control-ui/assets/zh-TW-CPSoC7Wz.js.map +1 -0
  96. package/dist/control-ui/favicon-32.png +0 -0
  97. package/dist/control-ui/favicon.ico +0 -0
  98. package/dist/control-ui/favicon.png +0 -0
  99. package/dist/control-ui/favicon.svg +22 -0
  100. package/dist/control-ui/index.html +73 -0
  101. package/dist/control-ui/openclaw-canvas-auth-sw.js +57 -0
  102. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels-BKdM7i5r.js","names":[],"sources":["../../../ui/src/ui/views/channels.config.ts","../../../ui/src/ui/views/channels.shared.ts","../../../ui/src/ui/views/channels.discord.ts","../../../ui/src/ui/views/channels.googlechat.ts","../../../ui/src/ui/views/channels.imessage.ts","../../../ui/src/ui/views/channels.nostr.ts","../../../ui/src/ui/views/channels.signal.ts","../../../ui/src/ui/views/channels.slack.ts","../../../ui/src/ui/views/channels.telegram.ts","../../../ui/src/ui/views/channels.ts"],"sourcesContent":["import { html } from \"lit\";\nimport type { ConfigUiHints } from \"../types.ts\";\nimport { formatChannelExtraValue, resolveChannelConfigValue } from \"./channel-config-extras.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\nimport { analyzeConfigSchema, renderNode, schemaType, type JsonSchema } from \"./config-form.ts\";\n\ntype ChannelConfigFormProps = {\n channelId: string;\n configValue: Record<string, unknown> | null;\n schema: unknown;\n uiHints: ConfigUiHints;\n disabled: boolean;\n onPatch: (path: Array<string | number>, value: unknown) => void;\n};\n\nfunction resolveSchemaNode(\n schema: JsonSchema | null,\n path: Array<string | number>,\n): JsonSchema | null {\n let current = schema;\n for (const key of path) {\n if (!current) {\n return null;\n }\n const type = schemaType(current);\n if (type === \"object\") {\n const properties = current.properties ?? {};\n if (typeof key === \"string\" && properties[key]) {\n current = properties[key];\n continue;\n }\n const additional = current.additionalProperties;\n if (typeof key === \"string\" && additional && typeof additional === \"object\") {\n current = additional;\n continue;\n }\n return null;\n }\n if (type === \"array\") {\n if (typeof key !== \"number\") {\n return null;\n }\n const items = Array.isArray(current.items) ? current.items[0] : current.items;\n current = items ?? null;\n continue;\n }\n return null;\n }\n return current;\n}\n\nfunction resolveChannelValue(\n config: Record<string, unknown>,\n channelId: string,\n): Record<string, unknown> {\n return resolveChannelConfigValue(config, channelId) ?? {};\n}\n\nconst EXTRA_CHANNEL_FIELDS = [\"groupPolicy\", \"streamMode\", \"dmPolicy\"] as const;\n\nfunction renderExtraChannelFields(value: Record<string, unknown>) {\n const entries = EXTRA_CHANNEL_FIELDS.flatMap((field) => {\n if (!(field in value)) {\n return [];\n }\n return [[field, value[field]]] as Array<[string, unknown]>;\n });\n if (entries.length === 0) {\n return null;\n }\n return html`\n <div class=\"status-list\" style=\"margin-top: 12px;\">\n ${entries.map(\n ([field, raw]) => html`\n <div>\n <span class=\"label\">${field}</span>\n <span>${formatChannelExtraValue(raw)}</span>\n </div>\n `,\n )}\n </div>\n `;\n}\n\nexport function renderChannelConfigForm(props: ChannelConfigFormProps) {\n const analysis = analyzeConfigSchema(props.schema);\n const normalized = analysis.schema;\n if (!normalized) {\n return html` <div class=\"callout danger\">Schema unavailable. Use Raw.</div> `;\n }\n const node = resolveSchemaNode(normalized, [\"channels\", props.channelId]);\n if (!node) {\n return html` <div class=\"callout danger\">Channel config schema unavailable.</div> `;\n }\n const configValue = props.configValue ?? {};\n const value = resolveChannelValue(configValue, props.channelId);\n return html`\n <div class=\"config-form\">\n ${renderNode({\n schema: node,\n value,\n path: [\"channels\", props.channelId],\n hints: props.uiHints,\n unsupported: new Set(analysis.unsupportedPaths),\n disabled: props.disabled,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n </div>\n ${renderExtraChannelFields(value)}\n `;\n}\n\nexport function renderChannelConfigSection(params: { channelId: string; props: ChannelsProps }) {\n const { channelId, props } = params;\n const disabled = props.configSaving || props.configSchemaLoading;\n return html`\n <div style=\"margin-top: 16px;\">\n ${props.configSchemaLoading\n ? html` <div class=\"muted\">Loading config schema…</div> `\n : renderChannelConfigForm({\n channelId,\n configValue: props.configForm,\n schema: props.configSchema,\n uiHints: props.configUiHints,\n disabled,\n onPatch: props.onConfigPatch,\n })}\n <div class=\"row\" style=\"margin-top: 12px;\">\n <button\n class=\"btn primary\"\n ?disabled=${disabled || !props.configFormDirty}\n @click=${() => props.onConfigSave()}\n >\n ${props.configSaving ? \"Saving…\" : \"Save\"}\n </button>\n <button class=\"btn\" ?disabled=${disabled} @click=${() => props.onConfigReload()}>\n Reload\n </button>\n </div>\n </div>\n `;\n}\n","import { html, nothing } from \"lit\";\nimport type { ChannelAccountSnapshot } from \"../types.ts\";\nimport type { ChannelKey, ChannelsProps } from \"./channels.types.ts\";\n\ntype ChannelDisplayState = {\n configured: boolean | null;\n running: boolean | null;\n connected: boolean | null;\n defaultAccount: ChannelAccountSnapshot | null;\n hasAnyActiveAccount: boolean;\n status: Record<string, unknown> | undefined;\n};\n\ntype ChannelStatusRow = {\n label: string;\n value: unknown;\n};\n\nfunction resolveChannelStatus(\n key: ChannelKey,\n props: ChannelsProps,\n): Record<string, unknown> | undefined {\n const channels = props.snapshot?.channels as Record<string, unknown> | null;\n return channels?.[key] as Record<string, unknown> | undefined;\n}\n\nexport function resolveDefaultChannelAccount(\n key: ChannelKey,\n props: ChannelsProps,\n): ChannelAccountSnapshot | null {\n const accounts = props.snapshot?.channelAccounts?.[key] ?? [];\n const defaultAccountId = props.snapshot?.channelDefaultAccountId?.[key];\n return (\n (defaultAccountId\n ? accounts.find((account) => account.accountId === defaultAccountId)\n : undefined) ??\n accounts[0] ??\n null\n );\n}\n\nexport function resolveChannelDisplayState(\n key: ChannelKey,\n props: ChannelsProps,\n): ChannelDisplayState {\n const status = resolveChannelStatus(key, props);\n const accounts = props.snapshot?.channelAccounts?.[key] ?? [];\n const defaultAccount = resolveDefaultChannelAccount(key, props);\n const configured =\n typeof status?.configured === \"boolean\"\n ? status.configured\n : typeof defaultAccount?.configured === \"boolean\"\n ? defaultAccount.configured\n : null;\n const running = typeof status?.running === \"boolean\" ? status.running : null;\n const connected = typeof status?.connected === \"boolean\" ? status.connected : null;\n const hasAnyActiveAccount = accounts.some(\n (account) => account.configured || account.running || account.connected,\n );\n\n return {\n configured,\n running,\n connected,\n defaultAccount,\n hasAnyActiveAccount,\n status,\n };\n}\n\nexport function channelEnabled(key: ChannelKey, props: ChannelsProps) {\n if (!props.snapshot) {\n return false;\n }\n const displayState = resolveChannelDisplayState(key, props);\n return (\n displayState.configured === true ||\n displayState.running === true ||\n displayState.connected === true ||\n displayState.hasAnyActiveAccount\n );\n}\n\nexport function resolveChannelConfigured(key: ChannelKey, props: ChannelsProps): boolean | null {\n return resolveChannelDisplayState(key, props).configured;\n}\n\nexport function formatNullableBoolean(value: boolean | null): string {\n if (value == null) {\n return \"n/a\";\n }\n return value ? \"Yes\" : \"No\";\n}\n\nexport function renderSingleAccountChannelCard(params: {\n title: string;\n subtitle: string;\n accountCountLabel: unknown;\n statusRows: readonly ChannelStatusRow[];\n lastError?: string | null;\n secondaryCallout?: unknown;\n extraContent?: unknown;\n configSection: unknown;\n footer?: unknown;\n}) {\n return html`\n <div class=\"card\">\n <div class=\"card-title\">${params.title}</div>\n <div class=\"card-sub\">${params.subtitle}</div>\n ${params.accountCountLabel}\n\n <div class=\"status-list\" style=\"margin-top: 16px;\">\n ${params.statusRows.map(\n (row) => html`\n <div>\n <span class=\"label\">${row.label}</span>\n <span>${row.value}</span>\n </div>\n `,\n )}\n </div>\n\n ${params.lastError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${params.lastError}</div>`\n : nothing}\n ${params.secondaryCallout ?? nothing} ${params.extraContent ?? nothing}\n ${params.configSection} ${params.footer ?? nothing}\n </div>\n `;\n}\n\nexport function getChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record<string, ChannelAccountSnapshot[]> | null,\n): number {\n return channelAccounts?.[key]?.length ?? 0;\n}\n\nexport function renderChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record<string, ChannelAccountSnapshot[]> | null,\n) {\n const count = getChannelAccountCount(key, channelAccounts);\n if (count < 2) {\n return nothing;\n }\n return html`<div class=\"account-count\">Accounts (${count})</div>`;\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { DiscordStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n formatNullableBoolean,\n renderSingleAccountChannelCard,\n resolveChannelConfigured,\n} from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nexport function renderDiscordCard(params: {\n props: ChannelsProps;\n discord?: DiscordStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, discord, accountCountLabel } = params;\n const configured = resolveChannelConfigured(\"discord\", props);\n\n return renderSingleAccountChannelCard({\n title: \"Discord\",\n subtitle: \"Bot status and channel configuration.\",\n accountCountLabel,\n statusRows: [\n { label: \"Configured\", value: formatNullableBoolean(configured) },\n { label: \"Running\", value: discord?.running ? \"Yes\" : \"No\" },\n {\n label: \"Last start\",\n value: discord?.lastStartAt ? formatRelativeTimestamp(discord.lastStartAt) : \"n/a\",\n },\n {\n label: \"Last probe\",\n value: discord?.lastProbeAt ? formatRelativeTimestamp(discord.lastProbeAt) : \"n/a\",\n },\n ],\n lastError: discord?.lastError,\n secondaryCallout: discord?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${discord.probe.ok ? \"ok\" : \"failed\"} · ${discord.probe.status ?? \"\"}\n ${discord.probe.error ?? \"\"}\n </div>`\n : nothing,\n configSection: renderChannelConfigSection({ channelId: \"discord\", props }),\n footer: html`<div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(true)}>Probe</button>\n </div>`,\n });\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { GoogleChatStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n formatNullableBoolean,\n renderSingleAccountChannelCard,\n resolveChannelConfigured,\n} from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nexport function renderGoogleChatCard(params: {\n props: ChannelsProps;\n googleChat?: GoogleChatStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, googleChat, accountCountLabel } = params;\n const configured = resolveChannelConfigured(\"googlechat\", props);\n\n return renderSingleAccountChannelCard({\n title: \"Google Chat\",\n subtitle: \"Chat API webhook status and channel configuration.\",\n accountCountLabel,\n statusRows: [\n { label: \"Configured\", value: formatNullableBoolean(configured) },\n {\n label: \"Running\",\n value: googleChat ? (googleChat.running ? \"Yes\" : \"No\") : \"n/a\",\n },\n { label: \"Credential\", value: googleChat?.credentialSource ?? \"n/a\" },\n {\n label: \"Audience\",\n value: googleChat?.audienceType\n ? `${googleChat.audienceType}${googleChat.audience ? ` · ${googleChat.audience}` : \"\"}`\n : \"n/a\",\n },\n {\n label: \"Last start\",\n value: googleChat?.lastStartAt ? formatRelativeTimestamp(googleChat.lastStartAt) : \"n/a\",\n },\n {\n label: \"Last probe\",\n value: googleChat?.lastProbeAt ? formatRelativeTimestamp(googleChat.lastProbeAt) : \"n/a\",\n },\n ],\n lastError: googleChat?.lastError,\n secondaryCallout: googleChat?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${googleChat.probe.ok ? \"ok\" : \"failed\"} · ${googleChat.probe.status ?? \"\"}\n ${googleChat.probe.error ?? \"\"}\n </div>`\n : nothing,\n configSection: renderChannelConfigSection({ channelId: \"googlechat\", props }),\n footer: html`<div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(true)}>Probe</button>\n </div>`,\n });\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { IMessageStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n formatNullableBoolean,\n renderSingleAccountChannelCard,\n resolveChannelConfigured,\n} from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nexport function renderIMessageCard(params: {\n props: ChannelsProps;\n imessage?: IMessageStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, imessage, accountCountLabel } = params;\n const configured = resolveChannelConfigured(\"imessage\", props);\n\n return renderSingleAccountChannelCard({\n title: \"iMessage\",\n subtitle: \"macOS bridge status and channel configuration.\",\n accountCountLabel,\n statusRows: [\n { label: \"Configured\", value: formatNullableBoolean(configured) },\n { label: \"Running\", value: imessage?.running ? \"Yes\" : \"No\" },\n {\n label: \"Last start\",\n value: imessage?.lastStartAt ? formatRelativeTimestamp(imessage.lastStartAt) : \"n/a\",\n },\n {\n label: \"Last probe\",\n value: imessage?.lastProbeAt ? formatRelativeTimestamp(imessage.lastProbeAt) : \"n/a\",\n },\n ],\n lastError: imessage?.lastError,\n secondaryCallout: imessage?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${imessage.probe.ok ? \"ok\" : \"failed\"} · ${imessage.probe.error ?? \"\"}\n </div>`\n : nothing,\n configSection: renderChannelConfigSection({ channelId: \"imessage\", props }),\n footer: html`<div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(true)}>Probe</button>\n </div>`,\n });\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { ChannelAccountSnapshot, NostrStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n renderNostrProfileForm,\n type NostrProfileFormState,\n type NostrProfileFormCallbacks,\n} from \"./channels.nostr-profile-form.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\n/**\n * Truncate a pubkey for display (shows first and last 8 chars)\n */\nfunction truncatePubkey(pubkey: string | null | undefined): string {\n if (!pubkey) {\n return \"n/a\";\n }\n if (pubkey.length <= 20) {\n return pubkey;\n }\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n}\n\nexport function renderNostrCard(params: {\n props: ChannelsProps;\n nostr?: NostrStatus | null;\n nostrAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n /** Profile form state (optional - if provided, shows form) */\n profileFormState?: NostrProfileFormState | null;\n /** Profile form callbacks */\n profileFormCallbacks?: NostrProfileFormCallbacks | null;\n /** Called when Edit Profile is clicked */\n onEditProfile?: () => void;\n}) {\n const {\n props,\n nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState,\n profileFormCallbacks,\n onEditProfile,\n } = params;\n const primaryAccount = nostrAccounts[0];\n const summaryConfigured = nostr?.configured ?? primaryAccount?.configured ?? false;\n const summaryRunning = nostr?.running ?? primaryAccount?.running ?? false;\n const summaryPublicKey =\n nostr?.publicKey ?? (primaryAccount as { publicKey?: string } | undefined)?.publicKey;\n const summaryLastStartAt = nostr?.lastStartAt ?? primaryAccount?.lastStartAt ?? null;\n const summaryLastError = nostr?.lastError ?? primaryAccount?.lastError ?? null;\n const hasMultipleAccounts = nostrAccounts.length > 1;\n const showingForm = profileFormState !== null && profileFormState !== undefined;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const publicKey = (account as { publicKey?: string }).publicKey;\n const profile = (account as { profile?: { name?: string; displayName?: string } }).profile;\n const displayName = profile?.displayName ?? profile?.name ?? account.name ?? account.accountId;\n\n return html`\n <div class=\"account-card\">\n <div class=\"account-card-header\">\n <div class=\"account-card-title\">${displayName}</div>\n <div class=\"account-card-id\">${account.accountId}</div>\n </div>\n <div class=\"status-list account-card-status\">\n <div>\n <span class=\"label\">Running</span>\n <span>${account.running ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Configured</span>\n <span>${account.configured ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Public Key</span>\n <span class=\"monospace\" title=\"${publicKey ?? \"\"}\">${truncatePubkey(publicKey)}</span>\n </div>\n <div>\n <span class=\"label\">Last inbound</span>\n <span\n >${account.lastInboundAt\n ? formatRelativeTimestamp(account.lastInboundAt)\n : \"n/a\"}</span\n >\n </div>\n ${account.lastError\n ? html` <div class=\"account-card-error\">${account.lastError}</div> `\n : nothing}\n </div>\n </div>\n `;\n };\n\n const renderProfileSection = () => {\n // If showing form, render the form instead of the read-only view\n if (showingForm && profileFormCallbacks) {\n return renderNostrProfileForm({\n state: profileFormState,\n callbacks: profileFormCallbacks,\n accountId: nostrAccounts[0]?.accountId ?? \"default\",\n });\n }\n\n const profile =\n (\n primaryAccount as\n | {\n profile?: {\n name?: string;\n displayName?: string;\n about?: string;\n picture?: string;\n nip05?: string;\n };\n }\n | undefined\n )?.profile ?? nostr?.profile;\n const { name, displayName, about, picture, nip05 } = profile ?? {};\n const hasAnyProfileData = name || displayName || about || picture || nip05;\n\n return html`\n <div\n style=\"margin-top: 16px; padding: 12px; background: var(--bg-secondary); border-radius: var(--radius-md);\"\n >\n <div\n style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;\"\n >\n <div style=\"font-weight: 500;\">Profile</div>\n ${summaryConfigured\n ? html`\n <button\n class=\"btn btn--sm\"\n @click=${onEditProfile}\n style=\"font-size: 12px; padding: 4px 8px;\"\n >\n Edit Profile\n </button>\n `\n : nothing}\n </div>\n ${hasAnyProfileData\n ? html`\n <div class=\"status-list\">\n ${picture\n ? html`\n <div style=\"margin-bottom: 8px;\">\n <img\n src=${picture}\n alt=\"Profile picture\"\n style=\"width: 48px; height: 48px; border-radius: 50%; object-fit: cover; border: 2px solid var(--border-color);\"\n @error=${(e: Event) => {\n (e.target as HTMLImageElement).style.display = \"none\";\n }}\n />\n </div>\n `\n : nothing}\n ${name\n ? html`<div><span class=\"label\">Name</span><span>${name}</span></div>`\n : nothing}\n ${displayName\n ? html`<div>\n <span class=\"label\">Display Name</span><span>${displayName}</span>\n </div>`\n : nothing}\n ${about\n ? html`<div>\n <span class=\"label\">About</span\n ><span style=\"max-width: 300px; overflow: hidden; text-overflow: ellipsis;\"\n >${about}</span\n >\n </div>`\n : nothing}\n ${nip05\n ? html`<div><span class=\"label\">NIP-05</span><span>${nip05}</span></div>`\n : nothing}\n </div>\n `\n : html`\n <div style=\"color: var(--text-muted); font-size: 13px\">\n No profile set. Click \"Edit Profile\" to add your name, bio, and avatar.\n </div>\n `}\n </div>\n `;\n };\n\n return html`\n <div class=\"card\">\n <div class=\"card-title\">Nostr</div>\n <div class=\"card-sub\">Decentralized DMs via Nostr relays (NIP-04).</div>\n ${accountCountLabel}\n ${hasMultipleAccounts\n ? html`\n <div class=\"account-card-list\">\n ${nostrAccounts.map((account) => renderAccountCard(account))}\n </div>\n `\n : html`\n <div class=\"status-list\" style=\"margin-top: 16px;\">\n <div>\n <span class=\"label\">Configured</span>\n <span>${summaryConfigured ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Running</span>\n <span>${summaryRunning ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Public Key</span>\n <span class=\"monospace\" title=\"${summaryPublicKey ?? \"\"}\"\n >${truncatePubkey(summaryPublicKey)}</span\n >\n </div>\n <div>\n <span class=\"label\">Last start</span>\n <span\n >${summaryLastStartAt ? formatRelativeTimestamp(summaryLastStartAt) : \"n/a\"}</span\n >\n </div>\n </div>\n `}\n ${summaryLastError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${summaryLastError}</div>`\n : nothing}\n ${renderProfileSection()} ${renderChannelConfigSection({ channelId: \"nostr\", props })}\n\n <div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(false)}>Refresh</button>\n </div>\n </div>\n `;\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { SignalStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n formatNullableBoolean,\n renderSingleAccountChannelCard,\n resolveChannelConfigured,\n} from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nexport function renderSignalCard(params: {\n props: ChannelsProps;\n signal?: SignalStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, signal, accountCountLabel } = params;\n const configured = resolveChannelConfigured(\"signal\", props);\n\n return renderSingleAccountChannelCard({\n title: \"Signal\",\n subtitle: \"signal-cli status and channel configuration.\",\n accountCountLabel,\n statusRows: [\n { label: \"Configured\", value: formatNullableBoolean(configured) },\n { label: \"Running\", value: signal?.running ? \"Yes\" : \"No\" },\n { label: \"Base URL\", value: signal?.baseUrl ?? \"n/a\" },\n {\n label: \"Last start\",\n value: signal?.lastStartAt ? formatRelativeTimestamp(signal.lastStartAt) : \"n/a\",\n },\n {\n label: \"Last probe\",\n value: signal?.lastProbeAt ? formatRelativeTimestamp(signal.lastProbeAt) : \"n/a\",\n },\n ],\n lastError: signal?.lastError,\n secondaryCallout: signal?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${signal.probe.ok ? \"ok\" : \"failed\"} · ${signal.probe.status ?? \"\"}\n ${signal.probe.error ?? \"\"}\n </div>`\n : nothing,\n configSection: renderChannelConfigSection({ channelId: \"signal\", props }),\n footer: html`<div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(true)}>Probe</button>\n </div>`,\n });\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { SlackStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport {\n formatNullableBoolean,\n renderSingleAccountChannelCard,\n resolveChannelConfigured,\n} from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nexport function renderSlackCard(params: {\n props: ChannelsProps;\n slack?: SlackStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, slack, accountCountLabel } = params;\n const configured = resolveChannelConfigured(\"slack\", props);\n\n return renderSingleAccountChannelCard({\n title: \"Slack\",\n subtitle: \"Socket mode status and channel configuration.\",\n accountCountLabel,\n statusRows: [\n { label: \"Configured\", value: formatNullableBoolean(configured) },\n { label: \"Running\", value: slack?.running ? \"Yes\" : \"No\" },\n {\n label: \"Last start\",\n value: slack?.lastStartAt ? formatRelativeTimestamp(slack.lastStartAt) : \"n/a\",\n },\n {\n label: \"Last probe\",\n value: slack?.lastProbeAt ? formatRelativeTimestamp(slack.lastProbeAt) : \"n/a\",\n },\n ],\n lastError: slack?.lastError,\n secondaryCallout: slack?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${slack.probe.ok ? \"ok\" : \"failed\"} · ${slack.probe.status ?? \"\"}\n ${slack.probe.error ?? \"\"}\n </div>`\n : nothing,\n configSection: renderChannelConfigSection({ channelId: \"slack\", props }),\n footer: html`<div class=\"row\" style=\"margin-top: 12px;\">\n <button class=\"btn\" @click=${() => props.onRefresh(true)}>Probe</button>\n </div>`,\n });\n}\n","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type { ChannelAccountSnapshot, TelegramStatus } from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport { resolveChannelConfigured } from \"./channels.shared.ts\";\nimport type { ChannelsProps } from \"./channels.types.ts\";\n\nfunction readTelegramConfig(props: ChannelsProps): Record<string, unknown> | null {\n const channels = (props.configForm as { channels?: unknown } | null)?.channels;\n if (!channels || typeof channels !== \"object\" || Array.isArray(channels)) {\n return null;\n }\n const telegram = (channels as { telegram?: unknown }).telegram;\n if (!telegram || typeof telegram !== \"object\" || Array.isArray(telegram)) {\n return null;\n }\n return telegram as Record<string, unknown>;\n}\n\nfunction hasStoredTelegramToken(props: ChannelsProps): boolean {\n const telegramConfig = readTelegramConfig(props);\n return typeof telegramConfig?.botToken === \"string\" && telegramConfig.botToken.trim().length > 0;\n}\n\nfunction resolveTelegramTokenSource(\n telegram: TelegramStatus | undefined,\n telegramAccounts: ChannelAccountSnapshot[],\n): string | null {\n const primaryAccount = telegramAccounts[0];\n return (\n telegram?.tokenSource ??\n primaryAccount?.tokenSource ??\n primaryAccount?.botTokenSource ??\n null\n );\n}\n\nfunction isTelegramConnected(\n props: ChannelsProps,\n telegram: TelegramStatus | undefined,\n telegramAccounts: ChannelAccountSnapshot[],\n): boolean {\n if (resolveChannelConfigured(\"telegram\", props) === true) {\n return true;\n }\n if (telegram?.configured || telegram?.running) {\n return true;\n }\n return telegramAccounts.some((account) => account.configured || account.running);\n}\n\nfunction renderTelegramSetupSection(params: {\n props: ChannelsProps;\n busy: boolean;\n tokenSource: string | null;\n}) {\n const { props, busy, tokenSource } = params;\n return html`\n <div\n style=\"margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 12px; display: grid; gap: 12px;\"\n >\n <div class=\"row\" style=\"justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;\">\n <div>\n <div style=\"font-weight: 600;\">Quick setup</div>\n <div class=\"muted\" style=\"font-size: 13px;\">\n Paste a Telegram bot token and connect without editing config files.\n </div>\n </div>\n <span class=\"pill pill--sm\">Not connected</span>\n </div>\n\n <label class=\"field\">\n <span>Bot token</span>\n <input\n type=\"password\"\n .value=${props.telegramSetupToken}\n @input=${(e: Event) => props.onTelegramTokenInput((e.target as HTMLInputElement).value)}\n placeholder=\"123456:ABCDEF...\"\n autocomplete=\"off\"\n ?disabled=${busy}\n />\n </label>\n\n <div class=\"row\" style=\"gap: 8px; flex-wrap: wrap; align-items: center;\">\n <button\n class=\"btn primary\"\n ?disabled=${busy || !props.connected}\n @click=${() => props.onTelegramConnect()}\n >\n ${busy ? \"Saving...\" : \"Connect Telegram\"}\n </button>\n ${tokenSource && tokenSource !== \"config\"\n ? html`<div class=\"muted\" style=\"font-size: 12px;\">Current token source: ${tokenSource}</div>`\n : nothing}\n </div>\n\n ${props.telegramSetupMessage\n ? html`\n <div class=\"callout ${props.telegramSetupMessage.kind === \"error\" ? \"danger\" : \"success\"}\">\n ${props.telegramSetupMessage.text}\n </div>\n `\n : nothing}\n </div>\n `;\n}\n\nfunction renderTelegramPendingApprovals(props: ChannelsProps) {\n const approvals = props.telegramPendingApprovals;\n\n return html`\n <div\n style=\"margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 12px; display: grid; gap: 12px;\"\n >\n <div class=\"row\" style=\"justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;\">\n <div>\n <div style=\"font-weight: 600;\">Pending approvals</div>\n <div class=\"muted\" style=\"font-size: 13px;\">\n Approve or reject new Telegram users without using the terminal.\n </div>\n </div>\n <button\n class=\"btn\"\n ?disabled=${props.telegramApprovalsLoading || !props.connected || Boolean(props.telegramApprovalsBusyCode)}\n @click=${() => props.onTelegramApprovalsRefresh()}\n >\n ${props.telegramApprovalsLoading ? \"Refreshing...\" : \"Refresh\"}\n </button>\n </div>\n\n ${props.telegramApprovalsMessage\n ? html`\n <div class=\"callout ${props.telegramApprovalsMessage.kind === \"error\" ? \"danger\" : \"success\"}\">\n ${props.telegramApprovalsMessage.text}\n </div>\n `\n : nothing}\n\n ${props.telegramApprovalsLoading && approvals.length === 0\n ? html`<div class=\"muted\" style=\"font-size: 13px;\">Loading pending approvals...</div>`\n : approvals.length === 0\n ? html`<div class=\"muted\" style=\"font-size: 13px;\">No pending approvals.</div>`\n : html`\n <div style=\"display: grid; gap: 8px;\">\n ${approvals.map((approval) => {\n const busy = props.telegramApprovalsBusyCode === approval.code;\n return html`\n <div\n style=\"padding: 12px; border: 1px solid var(--border); border-radius: 10px; display: grid; gap: 10px;\"\n >\n <div style=\"display: grid; gap: 4px;\">\n <div style=\"font-weight: 600;\">Telegram user ID ${approval.userId}</div>\n <div class=\"muted\" style=\"font-size: 12px;\">Pairing code ${approval.code}</div>\n <div class=\"muted\" style=\"font-size: 12px;\">\n Requested ${formatRelativeTimestamp(approval.createdAt)}\n </div>\n </div>\n <div class=\"row\" style=\"gap: 8px; flex-wrap: wrap; align-items: center;\">\n <button\n class=\"btn\"\n style=\"background: var(--success, #15803d); border-color: var(--success, #15803d); color: white;\"\n ?disabled=${busy || !props.connected}\n @click=${() => props.onTelegramApprove(approval.code)}\n >\n ${busy ? \"Working...\" : \"Approve\"}\n </button>\n <button\n class=\"btn\"\n ?disabled=${busy || !props.connected}\n @click=${() => props.onTelegramReject(approval.code)}\n >\n Reject\n </button>\n </div>\n </div>\n `;\n })}\n </div>\n `}\n </div>\n `;\n}\n\nfunction renderTelegramAccountCard(account: ChannelAccountSnapshot) {\n const probe = account.probe as { bot?: { username?: string } } | undefined;\n const botUsername = probe?.bot?.username;\n const label = account.name || account.accountId;\n return html`\n <div class=\"account-card\">\n <div class=\"account-card-header\">\n <div class=\"account-card-title\">${botUsername ? `@${botUsername}` : label}</div>\n <div class=\"account-card-id\">${account.accountId}</div>\n </div>\n <div class=\"status-list account-card-status\">\n <div>\n <span class=\"label\">Running</span>\n <span>${account.running ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Configured</span>\n <span>${account.configured ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Last inbound</span>\n <span>${account.lastInboundAt ? formatRelativeTimestamp(account.lastInboundAt) : \"n/a\"}</span>\n </div>\n ${account.lastError ? html`<div class=\"account-card-error\">${account.lastError}</div>` : nothing}\n </div>\n </div>\n `;\n}\n\nfunction renderTelegramConnectedContent(params: {\n telegram: TelegramStatus | undefined;\n telegramAccounts: ChannelAccountSnapshot[];\n}) {\n const { telegram, telegramAccounts } = params;\n if (telegramAccounts.length > 1) {\n return html`\n <div class=\"account-card-list\" style=\"margin-top: 16px;\">\n ${telegramAccounts.map((account) => renderTelegramAccountCard(account))}\n </div>\n `;\n }\n\n return html`\n <div class=\"status-list\" style=\"margin-top: 16px;\">\n <div>\n <span class=\"label\">Running</span>\n <span>${telegram?.running ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Mode</span>\n <span>${telegram?.mode ?? \"n/a\"}</span>\n </div>\n <div>\n <span class=\"label\">Last start</span>\n <span>${telegram?.lastStartAt ? formatRelativeTimestamp(telegram.lastStartAt) : \"n/a\"}</span>\n </div>\n <div>\n <span class=\"label\">Last probe</span>\n <span>${telegram?.lastProbeAt ? formatRelativeTimestamp(telegram.lastProbeAt) : \"n/a\"}</span>\n </div>\n </div>\n `;\n}\n\nexport function renderTelegramCard(params: {\n props: ChannelsProps;\n telegram?: TelegramStatus;\n telegramAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n}) {\n const { props, telegram, telegramAccounts, accountCountLabel } = params;\n const connected = isTelegramConnected(props, telegram, telegramAccounts);\n const busy = props.telegramSetupBusy || props.configSaving;\n const tokenSource = resolveTelegramTokenSource(telegram, telegramAccounts);\n const canDisconnect = tokenSource === \"config\" || hasStoredTelegramToken(props);\n\n if (!connected) {\n return html`\n <div class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between; align-items: flex-start; gap: 12px;\">\n <div>\n <div class=\"card-title\">Telegram</div>\n <div class=\"card-sub\">Connect a Telegram bot to start receiving and sending messages.</div>\n </div>\n <span class=\"pill pill--sm\">Not connected</span>\n </div>\n ${accountCountLabel}\n ${renderTelegramSetupSection({ props, busy, tokenSource })}\n </div>\n `;\n }\n\n return html`\n <div class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap;\">\n <div>\n <div class=\"card-title\">Telegram</div>\n <div class=\"card-sub\">Bot status, approvals, and channel configuration.</div>\n </div>\n <span class=\"pill pill--sm pill--ok\">Connected</span>\n </div>\n\n ${accountCountLabel}\n ${renderTelegramConnectedContent({ telegram, telegramAccounts })}\n\n ${telegram?.lastError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${telegram.lastError}</div>`\n : nothing}\n ${telegram?.probe\n ? html`<div class=\"callout\" style=\"margin-top: 12px;\">\n Probe ${telegram.probe.ok ? \"ok\" : \"failed\"} · ${telegram.probe.status ?? \"\"}\n ${telegram.probe.error ?? \"\"}\n </div>`\n : nothing}\n\n ${renderTelegramPendingApprovals(props)}\n ${renderChannelConfigSection({ channelId: \"telegram\", props })}\n\n <div class=\"row\" style=\"margin-top: 12px; gap: 8px; flex-wrap: wrap; align-items: center;\">\n ${canDisconnect\n ? html`\n <button\n class=\"btn\"\n ?disabled=${busy || !props.connected}\n @click=${() => props.onTelegramDisconnect()}\n >\n Disconnect\n </button>\n `\n : nothing}\n <button class=\"btn\" ?disabled=${props.loading} @click=${() => props.onRefresh(true)}>Probe</button>\n ${tokenSource && tokenSource !== \"config\"\n ? html`<div class=\"muted\" style=\"font-size: 12px;\">Current token source: ${tokenSource}</div>`\n : nothing}\n </div>\n\n ${props.telegramSetupMessage && props.telegramSetupMessage.kind === \"error\"\n ? html`\n <div class=\"callout danger\" style=\"margin-top: 12px;\">\n ${props.telegramSetupMessage.text}\n </div>\n `\n : nothing}\n </div>\n `;\n}","import { html, nothing } from \"lit\";\nimport { formatRelativeTimestamp } from \"../format.ts\";\nimport type {\n ChannelAccountSnapshot,\n ChannelUiMetaEntry,\n ChannelsStatusSnapshot,\n DiscordStatus,\n GoogleChatStatus,\n IMessageStatus,\n NostrProfile,\n NostrStatus,\n SignalStatus,\n SlackStatus,\n TelegramStatus,\n WhatsAppStatus,\n} from \"../types.ts\";\nimport { renderChannelConfigSection } from \"./channels.config.ts\";\nimport { renderDiscordCard } from \"./channels.discord.ts\";\nimport { renderGoogleChatCard } from \"./channels.googlechat.ts\";\nimport { renderIMessageCard } from \"./channels.imessage.ts\";\nimport { renderNostrCard } from \"./channels.nostr.ts\";\nimport {\n channelEnabled,\n formatNullableBoolean,\n renderChannelAccountCount,\n resolveChannelDisplayState,\n} from \"./channels.shared.ts\";\nimport { renderSignalCard } from \"./channels.signal.ts\";\nimport { renderSlackCard } from \"./channels.slack.ts\";\nimport { renderTelegramCard } from \"./channels.telegram.ts\";\nimport type { ChannelKey, ChannelsChannelData, ChannelsProps } from \"./channels.types.ts\";\nimport { renderWhatsAppCard } from \"./channels.whatsapp.ts\";\n\nconst SHOW_WHATSAPP_CHANNEL = false;\nconst SUPPORTED_CHANNELS = [\"telegram\"] as const satisfies readonly ChannelKey[];\n\nexport function renderChannels(props: ChannelsProps) {\n const channels = props.snapshot?.channels as Record<string, unknown> | null;\n const whatsapp = (channels?.whatsapp ?? undefined) as WhatsAppStatus | undefined;\n const telegram = (channels?.telegram ?? undefined) as TelegramStatus | undefined;\n const discord = (channels?.discord ?? null) as DiscordStatus | null;\n const googlechat = (channels?.googlechat ?? null) as GoogleChatStatus | null;\n const slack = (channels?.slack ?? null) as SlackStatus | null;\n const signal = (channels?.signal ?? null) as SignalStatus | null;\n const imessage = (channels?.imessage ?? null) as IMessageStatus | null;\n const nostr = (channels?.nostr ?? null) as NostrStatus | null;\n const channelOrder = resolveChannelOrder(props.snapshot);\n const orderedChannels = channelOrder\n .map((key, index) => ({\n key,\n enabled: channelEnabled(key, props),\n order: index,\n }))\n .toSorted((a, b) => {\n if (a.enabled !== b.enabled) {\n return a.enabled ? -1 : 1;\n }\n return a.order - b.order;\n });\n\n return html`\n <section class=\"grid grid-cols-2\">\n ${orderedChannels.map((channel) =>\n renderChannel(channel.key, props, {\n whatsapp,\n telegram,\n discord,\n googlechat,\n slack,\n signal,\n imessage,\n nostr,\n channelAccounts: props.snapshot?.channelAccounts ?? null,\n }),\n )}\n </section>\n\n <section class=\"card\" style=\"margin-top: 18px;\">\n <div class=\"row\" style=\"justify-content: space-between;\">\n <div>\n <div class=\"card-title\">Channel health</div>\n <div class=\"card-sub\">Channel status snapshots from the gateway.</div>\n </div>\n <div class=\"muted\">\n ${props.lastSuccessAt ? formatRelativeTimestamp(props.lastSuccessAt) : \"n/a\"}\n </div>\n </div>\n ${props.lastError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${props.lastError}</div>`\n : nothing}\n <pre class=\"code-block\" style=\"margin-top: 12px;\">\n${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : \"No snapshot yet.\"}\n </pre\n >\n </section>\n `;\n}\n\nfunction resolveChannelOrder(snapshot: ChannelsStatusSnapshot | null): ChannelKey[] {\n const snapshotOrder = snapshot?.channelMeta?.length\n ? snapshot.channelMeta.map((entry) => entry.id)\n : snapshot?.channelOrder?.length\n ? snapshot.channelOrder\n : [];\n return [...new Set<ChannelKey>([...SUPPORTED_CHANNELS, ...snapshotOrder])].filter(\n (channelId) => SHOW_WHATSAPP_CHANNEL || channelId !== \"whatsapp\",\n );\n}\n\nfunction renderChannel(key: ChannelKey, props: ChannelsProps, data: ChannelsChannelData) {\n const accountCountLabel = renderChannelAccountCount(key, data.channelAccounts);\n switch (key) {\n case \"whatsapp\":\n if (!SHOW_WHATSAPP_CHANNEL) {\n return nothing;\n }\n return renderWhatsAppCard({\n props,\n whatsapp: data.whatsapp,\n accountCountLabel,\n });\n case \"telegram\":\n return renderTelegramCard({\n props,\n telegram: data.telegram,\n telegramAccounts: data.channelAccounts?.telegram ?? [],\n accountCountLabel,\n });\n case \"discord\":\n return renderDiscordCard({\n props,\n discord: data.discord,\n accountCountLabel,\n });\n case \"googlechat\":\n return renderGoogleChatCard({\n props,\n googleChat: data.googlechat,\n accountCountLabel,\n });\n case \"slack\":\n return renderSlackCard({\n props,\n slack: data.slack,\n accountCountLabel,\n });\n case \"signal\":\n return renderSignalCard({\n props,\n signal: data.signal,\n accountCountLabel,\n });\n case \"imessage\":\n return renderIMessageCard({\n props,\n imessage: data.imessage,\n accountCountLabel,\n });\n case \"nostr\": {\n const nostrAccounts = data.channelAccounts?.nostr ?? [];\n const primaryAccount = nostrAccounts[0];\n const accountId = primaryAccount?.accountId ?? \"default\";\n const profile =\n (primaryAccount as { profile?: NostrProfile | null } | undefined)?.profile ?? null;\n const showForm =\n props.nostrProfileAccountId === accountId ? props.nostrProfileFormState : null;\n const profileFormCallbacks = showForm\n ? {\n onFieldChange: props.onNostrProfileFieldChange,\n onSave: props.onNostrProfileSave,\n onImport: props.onNostrProfileImport,\n onCancel: props.onNostrProfileCancel,\n onToggleAdvanced: props.onNostrProfileToggleAdvanced,\n }\n : null;\n return renderNostrCard({\n props,\n nostr: data.nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState: showForm,\n profileFormCallbacks,\n onEditProfile: () => props.onNostrProfileEdit(accountId, profile),\n });\n }\n default:\n return renderGenericChannelCard(key, props, data.channelAccounts ?? {});\n }\n}\n\nfunction renderGenericChannelCard(\n key: ChannelKey,\n props: ChannelsProps,\n channelAccounts: Record<string, ChannelAccountSnapshot[]>,\n) {\n const label = resolveChannelLabel(props.snapshot, key);\n const displayState = resolveChannelDisplayState(key, props);\n const lastError =\n typeof displayState.status?.lastError === \"string\" ? displayState.status.lastError : undefined;\n const accounts = channelAccounts[key] ?? [];\n const accountCountLabel = renderChannelAccountCount(key, channelAccounts);\n\n return html`\n <div class=\"card\">\n <div class=\"card-title\">${label}</div>\n <div class=\"card-sub\">Channel status and configuration.</div>\n ${accountCountLabel}\n ${accounts.length > 0\n ? html`\n <div class=\"account-card-list\">\n ${accounts.map((account) => renderGenericAccount(account))}\n </div>\n `\n : html`\n <div class=\"status-list\" style=\"margin-top: 16px;\">\n <div>\n <span class=\"label\">Configured</span>\n <span>${formatNullableBoolean(displayState.configured)}</span>\n </div>\n <div>\n <span class=\"label\">Running</span>\n <span>${formatNullableBoolean(displayState.running)}</span>\n </div>\n <div>\n <span class=\"label\">Connected</span>\n <span>${formatNullableBoolean(displayState.connected)}</span>\n </div>\n </div>\n `}\n ${lastError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${lastError}</div>`\n : nothing}\n ${renderChannelConfigSection({ channelId: key, props })}\n </div>\n `;\n}\n\nfunction resolveChannelMetaMap(\n snapshot: ChannelsStatusSnapshot | null,\n): Record<string, ChannelUiMetaEntry> {\n if (!snapshot?.channelMeta?.length) {\n return {};\n }\n return Object.fromEntries(snapshot.channelMeta.map((entry) => [entry.id, entry]));\n}\n\nfunction resolveChannelLabel(snapshot: ChannelsStatusSnapshot | null, key: string): string {\n const meta = resolveChannelMetaMap(snapshot)[key];\n return meta?.label ?? snapshot?.channelLabels?.[key] ?? key;\n}\n\nconst RECENT_ACTIVITY_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes\n\nfunction hasRecentActivity(account: ChannelAccountSnapshot): boolean {\n if (!account.lastInboundAt) {\n return false;\n }\n return Date.now() - account.lastInboundAt < RECENT_ACTIVITY_THRESHOLD_MS;\n}\n\nfunction deriveRunningStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" {\n if (account.running) {\n return \"Yes\";\n }\n // If we have recent inbound activity, the channel is effectively running\n if (hasRecentActivity(account)) {\n return \"Active\";\n }\n return \"No\";\n}\n\nfunction deriveConnectedStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" | \"n/a\" {\n if (account.connected === true) {\n return \"Yes\";\n }\n if (account.connected === false) {\n return \"No\";\n }\n // If connected is null/undefined but we have recent activity, show as active\n if (hasRecentActivity(account)) {\n return \"Active\";\n }\n return \"n/a\";\n}\n\nfunction renderGenericAccount(account: ChannelAccountSnapshot) {\n const runningStatus = deriveRunningStatus(account);\n const connectedStatus = deriveConnectedStatus(account);\n\n return html`\n <div class=\"account-card\">\n <div class=\"account-card-header\">\n <div class=\"account-card-title\">${account.name || account.accountId}</div>\n <div class=\"account-card-id\">${account.accountId}</div>\n </div>\n <div class=\"status-list account-card-status\">\n <div>\n <span class=\"label\">Running</span>\n <span>${runningStatus}</span>\n </div>\n <div>\n <span class=\"label\">Configured</span>\n <span>${account.configured ? \"Yes\" : \"No\"}</span>\n </div>\n <div>\n <span class=\"label\">Connected</span>\n <span>${connectedStatus}</span>\n </div>\n <div>\n <span class=\"label\">Last inbound</span>\n <span\n >${account.lastInboundAt ? formatRelativeTimestamp(account.lastInboundAt) : \"n/a\"}</span\n >\n </div>\n ${account.lastError\n ? html` <div class=\"account-card-error\">${account.lastError}</div> `\n : nothing}\n </div>\n </div>\n `;\n}\n"],"mappings":"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"}