@jiggai/kitchen-plugin-marketing 0.2.9 → 0.2.11
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.
- package/dist/tabs/accounts.js +206 -157
- package/dist/tabs/content-library.js +226 -92
- package/package.json +3 -7
- package/dist/api/routes.js +0 -1486
package/dist/tabs/accounts.js
CHANGED
|
@@ -53,59 +53,59 @@
|
|
|
53
53
|
fontSize: "0.7rem",
|
|
54
54
|
fontWeight: 600,
|
|
55
55
|
color: "white"
|
|
56
|
+
}),
|
|
57
|
+
capPill: (active) => ({
|
|
58
|
+
display: "inline-block",
|
|
59
|
+
background: active ? "rgba(99,179,237,0.15)" : "rgba(255,255,255,0.03)",
|
|
60
|
+
border: `1px solid ${active ? "rgba(99,179,237,0.3)" : "var(--ck-border-subtle)"}`,
|
|
61
|
+
borderRadius: "999px",
|
|
62
|
+
padding: "0.1rem 0.4rem",
|
|
63
|
+
fontSize: "0.6rem",
|
|
64
|
+
color: active ? "rgba(210,235,255,0.9)" : "var(--ck-text-tertiary)"
|
|
56
65
|
})
|
|
57
66
|
};
|
|
58
|
-
const
|
|
59
|
-
x: "\u{1D54F}",
|
|
60
|
-
twitter: "\u{1D54F}",
|
|
61
|
-
instagram: "\u{1F4F7}",
|
|
62
|
-
linkedin: "\u{1F4BC}",
|
|
63
|
-
facebook: "\u{1F4D8}",
|
|
64
|
-
youtube: "\u25B6\uFE0F",
|
|
65
|
-
tiktok: "\u{1F3B5}",
|
|
66
|
-
bluesky: "\u{1F98B}",
|
|
67
|
-
mastodon: "\u{1F418}",
|
|
68
|
-
reddit: "\u{1F916}",
|
|
69
|
-
discord: "\u{1F4AC}",
|
|
70
|
-
telegram: "\u2708\uFE0F",
|
|
71
|
-
pinterest: "\u{1F4CC}",
|
|
72
|
-
threads: "\u{1F9F5}",
|
|
73
|
-
medium: "\u270D\uFE0F",
|
|
74
|
-
wordpress: "\u{1F4DD}"
|
|
75
|
-
};
|
|
76
|
-
const TYPE_COLORS = {
|
|
67
|
+
const BACKEND_COLORS = {
|
|
77
68
|
postiz: "rgba(99,179,237,0.7)",
|
|
78
69
|
gateway: "rgba(134,239,172,0.7)",
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
direct: "rgba(251,191,36,0.7)",
|
|
71
|
+
none: "rgba(100,100,100,0.5)"
|
|
72
|
+
};
|
|
73
|
+
const BACKEND_LABELS = {
|
|
74
|
+
postiz: "Postiz",
|
|
75
|
+
gateway: "OpenClaw",
|
|
76
|
+
direct: "Direct API",
|
|
77
|
+
none: "Not connected"
|
|
81
78
|
};
|
|
82
79
|
function Accounts(props) {
|
|
83
80
|
const teamId = String(props?.teamId || "default");
|
|
84
81
|
const apiBase = useMemo(() => `/api/plugins/marketing`, []);
|
|
85
|
-
const [
|
|
82
|
+
const [drivers, setDrivers] = useState([]);
|
|
86
83
|
const [manualAccounts, setManualAccounts] = useState([]);
|
|
87
84
|
const [loading, setLoading] = useState(true);
|
|
88
|
-
const [detecting, setDetecting] = useState(false);
|
|
89
85
|
const [error, setError] = useState(null);
|
|
90
86
|
const [postizKey, setPostizKey] = useState("");
|
|
91
87
|
const [postizUrl, setPostizUrl] = useState("https://api.postiz.com/public/v1");
|
|
92
88
|
const [showPostizSetup, setShowPostizSetup] = useState(false);
|
|
93
89
|
const [showManual, setShowManual] = useState(false);
|
|
94
|
-
const [manPlatform, setManPlatform] = useState("
|
|
90
|
+
const [manPlatform, setManPlatform] = useState("x");
|
|
95
91
|
const [manName, setManName] = useState("");
|
|
96
92
|
const [manUser, setManUser] = useState("");
|
|
97
93
|
const [manToken, setManToken] = useState("");
|
|
98
94
|
const [saving, setSaving] = useState(false);
|
|
99
|
-
|
|
95
|
+
const getStoredPostiz = () => {
|
|
100
96
|
try {
|
|
101
97
|
const stored = localStorage.getItem(`ck-postiz-${teamId}`);
|
|
102
|
-
if (stored)
|
|
103
|
-
const parsed = JSON.parse(stored);
|
|
104
|
-
setPostizKey(parsed.apiKey || "");
|
|
105
|
-
setPostizUrl(parsed.baseUrl || "https://api.postiz.com/public/v1");
|
|
106
|
-
}
|
|
98
|
+
if (stored) return JSON.parse(stored);
|
|
107
99
|
} catch {
|
|
108
100
|
}
|
|
101
|
+
return null;
|
|
102
|
+
};
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const stored = getStoredPostiz();
|
|
105
|
+
if (stored) {
|
|
106
|
+
setPostizKey(stored.apiKey || "");
|
|
107
|
+
setPostizUrl(stored.baseUrl || "https://api.postiz.com/public/v1");
|
|
108
|
+
}
|
|
109
109
|
}, [teamId]);
|
|
110
110
|
const savePostizConfig = () => {
|
|
111
111
|
try {
|
|
@@ -113,18 +113,9 @@
|
|
|
113
113
|
} catch {
|
|
114
114
|
}
|
|
115
115
|
setShowPostizSetup(false);
|
|
116
|
-
void
|
|
116
|
+
void loadDrivers();
|
|
117
117
|
};
|
|
118
|
-
const
|
|
119
|
-
try {
|
|
120
|
-
const stored = localStorage.getItem(`ck-postiz-${teamId}`);
|
|
121
|
-
if (stored) return JSON.parse(stored);
|
|
122
|
-
} catch {
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
};
|
|
126
|
-
const detectAll = async () => {
|
|
127
|
-
setDetecting(true);
|
|
118
|
+
const loadDrivers = async () => {
|
|
128
119
|
setError(null);
|
|
129
120
|
try {
|
|
130
121
|
const stored = getStoredPostiz();
|
|
@@ -135,13 +126,20 @@
|
|
|
135
126
|
headers["x-postiz-api-key"] = key;
|
|
136
127
|
headers["x-postiz-base-url"] = url;
|
|
137
128
|
}
|
|
138
|
-
const res = await fetch(`${apiBase}/
|
|
129
|
+
const res = await fetch(`${apiBase}/drivers?team=${encodeURIComponent(teamId)}`, { headers });
|
|
130
|
+
if (!res.ok) {
|
|
131
|
+
const errText = await res.text().catch(() => `HTTP ${res.status}`);
|
|
132
|
+
setError(`Driver API error: ${res.status} \u2014 ${errText.slice(0, 200)}`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
139
135
|
const json = await res.json();
|
|
140
|
-
|
|
136
|
+
const list = Array.isArray(json.drivers) ? json.drivers : json.data?.drivers || [];
|
|
137
|
+
setDrivers(list);
|
|
138
|
+
if (list.length === 0) {
|
|
139
|
+
setError("No platform drivers returned. The plugin handler may not be loaded \u2014 try restarting Kitchen.");
|
|
140
|
+
}
|
|
141
141
|
} catch (e) {
|
|
142
|
-
setError(e?.message || "
|
|
143
|
-
} finally {
|
|
144
|
-
setDetecting(false);
|
|
142
|
+
setError(`Failed to load drivers: ${e?.message || "unknown"}. Check browser console for details.`);
|
|
145
143
|
}
|
|
146
144
|
};
|
|
147
145
|
const loadManual = async () => {
|
|
@@ -154,7 +152,7 @@
|
|
|
154
152
|
};
|
|
155
153
|
const refresh = async () => {
|
|
156
154
|
setLoading(true);
|
|
157
|
-
await Promise.all([
|
|
155
|
+
await Promise.all([loadDrivers(), loadManual()]);
|
|
158
156
|
setLoading(false);
|
|
159
157
|
};
|
|
160
158
|
useEffect(() => {
|
|
@@ -179,43 +177,17 @@
|
|
|
179
177
|
setManName("");
|
|
180
178
|
setManUser("");
|
|
181
179
|
setManToken("");
|
|
182
|
-
await loadManual();
|
|
180
|
+
await Promise.all([loadDrivers(), loadManual()]);
|
|
183
181
|
} catch (e) {
|
|
184
182
|
setError(e?.message || "Failed to connect");
|
|
185
183
|
} finally {
|
|
186
184
|
setSaving(false);
|
|
187
185
|
}
|
|
188
186
|
};
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
id: `manual:${ma.id}`,
|
|
194
|
-
type: "manual",
|
|
195
|
-
platform: ma.platform,
|
|
196
|
-
displayName: ma.displayName,
|
|
197
|
-
username: ma.username,
|
|
198
|
-
isActive: ma.isActive,
|
|
199
|
-
capabilities: ["post"]
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
return combined;
|
|
203
|
-
}, [providers, manualAccounts]);
|
|
204
|
-
const grouped = useMemo(() => {
|
|
205
|
-
const g = {};
|
|
206
|
-
for (const p of allProviders) {
|
|
207
|
-
const key = p.type;
|
|
208
|
-
if (!g[key]) g[key] = [];
|
|
209
|
-
g[key].push(p);
|
|
210
|
-
}
|
|
211
|
-
return g;
|
|
212
|
-
}, [allProviders]);
|
|
213
|
-
const typeLabels = {
|
|
214
|
-
postiz: "Postiz",
|
|
215
|
-
gateway: "OpenClaw Channels",
|
|
216
|
-
skill: "Skills",
|
|
217
|
-
manual: "Manual"
|
|
218
|
-
};
|
|
187
|
+
const connectedDrivers = useMemo(() => drivers.filter((d) => d.connected), [drivers]);
|
|
188
|
+
const disconnectedDrivers = useMemo(() => drivers.filter((d) => !d.connected), [drivers]);
|
|
189
|
+
const connectedCount = connectedDrivers.length;
|
|
190
|
+
const totalCount = drivers.length;
|
|
219
191
|
return h(
|
|
220
192
|
"div",
|
|
221
193
|
{ className: "space-y-3" },
|
|
@@ -229,11 +201,11 @@
|
|
|
229
201
|
h(
|
|
230
202
|
"div",
|
|
231
203
|
null,
|
|
232
|
-
h("div", { className: "text-sm font-medium", style: t.text }, "
|
|
204
|
+
h("div", { className: "text-sm font-medium", style: t.text }, "Platform Drivers"),
|
|
233
205
|
h(
|
|
234
206
|
"div",
|
|
235
207
|
{ className: "mt-1 text-xs", style: t.faint },
|
|
236
|
-
`${
|
|
208
|
+
`${connectedCount}/${totalCount} platforms connected`
|
|
237
209
|
)
|
|
238
210
|
),
|
|
239
211
|
h(
|
|
@@ -241,15 +213,15 @@
|
|
|
241
213
|
{ className: "flex flex-wrap gap-2" },
|
|
242
214
|
h(
|
|
243
215
|
"button",
|
|
244
|
-
{ type: "button", onClick: () => void refresh(), style: t.btnGhost, disabled:
|
|
245
|
-
|
|
216
|
+
{ type: "button", onClick: () => void refresh(), style: t.btnGhost, disabled: loading },
|
|
217
|
+
loading ? "Loading\u2026" : "\u21BB Refresh"
|
|
246
218
|
),
|
|
247
219
|
h(
|
|
248
220
|
"button",
|
|
249
221
|
{ type: "button", onClick: () => setShowPostizSetup(!showPostizSetup), style: t.btnGhost },
|
|
250
222
|
postizKey ? "\u2699 Postiz" : "+ Postiz"
|
|
251
223
|
),
|
|
252
|
-
h("button", { type: "button", onClick: () => setShowManual(!showManual), style: t.btnGhost }, "+
|
|
224
|
+
h("button", { type: "button", onClick: () => setShowManual(!showManual), style: t.btnGhost }, "+ Direct token")
|
|
253
225
|
)
|
|
254
226
|
),
|
|
255
227
|
error && h("div", { className: "mt-2 text-xs", style: { color: "rgba(248,113,113,0.95)" } }, error)
|
|
@@ -262,7 +234,7 @@
|
|
|
262
234
|
h(
|
|
263
235
|
"div",
|
|
264
236
|
{ className: "text-xs mb-3", style: t.faint },
|
|
265
|
-
"
|
|
237
|
+
"Postiz manages OAuth connections to social platforms. Get your API key from Postiz Settings \u2192 Developers \u2192 Public API."
|
|
266
238
|
),
|
|
267
239
|
h(
|
|
268
240
|
"div",
|
|
@@ -295,15 +267,15 @@
|
|
|
295
267
|
"div",
|
|
296
268
|
{ className: "mt-3 flex gap-2" },
|
|
297
269
|
h("button", { type: "button", onClick: () => setShowPostizSetup(false), style: t.btnGhost }, "Cancel"),
|
|
298
|
-
h("button", { type: "button", onClick: savePostizConfig, style: t.btnPrimary },
|
|
270
|
+
h("button", { type: "button", onClick: savePostizConfig, style: t.btnPrimary }, "Save & Detect")
|
|
299
271
|
)
|
|
300
272
|
),
|
|
301
|
-
// ---- Manual
|
|
273
|
+
// ---- Manual token form ----
|
|
302
274
|
showManual && h(
|
|
303
275
|
"div",
|
|
304
276
|
{ style: t.card },
|
|
305
|
-
h("div", { className: "text-sm font-medium mb-2", style: t.text }, "Add
|
|
306
|
-
h("div", { className: "text-xs mb-3", style: t.faint }, "For
|
|
277
|
+
h("div", { className: "text-sm font-medium mb-2", style: t.text }, "Add direct API token"),
|
|
278
|
+
h("div", { className: "text-xs mb-3", style: t.faint }, "For platforms where you have your own API credentials. Token is encrypted at rest."),
|
|
307
279
|
h(
|
|
308
280
|
"div",
|
|
309
281
|
{ className: "grid grid-cols-1 gap-2 sm:grid-cols-2" },
|
|
@@ -314,18 +286,14 @@
|
|
|
314
286
|
h(
|
|
315
287
|
"select",
|
|
316
288
|
{ value: manPlatform, onChange: (e) => setManPlatform(e.target.value), style: t.input },
|
|
317
|
-
h("option", { value:
|
|
318
|
-
h("option", { value: "instagram" }, "Instagram"),
|
|
319
|
-
h("option", { value: "linkedin" }, "LinkedIn"),
|
|
320
|
-
h("option", { value: "bluesky" }, "Bluesky"),
|
|
321
|
-
h("option", { value: "mastodon" }, "Mastodon")
|
|
289
|
+
...drivers.map((d) => h("option", { key: d.platform, value: d.platform }, d.label))
|
|
322
290
|
)
|
|
323
291
|
),
|
|
324
292
|
h(
|
|
325
293
|
"div",
|
|
326
294
|
null,
|
|
327
295
|
h("div", { className: "text-xs font-medium mb-1", style: t.faint }, "Display name"),
|
|
328
|
-
h("input", { value: manName, onChange: (e) => setManName(e.target.value), placeholder: "My
|
|
296
|
+
h("input", { value: manName, onChange: (e) => setManName(e.target.value), placeholder: "My account", style: t.input })
|
|
329
297
|
),
|
|
330
298
|
h(
|
|
331
299
|
"div",
|
|
@@ -347,87 +315,168 @@
|
|
|
347
315
|
h("button", { type: "button", onClick: () => void onManualConnect(), style: t.btnPrimary, disabled: saving }, saving ? "Saving\u2026" : "Connect")
|
|
348
316
|
)
|
|
349
317
|
),
|
|
350
|
-
// ----
|
|
351
|
-
|
|
352
|
-
"div",
|
|
353
|
-
{ style: t.card },
|
|
354
|
-
h("div", { className: "py-6 text-center text-sm", style: t.faint }, "Detecting providers\u2026")
|
|
355
|
-
),
|
|
356
|
-
// ---- Provider groups ----
|
|
357
|
-
!loading && allProviders.length === 0 && h(
|
|
318
|
+
// ---- Connected platforms ----
|
|
319
|
+
connectedDrivers.length > 0 && h(
|
|
358
320
|
"div",
|
|
359
321
|
{ style: t.card },
|
|
360
322
|
h(
|
|
361
323
|
"div",
|
|
362
|
-
{ className: "
|
|
363
|
-
h("div", { className: "text-sm", style: t.
|
|
364
|
-
h(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
324
|
+
{ className: "flex items-center gap-2 mb-3" },
|
|
325
|
+
h("div", { className: "text-sm font-medium", style: t.text }, "Connected"),
|
|
326
|
+
h("span", { style: t.badge("rgba(74,222,128,0.7)") }, `${connectedCount}`)
|
|
327
|
+
),
|
|
328
|
+
h(
|
|
329
|
+
"div",
|
|
330
|
+
{ className: "space-y-2" },
|
|
331
|
+
...connectedDrivers.map(
|
|
332
|
+
(d) => h(
|
|
333
|
+
"div",
|
|
334
|
+
{ key: d.platform, style: { ...t.card, padding: "0.75rem", display: "flex", alignItems: "center", gap: "0.75rem" } },
|
|
335
|
+
d.avatar ? h("img", { src: d.avatar, alt: "", style: { width: 36, height: 36, borderRadius: "50%", objectFit: "cover" } }) : h("div", {
|
|
336
|
+
style: {
|
|
337
|
+
width: 36,
|
|
338
|
+
height: 36,
|
|
339
|
+
borderRadius: "50%",
|
|
340
|
+
background: "rgba(255,255,255,0.06)",
|
|
341
|
+
display: "flex",
|
|
342
|
+
alignItems: "center",
|
|
343
|
+
justifyContent: "center",
|
|
344
|
+
fontSize: "1.1rem"
|
|
345
|
+
}
|
|
346
|
+
}, d.icon),
|
|
347
|
+
h(
|
|
348
|
+
"div",
|
|
349
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
350
|
+
h("div", { className: "text-sm font-medium", style: t.text }, d.displayName),
|
|
351
|
+
h(
|
|
352
|
+
"div",
|
|
353
|
+
{ className: "text-xs", style: t.faint },
|
|
354
|
+
[d.username, d.platform].filter(Boolean).join(" \xB7 ")
|
|
355
|
+
)
|
|
356
|
+
),
|
|
357
|
+
h(
|
|
358
|
+
"div",
|
|
359
|
+
{ className: "flex items-center gap-2 shrink-0 flex-wrap" },
|
|
360
|
+
h("span", { style: t.badge(BACKEND_COLORS[d.backend] || BACKEND_COLORS.none) }, BACKEND_LABELS[d.backend] || d.backend),
|
|
361
|
+
d.capabilities.canPost && h("span", { style: t.capPill(true) }, "post"),
|
|
362
|
+
d.capabilities.canSchedule && h("span", { style: t.capPill(true) }, "schedule"),
|
|
363
|
+
d.capabilities.canUploadMedia && h("span", { style: t.capPill(true) }, "media"),
|
|
364
|
+
d.capabilities.maxLength && h("span", { style: t.capPill(false) }, `${d.capabilities.maxLength} chars`),
|
|
365
|
+
h("div", {
|
|
366
|
+
style: {
|
|
367
|
+
width: 8,
|
|
368
|
+
height: 8,
|
|
369
|
+
borderRadius: "50%",
|
|
370
|
+
background: "rgba(74,222,128,0.8)"
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
)
|
|
374
|
+
)
|
|
368
375
|
)
|
|
369
376
|
)
|
|
370
377
|
),
|
|
371
|
-
|
|
372
|
-
|
|
378
|
+
// ---- Disconnected platforms ----
|
|
379
|
+
disconnectedDrivers.length > 0 && h(
|
|
380
|
+
"div",
|
|
381
|
+
{ style: t.card },
|
|
382
|
+
h(
|
|
373
383
|
"div",
|
|
374
|
-
{
|
|
375
|
-
h(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
{ className: "flex items-center gap-2 mb-3" },
|
|
385
|
+
h("div", { className: "text-sm font-medium", style: t.text }, "Available"),
|
|
386
|
+
h("span", { style: t.badge("rgba(100,100,100,0.5)") }, `${disconnectedDrivers.length}`)
|
|
387
|
+
),
|
|
388
|
+
h(
|
|
389
|
+
"div",
|
|
390
|
+
{ className: "text-xs mb-3", style: t.faint },
|
|
391
|
+
"Connect these via Postiz or by adding a direct API token above."
|
|
392
|
+
),
|
|
393
|
+
h(
|
|
394
|
+
"div",
|
|
395
|
+
{ className: "space-y-2" },
|
|
396
|
+
...disconnectedDrivers.map(
|
|
397
|
+
(d) => h(
|
|
398
|
+
"div",
|
|
399
|
+
{ key: d.platform, style: { ...t.card, padding: "0.75rem", display: "flex", alignItems: "center", gap: "0.75rem", opacity: 0.6 } },
|
|
400
|
+
h("div", {
|
|
401
|
+
style: {
|
|
402
|
+
width: 36,
|
|
403
|
+
height: 36,
|
|
404
|
+
borderRadius: "50%",
|
|
405
|
+
background: "rgba(255,255,255,0.04)",
|
|
406
|
+
display: "flex",
|
|
407
|
+
alignItems: "center",
|
|
408
|
+
justifyContent: "center",
|
|
409
|
+
fontSize: "1.1rem"
|
|
410
|
+
}
|
|
411
|
+
}, d.icon),
|
|
412
|
+
h(
|
|
413
|
+
"div",
|
|
414
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
415
|
+
h("div", { className: "text-sm font-medium", style: t.text }, d.label),
|
|
416
|
+
h("div", { className: "text-xs", style: t.faint }, "Not connected")
|
|
417
|
+
),
|
|
418
|
+
h(
|
|
386
419
|
"div",
|
|
387
|
-
{
|
|
388
|
-
|
|
389
|
-
|
|
420
|
+
{ className: "flex items-center gap-2 shrink-0" },
|
|
421
|
+
d.capabilities.maxLength && h("span", { style: t.capPill(false) }, `${d.capabilities.maxLength} chars`),
|
|
422
|
+
h("div", {
|
|
390
423
|
style: {
|
|
391
|
-
width:
|
|
392
|
-
height:
|
|
424
|
+
width: 8,
|
|
425
|
+
height: 8,
|
|
393
426
|
borderRadius: "50%",
|
|
394
|
-
background: "rgba(
|
|
395
|
-
display: "flex",
|
|
396
|
-
alignItems: "center",
|
|
397
|
-
justifyContent: "center",
|
|
398
|
-
fontSize: "1rem"
|
|
427
|
+
background: "rgba(100,100,100,0.4)"
|
|
399
428
|
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
429
|
+
})
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
),
|
|
435
|
+
// ---- Manual accounts (if any exist beyond drivers) ----
|
|
436
|
+
manualAccounts.length > 0 && h(
|
|
437
|
+
"div",
|
|
438
|
+
{ style: t.card },
|
|
439
|
+
h(
|
|
440
|
+
"div",
|
|
441
|
+
{ className: "flex items-center gap-2 mb-3" },
|
|
442
|
+
h("div", { className: "text-sm font-medium", style: t.text }, "Stored tokens"),
|
|
443
|
+
h("span", { style: t.badge("rgba(251,191,36,0.7)") }, `${manualAccounts.length}`)
|
|
444
|
+
),
|
|
445
|
+
h("div", { className: "text-xs mb-3", style: t.faint }, "Tokens stored locally, encrypted at rest. These feed into the direct backend for their platform driver."),
|
|
446
|
+
h(
|
|
447
|
+
"div",
|
|
448
|
+
{ className: "space-y-2" },
|
|
449
|
+
...manualAccounts.map(
|
|
450
|
+
(a) => h(
|
|
451
|
+
"div",
|
|
452
|
+
{ key: a.id, style: { ...t.card, padding: "0.75rem", display: "flex", alignItems: "center", gap: "0.75rem" } },
|
|
453
|
+
h(
|
|
454
|
+
"div",
|
|
455
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
456
|
+
h("div", { className: "text-sm", style: t.text }, a.displayName),
|
|
413
457
|
h(
|
|
414
458
|
"div",
|
|
415
|
-
{ className: "
|
|
416
|
-
|
|
417
|
-
p.capabilities?.includes("post") && h("span", { className: "text-xs", style: t.faint }, "\u{1F4E4}"),
|
|
418
|
-
h("div", {
|
|
419
|
-
style: {
|
|
420
|
-
width: 8,
|
|
421
|
-
height: 8,
|
|
422
|
-
borderRadius: "50%",
|
|
423
|
-
background: p.isActive ? "rgba(74,222,128,0.8)" : "rgba(248,113,113,0.6)"
|
|
424
|
-
}
|
|
425
|
-
})
|
|
459
|
+
{ className: "text-xs", style: t.faint },
|
|
460
|
+
[a.platform, a.username].filter(Boolean).join(" \xB7 ")
|
|
426
461
|
)
|
|
427
|
-
)
|
|
462
|
+
),
|
|
463
|
+
h("div", {
|
|
464
|
+
style: {
|
|
465
|
+
width: 8,
|
|
466
|
+
height: 8,
|
|
467
|
+
borderRadius: "50%",
|
|
468
|
+
background: a.isActive ? "rgba(74,222,128,0.8)" : "rgba(248,113,113,0.6)"
|
|
469
|
+
}
|
|
470
|
+
})
|
|
428
471
|
)
|
|
429
472
|
)
|
|
430
473
|
)
|
|
474
|
+
),
|
|
475
|
+
// ---- Loading ----
|
|
476
|
+
loading && drivers.length === 0 && h(
|
|
477
|
+
"div",
|
|
478
|
+
{ style: t.card },
|
|
479
|
+
h("div", { className: "py-6 text-center text-sm", style: t.faint }, "Detecting platform drivers\u2026")
|
|
431
480
|
)
|
|
432
481
|
);
|
|
433
482
|
}
|