@burdenoff/microfe-channel 2026.715.2 → 2026.731.1

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.
@@ -0,0 +1,23 @@
1
+ interface MarkdownEditorProps {
2
+ /** Current markdown value. */
3
+ value: string;
4
+ /** Called whenever the markdown value changes. */
5
+ onChange: (value: string) => void;
6
+ /** Optional visible label. */
7
+ label?: string;
8
+ /** Placeholder for the write tab textarea. */
9
+ placeholder?: string;
10
+ /** Approximate visible rows for the textarea. */
11
+ rows?: number;
12
+ /** Accessible label for the textarea (used when no visible label). */
13
+ 'aria-label'?: string;
14
+ /** Additional classes for the outer container. */
15
+ className?: string;
16
+ /** Additional classes for the textarea. */
17
+ textareaClassName?: string;
18
+ /** Unique id, forwarded to the textarea. */
19
+ id?: string;
20
+ }
21
+ export declare function MarkdownEditor({ value, onChange, label, placeholder, rows, 'aria-label': ariaLabel, className, textareaClassName, id, }: MarkdownEditorProps): import("react").JSX.Element;
22
+ export {};
23
+ //# sourceMappingURL=MarkdownEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.d.ts","sourceRoot":"","sources":["../../../src/channel/components/MarkdownEditor.tsx"],"names":[],"mappings":"AAQA,UAAU,mBAAmB;IAC3B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAkED,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,IAAQ,EACR,YAAY,EAAE,SAAS,EACvB,SAAc,EACd,iBAAsB,EACtB,EAAE,GACH,EAAE,mBAAmB,+BAgKrB"}
@@ -0,0 +1,172 @@
1
+ import { useRef as e, useState as t } from "react";
2
+ import { Bold as n, Code as r, Italic as i, Link as a, List as o } from "lucide-react";
3
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
4
+ import { Markdown as l } from "@burdenoff/fe-libs/shared/components/Markdown";
5
+ //#region src/channel/components/MarkdownEditor.tsx
6
+ var u = (e, t, n) => (r, i, a) => {
7
+ let o = r.slice(0, i), s = r.slice(i, a), c = r.slice(a);
8
+ if (s.length === 0) {
9
+ let r = `${e}${n}${t}`, a = i + e.length, s = a + n.length;
10
+ return {
11
+ nextValue: `${o}${r}${c}`,
12
+ cursorStart: a,
13
+ cursorEnd: s
14
+ };
15
+ }
16
+ let l = `${e}${s}${t}`, u = i + l.length;
17
+ return {
18
+ nextValue: `${o}${l}${c}`,
19
+ cursorStart: u,
20
+ cursorEnd: u
21
+ };
22
+ }, d = (e, t, n) => {
23
+ let r = e.slice(0, t), i = e.slice(t, n), a = e.slice(n), o = `[${i.length > 0 ? i : "text"}](https://example.com)`, s = t + o.length - 1, c = s + 1;
24
+ return {
25
+ nextValue: `${r}${o}${a}`,
26
+ cursorStart: s,
27
+ cursorEnd: c
28
+ };
29
+ }, f = (e, t, n) => {
30
+ let r = e.slice(0, t), i = e.slice(t, n), a = e.slice(n);
31
+ if (i.length === 0) {
32
+ let e = t + 2;
33
+ return {
34
+ nextValue: `${r}- ${a}`,
35
+ cursorStart: e,
36
+ cursorEnd: e
37
+ };
38
+ }
39
+ let o = i.split("\n").map((e) => e.startsWith("- ") ? e : `- ${e}`).join("\n");
40
+ return {
41
+ nextValue: `${r}${o}${a}`,
42
+ cursorStart: t,
43
+ cursorEnd: t + o.length
44
+ };
45
+ };
46
+ function p({ value: p, onChange: m, label: h, placeholder: g, rows: _ = 5, "aria-label": v, className: y = "", textareaClassName: b = "", id: x }) {
47
+ let [S, C] = t("write"), w = e(null), T = (e) => {
48
+ let t = w.current;
49
+ if (!t) return;
50
+ let { nextValue: n, cursorStart: r, cursorEnd: i } = e(p, t.selectionStart ?? 0, t.selectionEnd ?? 0);
51
+ m(n), requestAnimationFrame(() => {
52
+ t.focus(), t.setSelectionRange(r, i);
53
+ });
54
+ }, E = (e) => {
55
+ switch (e) {
56
+ case "bold":
57
+ T(u("**", "**", "bold text"));
58
+ break;
59
+ case "italic":
60
+ T(u("*", "*", "italic text"));
61
+ break;
62
+ case "link":
63
+ T(d);
64
+ break;
65
+ case "code":
66
+ T(u("`", "`", "code"));
67
+ break;
68
+ case "list":
69
+ T(f);
70
+ break;
71
+ }
72
+ };
73
+ return /* @__PURE__ */ c("div", {
74
+ className: "space-y-2",
75
+ children: [h && /* @__PURE__ */ s("label", {
76
+ htmlFor: x,
77
+ className: "text-sm font-medium text-text-primary",
78
+ children: h
79
+ }), /* @__PURE__ */ c("div", {
80
+ className: `border border-border-subtle rounded-lg overflow-hidden bg-bg-surface focus-within:ring-2 focus-within:ring-primary ${y}`,
81
+ children: [/* @__PURE__ */ c("div", {
82
+ className: "flex items-center justify-between border-b border-border-subtle bg-bg-sunken px-2 py-1",
83
+ children: [/* @__PURE__ */ c("div", {
84
+ className: "flex items-center gap-1",
85
+ children: [
86
+ /* @__PURE__ */ s("button", {
87
+ type: "button",
88
+ onClick: () => E("bold"),
89
+ "aria-label": "Bold",
90
+ title: "Bold",
91
+ className: "p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors",
92
+ children: /* @__PURE__ */ s(n, { className: "size-4" })
93
+ }),
94
+ /* @__PURE__ */ s("button", {
95
+ type: "button",
96
+ onClick: () => E("italic"),
97
+ "aria-label": "Italic",
98
+ title: "Italic",
99
+ className: "p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors",
100
+ children: /* @__PURE__ */ s(i, { className: "size-4" })
101
+ }),
102
+ /* @__PURE__ */ s("button", {
103
+ type: "button",
104
+ onClick: () => E("link"),
105
+ "aria-label": "Link",
106
+ title: "Link",
107
+ className: "p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors",
108
+ children: /* @__PURE__ */ s(a, { className: "size-4" })
109
+ }),
110
+ /* @__PURE__ */ s("button", {
111
+ type: "button",
112
+ onClick: () => E("code"),
113
+ "aria-label": "Code",
114
+ title: "Code",
115
+ className: "p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors",
116
+ children: /* @__PURE__ */ s(r, { className: "size-4" })
117
+ }),
118
+ /* @__PURE__ */ s("button", {
119
+ type: "button",
120
+ onClick: () => E("list"),
121
+ "aria-label": "Bullet list",
122
+ title: "Bullet list",
123
+ className: "p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors",
124
+ children: /* @__PURE__ */ s(o, { className: "size-4" })
125
+ })
126
+ ]
127
+ }), /* @__PURE__ */ c("div", {
128
+ className: "flex items-center gap-1",
129
+ role: "tablist",
130
+ "aria-label": "Markdown editor tabs",
131
+ children: [/* @__PURE__ */ s("button", {
132
+ type: "button",
133
+ role: "tab",
134
+ "aria-selected": S === "write",
135
+ onClick: () => C("write"),
136
+ className: `px-3 py-1 text-xs font-medium rounded-md transition-colors ${S === "write" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary hover:bg-bg-surface"}`,
137
+ children: "Write"
138
+ }), /* @__PURE__ */ s("button", {
139
+ type: "button",
140
+ role: "tab",
141
+ "aria-selected": S === "preview",
142
+ onClick: () => C("preview"),
143
+ className: `px-3 py-1 text-xs font-medium rounded-md transition-colors ${S === "preview" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary hover:bg-bg-surface"}`,
144
+ children: "Preview"
145
+ })]
146
+ })]
147
+ }), /* @__PURE__ */ s("div", {
148
+ className: "relative",
149
+ children: S === "write" ? /* @__PURE__ */ s("textarea", {
150
+ ref: w,
151
+ id: x,
152
+ "aria-label": v,
153
+ value: p,
154
+ onChange: (e) => m(e.target.value),
155
+ placeholder: g,
156
+ rows: _,
157
+ className: `w-full px-4 py-2 bg-bg-surface text-text-primary placeholder:text-text-secondary resize-none focus:outline-none ${b}`
158
+ }) : /* @__PURE__ */ s("div", {
159
+ className: "min-h-[theme(spacing.32)] max-h-96 overflow-auto px-4 py-2 text-sm text-text-primary",
160
+ children: p.trim() ? /* @__PURE__ */ s(l, { children: p }) : /* @__PURE__ */ s("p", {
161
+ className: "text-text-secondary italic",
162
+ children: g || "Nothing to preview yet."
163
+ })
164
+ })
165
+ })]
166
+ })]
167
+ });
168
+ }
169
+ //#endregion
170
+ export { p as MarkdownEditor };
171
+
172
+ //# sourceMappingURL=MarkdownEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../src/channel/components/MarkdownEditor.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components/Markdown';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface MarkdownEditorProps {\n /** Current markdown value. */\n value: string;\n /** Called whenever the markdown value changes. */\n onChange: (value: string) => void;\n /** Optional visible label. */\n label?: string;\n /** Placeholder for the write tab textarea. */\n placeholder?: string;\n /** Approximate visible rows for the textarea. */\n rows?: number;\n /** Accessible label for the textarea (used when no visible label). */\n 'aria-label'?: string;\n /** Additional classes for the outer container. */\n className?: string;\n /** Additional classes for the textarea. */\n textareaClassName?: string;\n /** Unique id, forwarded to the textarea. */\n id?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\ntype InsertAction = (\n value: string,\n selectionStart: number,\n selectionEnd: number\n) => { nextValue: string; cursorStart: number; cursorEnd: number };\n\nconst wrapWith = (prefix: string, suffix: string, fallback: string): InsertAction => {\n return (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = `${prefix}${fallback}${suffix}`;\n const cursorStart = start + prefix.length;\n const cursorEnd = cursorStart + fallback.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n }\n\n const wrapped = `${prefix}${selected}${suffix}`;\n const cursorStart = start + wrapped.length;\n return { nextValue: `${before}${wrapped}${after}`, cursorStart, cursorEnd: cursorStart };\n };\n};\n\nconst insertLink: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n const linkText = selected.length > 0 ? selected : 'text';\n const insertion = `[${linkText}](https://example.com)`;\n const cursorStart = start + insertion.length - 1;\n const cursorEnd = cursorStart + 1;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n};\n\nconst insertList: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = '- ';\n const cursorStart = start + insertion.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd: cursorStart };\n }\n\n const lines = selected.split('\\n');\n const listified = lines.map((line) => (line.startsWith('- ') ? line : `- ${line}`)).join('\\n');\n const nextValue = `${before}${listified}${after}`;\n const cursorStart = start;\n const cursorEnd = start + listified.length;\n return { nextValue, cursorStart, cursorEnd };\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function MarkdownEditor({\n value,\n onChange,\n label,\n placeholder,\n rows = 5,\n 'aria-label': ariaLabel,\n className = '',\n textareaClassName = '',\n id,\n}: MarkdownEditorProps) {\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const apply = (action: InsertAction) => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n const start = textarea.selectionStart ?? 0;\n const end = textarea.selectionEnd ?? 0;\n const { nextValue, cursorStart, cursorEnd } = action(value, start, end);\n\n onChange(nextValue);\n\n // Re-focus and restore selection after the next render has applied the value.\n requestAnimationFrame(() => {\n textarea.focus();\n textarea.setSelectionRange(cursorStart, cursorEnd);\n });\n };\n\n const handleToolbarAction = (action: 'bold' | 'italic' | 'link' | 'code' | 'list') => {\n switch (action) {\n case 'bold':\n apply(wrapWith('**', '**', 'bold text'));\n break;\n case 'italic':\n apply(wrapWith('*', '*', 'italic text'));\n break;\n case 'link':\n apply(insertLink);\n break;\n case 'code':\n apply(wrapWith('`', '`', 'code'));\n break;\n case 'list':\n apply(insertList);\n break;\n }\n };\n\n const containerBase =\n 'border border-border-subtle rounded-lg overflow-hidden bg-bg-surface focus-within:ring-2 focus-within:ring-primary';\n const textareaBase =\n 'w-full px-4 py-2 bg-bg-surface text-text-primary placeholder:text-text-secondary resize-none focus:outline-none';\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"text-sm font-medium text-text-primary\">\n {label}\n </label>\n )}\n <div className={`${containerBase} ${className}`}>\n <div className=\"flex items-center justify-between border-b border-border-subtle bg-bg-sunken px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('bold')}\n aria-label=\"Bold\"\n title=\"Bold\"\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Bold className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('italic')}\n aria-label=\"Italic\"\n title=\"Italic\"\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Italic className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('link')}\n aria-label=\"Link\"\n title=\"Link\"\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Link className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('code')}\n aria-label=\"Code\"\n title=\"Code\"\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Code className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('list')}\n aria-label=\"Bullet list\"\n title=\"Bullet list\"\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <List className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"flex items-center gap-1\" role=\"tablist\" aria-label=\"Markdown editor tabs\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'write'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n Write\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'preview'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n Preview\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n {activeTab === 'write' ? (\n <textarea\n ref={textareaRef}\n id={id}\n aria-label={ariaLabel}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n rows={rows}\n className={`${textareaBase} ${textareaClassName}`}\n />\n ) : (\n <div className=\"min-h-[theme(spacing.32)] max-h-96 overflow-auto px-4 py-2 text-sm text-text-primary\">\n {value.trim() ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">\n {placeholder || 'Nothing to preview yet.'}\n </p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelDetailsPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelDetailsPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAgIH,wBAAgB,kBAAkB,gCA6lCjC"}
1
+ {"version":3,"file":"ChannelDetailsPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelDetailsPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAiIH,wBAAgB,kBAAkB,gCA+lCjC"}
@@ -12,6 +12,7 @@ import { usePermissions as we } from "@burdenoff/fe-libs/shared/providers/shell"
12
12
  import { jsx as x, jsxs as S } from "react/jsx-runtime";
13
13
  import { CTAOverflowMenu as Te, EmphasisPanel as Ee, GlassCard as C, NextSteps as De, PagePurpose as Oe, ResponsiveDialog as ke, ResponsiveDialogContent as Ae, ResponsiveDialogHeader as je, ResponsiveDialogTitle as Me } from "@burdenoff/fe-libs/ui";
14
14
  import { TagBadge as Ne } from "@burdenoff/fe-libs/tag";
15
+ import { Markdown as Pe } from "@burdenoff/fe-libs/shared/components/Markdown";
15
16
  //#region src/channel/modules/channels/pages/ChannelDetailsPage.tsx
16
17
  var w = {
17
18
  EMAIL: {
@@ -103,42 +104,42 @@ var w = {
103
104
  }
104
105
  };
105
106
  function D() {
106
- let { channelId: c } = Ce(), f = Se(), D = xe(), { hasPermission: O } = we(), k = O(i.CHANNEL_UPDATE), A = O(i.CHANNEL_EXECUTE), Pe = O(i.CHANNEL_CONNECTION_CREATE), Fe = O(i.CHANNEL_CONNECTION_UPDATE), { t: Ie } = se(), j = (e, t) => {
107
- let n = Ie(e);
107
+ let { channelId: c } = Ce(), f = Se(), D = xe(), { hasPermission: O } = we(), k = O(i.CHANNEL_UPDATE), A = O(i.CHANNEL_EXECUTE), Fe = O(i.CHANNEL_CONNECTION_CREATE), Ie = O(i.CHANNEL_CONNECTION_UPDATE), { t: Le } = se(), j = (e, t) => {
108
+ let n = Le(e);
108
109
  return n === e ? t : n;
109
- }, M = D.state, [N, P] = o(null), [F, I] = o(M?.showCredentialsForm || !1), [L, R] = o(M?.pendingCredentials || {}), [Le, z] = o(!1), [B, V] = o(null), [Re, H] = o(!1), [U, W] = o(""), [G, ze] = o(() => j("channel.testNotificationSubject", "Test Notification")), [K, Be] = o(() => j("channel.testNotificationBody", "This is a test notification from Burdenoff Channel Service.")), [q, J] = o(null);
110
+ }, M = D.state, [N, P] = o(null), [F, I] = o(M?.showCredentialsForm || !1), [L, R] = o(M?.pendingCredentials || {}), [Re, z] = o(!1), [B, V] = o(null), [ze, H] = o(!1), [U, W] = o(""), [G, Be] = o(() => j("channel.testNotificationSubject", "Test Notification")), [K, Ve] = o(() => j("channel.testNotificationBody", "This is a test notification from Burdenoff Channel Service.")), [q, J] = o(null);
110
111
  oe(() => {
111
112
  (M?.pendingCredentials || M?.showCredentialsForm) && f(D.pathname, {
112
113
  replace: !0,
113
114
  state: null
114
115
  });
115
116
  }, []);
116
- let { channel: Y, isLoading: Ve, error: X, updateChannel: He, runHealthCheck: Ue, createConnection: We, updateConnection: Ge, deleteConnection: Ke, testConnection: qe, sendTestNotification: Je } = n(c), Ye = async () => {
117
+ let { channel: Y, isLoading: He, error: X, updateChannel: Ue, runHealthCheck: We, createConnection: Ge, updateConnection: Ke, deleteConnection: qe, testConnection: Je, sendTestNotification: Ye } = n(c), Xe = async () => {
117
118
  P("health");
118
119
  try {
119
- await Ue();
120
+ await We();
120
121
  } catch (e) {
121
122
  console.error("[channel] Failed to run health check", e);
122
123
  } finally {
123
124
  P(null);
124
125
  }
125
- }, Xe = async () => {
126
+ }, Ze = async () => {
126
127
  if (Y) {
127
128
  P("status");
128
129
  try {
129
- await He({ status: Y.status === "ACTIVE" ? "INACTIVE" : "ACTIVE" });
130
+ await Ue({ status: Y.status === "ACTIVE" ? "INACTIVE" : "ACTIVE" });
130
131
  } catch (e) {
131
132
  console.error("[channel] Failed to toggle channel status", e);
132
133
  } finally {
133
134
  P(null);
134
135
  }
135
136
  }
136
- }, Ze = async () => {
137
+ }, Qe = async () => {
137
138
  if (Y) {
138
139
  P("connection");
139
140
  try {
140
141
  let e = "API_KEY", t = Y.adapterType;
141
- t?.includes("BOT") ? e = "BOT_TOKEN" : t?.includes("WEBHOOK") ? e = "WEBHOOK" : t === "SMTP" && (e = "SMTP_IMAP"), await We({
142
+ t?.includes("BOT") ? e = "BOT_TOKEN" : t?.includes("WEBHOOK") ? e = "WEBHOOK" : t === "SMTP" && (e = "SMTP_IMAP"), await Ge({
142
143
  authType: e,
143
144
  credentials: L
144
145
  }), I(!1), R({}), z(!1);
@@ -148,28 +149,28 @@ function D() {
148
149
  P(null);
149
150
  }
150
151
  }
151
- }, Qe = async (e) => {
152
+ }, $e = async (e) => {
152
153
  P("connection");
153
154
  try {
154
- await Ge(e, { credentials: L }), I(!1), R({}), z(!1);
155
+ await Ke(e, { credentials: L }), I(!1), R({}), z(!1);
155
156
  } catch (e) {
156
157
  console.error("[channel] Failed to update connection", e);
157
158
  } finally {
158
159
  P(null);
159
160
  }
160
- }, $e = async (n) => {
161
+ }, et = async (n) => {
161
162
  e("medium"), P("delete-connection");
162
163
  try {
163
- await Ke(n), t("success");
164
+ await qe(n), t("success");
164
165
  } catch (e) {
165
166
  t("error"), console.error("[channel] Failed to delete connection", e);
166
167
  } finally {
167
168
  P(null);
168
169
  }
169
- }, et = async (e) => {
170
+ }, tt = async (e) => {
170
171
  P("test"), V(null);
171
172
  try {
172
- let t = await qe(e);
173
+ let t = await Je(e);
173
174
  t && V({
174
175
  success: t.success,
175
176
  message: t.message || (t.success ? j("channel.connectionTestSuccess", "Connection successful!") : j("channel.connectionTestFailed", "Connection failed"))
@@ -182,14 +183,14 @@ function D() {
182
183
  } finally {
183
184
  P(null);
184
185
  }
185
- }, tt = async () => {
186
+ }, nt = async () => {
186
187
  if (U.trim()) {
187
188
  P("send-test"), J(null);
188
189
  try {
189
190
  let e = Y?.platform === "EMAIL" ? {
190
191
  subject: G,
191
192
  html: `<p>${K}</p>`
192
- } : void 0, t = await Je(U.trim(), K, e);
193
+ } : void 0, t = await Ye(U.trim(), K, e);
193
194
  t && J({
194
195
  success: t.success,
195
196
  messageId: t.messageId,
@@ -216,7 +217,7 @@ function D() {
216
217
  P(null);
217
218
  }
218
219
  }
219
- }, nt = (e) => {
220
+ }, rt = (e) => {
220
221
  switch (e) {
221
222
  case "EMAIL": return j("channel.recipientPlaceholder.email", "email@example.com");
222
223
  case "SMS": return j("channel.recipientPlaceholder.phone", "+1234567890");
@@ -227,7 +228,7 @@ function D() {
227
228
  case "WHATSAPP": return j("channel.recipientPlaceholder.phone", "+1234567890");
228
229
  default: return j("channel.recipientPlaceholder.default", "Recipient");
229
230
  }
230
- }, rt = (e) => {
231
+ }, it = (e) => {
231
232
  switch (e) {
232
233
  case "EMAIL": return j("channel.recipientLabel.email", "Email Address");
233
234
  case "SMS":
@@ -238,20 +239,20 @@ function D() {
238
239
  case "DISCORD": return j("channel.recipientLabel.channelOrUserId", "Channel/User ID");
239
240
  default: return j("channel.recipientLabel.default", "Recipient");
240
241
  }
241
- }, it = (e) => ({
242
+ }, at = (e) => ({
242
243
  ...w[e] || {
243
244
  label: e,
244
245
  color: "bg-bg-sunken text-text-secondary"
245
246
  },
246
247
  label: a(e, ne, j)
247
- }), at = (e) => ({
248
+ }), ot = (e) => ({
248
249
  ...T[e] || T.INACTIVE,
249
250
  label: a(e, re, j)
250
- }), ot = (e) => ({
251
+ }), st = (e) => ({
251
252
  ...E[e] || E.UNKNOWN,
252
253
  label: a(e, te, j)
253
254
  });
254
- if (Ve) return /* @__PURE__ */ x("div", {
255
+ if (He) return /* @__PURE__ */ x("div", {
255
256
  className: "flex items-center justify-center min-h-screen",
256
257
  children: /* @__PURE__ */ x(p, {
257
258
  size: 32,
@@ -280,7 +281,7 @@ function D() {
280
281
  ]
281
282
  })
282
283
  });
283
- let st = it(Y.platform), Z = at(Y.status), Q = ot(Y.connectionHealth), ct = Z.icon, lt = Q.icon, $ = Y.connection;
284
+ let ct = at(Y.platform), Z = ot(Y.status), Q = st(Y.connectionHealth), lt = Z.icon, ut = Q.icon, $ = Y.connection;
284
285
  return /* @__PURE__ */ x(r, {
285
286
  permission: i.CHANNEL_READ,
286
287
  children: /* @__PURE__ */ S("div", {
@@ -304,9 +305,9 @@ function D() {
304
305
  className: "text-xl sm:text-2xl font-bold text-text-primary",
305
306
  children: Y.name
306
307
  })]
307
- }), Y.description && /* @__PURE__ */ x("p", {
308
+ }), Y.description && /* @__PURE__ */ x("div", {
308
309
  className: "text-sm text-text-secondary mt-1",
309
- children: Y.description
310
+ children: /* @__PURE__ */ x(Pe, { children: Y.description })
310
311
  })] })]
311
312
  }), /* @__PURE__ */ x(Te, {
312
313
  primary: k ? {
@@ -329,7 +330,7 @@ function D() {
329
330
  "aria-hidden": "true"
330
331
  }),
331
332
  disabled: N === "health",
332
- onSelect: () => void Ye()
333
+ onSelect: () => void Xe()
333
334
  }] : [],
334
335
  ...k ? [{
335
336
  label: Y.status === "ACTIVE" ? j("channel.deactivate", "Deactivate") : j("channel.activate", "Activate"),
@@ -345,7 +346,7 @@ function D() {
345
346
  "aria-hidden": "true"
346
347
  }),
347
348
  disabled: N === "status",
348
- onSelect: () => void Xe()
349
+ onSelect: () => void Ze()
349
350
  }] : [],
350
351
  {
351
352
  label: j("channel.testChannel", "Test Channel"),
@@ -379,7 +380,7 @@ function D() {
379
380
  size: 16,
380
381
  "aria-hidden": "true"
381
382
  }),
382
- onClick: () => void Ye()
383
+ onClick: () => void Xe()
383
384
  }] : [] : [{
384
385
  id: "connect",
385
386
  label: j("channel.nextAddConnection", "Add a connection"),
@@ -425,8 +426,8 @@ function D() {
425
426
  className: "text-xs text-text-secondary mb-1",
426
427
  children: j("channel.platform", "Platform")
427
428
  }), /* @__PURE__ */ x("span", {
428
- className: `inline-flex px-2 py-1 text-sm font-semibold rounded-full ${st.color}`,
429
- children: st.label
429
+ className: `inline-flex px-2 py-1 text-sm font-semibold rounded-full ${ct.color}`,
430
+ children: ct.label
430
431
  })]
431
432
  }),
432
433
  /* @__PURE__ */ S(C, {
@@ -437,7 +438,7 @@ function D() {
437
438
  children: j("channel.status", "Status")
438
439
  }), /* @__PURE__ */ S("div", {
439
440
  className: "flex items-center gap-2",
440
- children: [/* @__PURE__ */ x(ct, {
441
+ children: [/* @__PURE__ */ x(lt, {
441
442
  size: 16,
442
443
  className: Z.color.split(" ")[0]
443
444
  }), /* @__PURE__ */ x("span", {
@@ -456,7 +457,7 @@ function D() {
456
457
  }),
457
458
  /* @__PURE__ */ S("div", {
458
459
  className: "flex items-center gap-2",
459
- children: [/* @__PURE__ */ x(lt, {
460
+ children: [/* @__PURE__ */ x(ut, {
460
461
  size: 16,
461
462
  className: Q.color
462
463
  }), /* @__PURE__ */ x("span", {
@@ -679,7 +680,7 @@ function D() {
679
680
  className: "text-lg font-semibold text-text-primary",
680
681
  children: j("channel.connectionAndCredentials", "Connection & Credentials")
681
682
  })]
682
- }), Pe && !$ && !F && /* @__PURE__ */ S("button", {
683
+ }), Fe && !$ && !F && /* @__PURE__ */ S("button", {
683
684
  type: "button",
684
685
  onClick: () => I(!0),
685
686
  className: "flex items-center gap-2 px-3 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors",
@@ -770,7 +771,7 @@ function D() {
770
771
  permission: i.CHANNEL_CONNECTION_UPDATE,
771
772
  children: /* @__PURE__ */ S("button", {
772
773
  type: "button",
773
- onClick: () => et($.id),
774
+ onClick: () => tt($.id),
774
775
  disabled: N === "test",
775
776
  className: "flex items-center gap-2 px-3 py-2 text-sm text-status-info-text hover:bg-status-info-bg-subtle rounded-lg transition-colors disabled:opacity-50",
776
777
  children: [N === "test" ? /* @__PURE__ */ x(p, {
@@ -779,7 +780,7 @@ function D() {
779
780
  }) : /* @__PURE__ */ x(me, { size: 16 }), j("channel.testConnection", "Test Connection")]
780
781
  })
781
782
  }),
782
- Fe && /* @__PURE__ */ S("button", {
783
+ Ie && /* @__PURE__ */ S("button", {
783
784
  type: "button",
784
785
  onClick: () => {
785
786
  I(!0), R($?.platformMetadata ?? {});
@@ -791,7 +792,7 @@ function D() {
791
792
  permission: i.CHANNEL_CONNECTION_DELETE,
792
793
  children: /* @__PURE__ */ S("button", {
793
794
  type: "button",
794
- onClick: () => $e($.id),
795
+ onClick: () => et($.id),
795
796
  disabled: N === "delete-connection",
796
797
  className: "flex items-center gap-2 px-3 py-2 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors disabled:opacity-50",
797
798
  children: [N === "delete-connection" ? /* @__PURE__ */ x(p, {
@@ -861,9 +862,9 @@ function D() {
861
862
  children: /* @__PURE__ */ S("button", {
862
863
  type: "button",
863
864
  onClick: () => {
864
- $ ? Qe($.id) : Ze();
865
+ $ ? $e($.id) : Qe();
865
866
  },
866
- disabled: N === "connection" || !Le && !!Y.adapterType,
867
+ disabled: N === "connection" || !Re && !!Y.adapterType,
867
868
  className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
868
869
  children: [N === "connection" ? /* @__PURE__ */ x(p, {
869
870
  size: 16,
@@ -884,7 +885,7 @@ function D() {
884
885
  ]
885
886
  }),
886
887
  /* @__PURE__ */ x(ke, {
887
- open: Re,
888
+ open: ze,
888
889
  onOpenChange: (e) => {
889
890
  e || H(!1);
890
891
  },
@@ -907,7 +908,7 @@ function D() {
907
908
  /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S("label", {
908
909
  className: "block text-sm font-medium text-text-primary mb-1.5",
909
910
  children: [
910
- rt(Y.platform),
911
+ it(Y.platform),
911
912
  " ",
912
913
  /* @__PURE__ */ x("span", {
913
914
  className: "text-status-error-text",
@@ -917,11 +918,11 @@ function D() {
917
918
  }), /* @__PURE__ */ S("div", {
918
919
  className: "relative",
919
920
  children: [/* @__PURE__ */ x("input", {
920
- "aria-label": nt(Y.platform),
921
+ "aria-label": rt(Y.platform),
921
922
  type: "text",
922
923
  value: U,
923
924
  onChange: (e) => W(e.target.value),
924
- placeholder: nt(Y.platform),
925
+ placeholder: rt(Y.platform),
925
926
  className: "w-full pl-9 pr-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
926
927
  }), /* @__PURE__ */ x("div", {
927
928
  className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary",
@@ -937,7 +938,7 @@ function D() {
937
938
  "aria-label": "Email subject",
938
939
  type: "text",
939
940
  value: G,
940
- onChange: (e) => ze(e.target.value),
941
+ onChange: (e) => Be(e.target.value),
941
942
  placeholder: "Email subject",
942
943
  className: "w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
943
944
  })] }),
@@ -949,7 +950,7 @@ function D() {
949
950
  id: "channel-details-test-message",
950
951
  "aria-label": "Test message content",
951
952
  value: K,
952
- onChange: (e) => Be(e.target.value),
953
+ onChange: (e) => Ve(e.target.value),
953
954
  placeholder: "Test message content",
954
955
  rows: 3,
955
956
  className: "w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none"
@@ -1004,7 +1005,7 @@ function D() {
1004
1005
  permission: i.CHANNEL_DELIVERY_SEND,
1005
1006
  children: /* @__PURE__ */ S("button", {
1006
1007
  type: "button",
1007
- onClick: tt,
1008
+ onClick: nt,
1008
1009
  disabled: N === "send-test" || !U.trim(),
1009
1010
  className: "flex-1 flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
1010
1011
  children: [N === "send-test" ? /* @__PURE__ */ x(p, {