@growthub/cli 0.3.59 → 0.3.60

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 (41) hide show
  1. package/assets/worker-kits/growthub-zernio-social-v1/.env.example +5 -0
  2. package/assets/worker-kits/growthub-zernio-social-v1/QUICKSTART.md +36 -4
  3. package/assets/worker-kits/growthub-zernio-social-v1/bundles/growthub-zernio-social-v1.json +30 -1
  4. package/assets/worker-kits/growthub-zernio-social-v1/docs/growthub-agentic-social-platform-ui-shell.md +134 -0
  5. package/assets/worker-kits/growthub-zernio-social-v1/docs/local-adapters.md +2 -2
  6. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/README.md +5 -8
  7. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/kit-standard.md +1 -1
  8. package/assets/worker-kits/growthub-zernio-social-v1/kit.json +33 -1
  9. package/assets/worker-kits/growthub-zernio-social-v1/skills.md +1 -1
  10. package/assets/worker-kits/growthub-zernio-social-v1/studio/.env.example +3 -0
  11. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/assets/index-DTmBMuXr.js +78 -0
  12. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/assets/index-gHr-nTMF.css +1 -0
  13. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/index.html +14 -0
  14. package/assets/worker-kits/growthub-zernio-social-v1/studio/index.html +13 -0
  15. package/assets/worker-kits/growthub-zernio-social-v1/studio/package-lock.json +1677 -0
  16. package/assets/worker-kits/growthub-zernio-social-v1/studio/package.json +20 -0
  17. package/assets/worker-kits/growthub-zernio-social-v1/studio/serve.mjs +60 -0
  18. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/App.jsx +130 -0
  19. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/api.js +146 -0
  20. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/app.css +558 -0
  21. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/lib/rules.js +64 -0
  22. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/lib/templates.js +207 -0
  23. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/main.jsx +10 -0
  24. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Accounts.jsx +57 -0
  25. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Agent.jsx +167 -0
  26. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Analytics.jsx +164 -0
  27. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/ApiKeys.jsx +143 -0
  28. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Automations.jsx +122 -0
  29. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/CommentRules.jsx +592 -0
  30. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Compose.jsx +185 -0
  31. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Dashboard.jsx +87 -0
  32. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Inbox.jsx +144 -0
  33. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Queues.jsx +167 -0
  34. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Scheduled.jsx +85 -0
  35. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Sequences.jsx +160 -0
  36. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Templates.jsx +275 -0
  37. package/assets/worker-kits/growthub-zernio-social-v1/studio/vite.config.js +7 -0
  38. package/assets/worker-kits/growthub-zernio-social-v1/workers/zernio-social-operator/CLAUDE.md +3 -3
  39. package/dist/index.js +1183 -592
  40. package/package.json +1 -1
  41. package/assets/worker-kits/growthub-zernio-social-v1/docs/postiz-ui-shell-integration.md +0 -166
@@ -0,0 +1,558 @@
1
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
2
+
3
+ :root {
4
+ --bg: #0f0f11;
5
+ --surface: #18181b;
6
+ --border: #27272a;
7
+ --hover: #27272a;
8
+ --active: #3f3f46;
9
+ --text: #e4e4e7;
10
+ --muted: #71717a;
11
+ --dim: #a1a1aa;
12
+ --accent: #7c3aed;
13
+ --accent2: #6d28d9;
14
+ --accentl: #c4b5fd;
15
+ --accentb: #4c1d95;
16
+ --green: #22c55e;
17
+ --greenb: #14532d;
18
+ --greenl: #86efac;
19
+ --red: #ef4444;
20
+ --redb: #450a0a;
21
+ --redl: #fca5a5;
22
+ --blue: #3b82f6;
23
+ --blueb: #1e3a5f;
24
+ --bluel: #93c5fd;
25
+ --radius: 10px;
26
+ --radius-sm: 6px;
27
+ }
28
+
29
+ body {
30
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
31
+ background: var(--bg);
32
+ color: var(--text);
33
+ min-height: 100vh;
34
+ font-size: 14px;
35
+ }
36
+
37
+ /* ── Layout ──────────────────────────────────────────────────────────────── */
38
+ .layout { display: flex; height: 100vh; overflow: hidden; }
39
+
40
+ .sidebar {
41
+ width: 220px;
42
+ background: var(--surface);
43
+ border-right: 1px solid var(--border);
44
+ display: flex;
45
+ flex-direction: column;
46
+ padding: 20px 10px;
47
+ gap: 2px;
48
+ flex-shrink: 0;
49
+ overflow-y: auto;
50
+ }
51
+
52
+ .logo {
53
+ font-size: 18px;
54
+ font-weight: 700;
55
+ color: var(--accentl);
56
+ padding: 6px 12px 18px;
57
+ letter-spacing: -0.5px;
58
+ }
59
+
60
+ .nav-section {
61
+ font-size: 10px;
62
+ font-weight: 600;
63
+ color: var(--muted);
64
+ text-transform: uppercase;
65
+ letter-spacing: 0.08em;
66
+ padding: 10px 12px 4px;
67
+ }
68
+
69
+ .nav-item {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 9px;
73
+ padding: 8px 12px;
74
+ border-radius: var(--radius-sm);
75
+ cursor: pointer;
76
+ font-size: 13px;
77
+ color: var(--dim);
78
+ background: none;
79
+ border: none;
80
+ width: 100%;
81
+ text-align: left;
82
+ transition: background 0.1s, color 0.1s;
83
+ }
84
+ .nav-item:hover { background: var(--hover); color: var(--text); }
85
+ .nav-item.active { background: var(--active); color: #fff; }
86
+ .nav-icon { font-size: 15px; width: 20px; text-align: center; flex-shrink: 0; }
87
+
88
+ .sidebar-bottom {
89
+ margin-top: auto;
90
+ border-top: 1px solid var(--border);
91
+ padding-top: 14px;
92
+ }
93
+ .account-badge {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 8px;
97
+ padding: 8px 12px;
98
+ font-size: 12px;
99
+ color: var(--muted);
100
+ }
101
+ .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); flex-shrink: 0; }
102
+
103
+ /* ── Main area ───────────────────────────────────────────────────────────── */
104
+ .main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
105
+
106
+ .topbar {
107
+ background: var(--surface);
108
+ border-bottom: 1px solid var(--border);
109
+ padding: 0 24px;
110
+ height: 54px;
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: space-between;
114
+ flex-shrink: 0;
115
+ }
116
+ .topbar-title { font-size: 14px; font-weight: 600; }
117
+
118
+ .content { flex: 1; overflow-y: auto; padding: 24px; }
119
+
120
+ /* ── Buttons ─────────────────────────────────────────────────────────────── */
121
+ .btn {
122
+ padding: 7px 14px;
123
+ border-radius: var(--radius-sm);
124
+ border: none;
125
+ cursor: pointer;
126
+ font-size: 13px;
127
+ font-weight: 500;
128
+ transition: background 0.1s, opacity 0.1s;
129
+ white-space: nowrap;
130
+ }
131
+ .btn:disabled { opacity: 0.45; cursor: not-allowed; }
132
+ .btn-primary { background: var(--accent); color: #fff; }
133
+ .btn-primary:hover:not(:disabled) { background: var(--accent2); }
134
+ .btn-secondary { background: var(--active); color: var(--text); }
135
+ .btn-secondary:hover:not(:disabled) { background: #52525b; }
136
+ .btn-danger { background: var(--redb); color: var(--redl); border: 1px solid #7f1d1d; }
137
+ .btn-danger:hover:not(:disabled) { background: #7f1d1d; }
138
+ .btn-ghost { background: transparent; color: var(--dim); border: 1px solid var(--border); }
139
+ .btn-ghost:hover:not(:disabled) { background: var(--hover); color: var(--text); }
140
+ .btn-sm { padding: 4px 10px; font-size: 12px; }
141
+ .btn-xs { padding: 2px 8px; font-size: 11px; border-radius: 4px; }
142
+
143
+ /* ── Cards ───────────────────────────────────────────────────────────────── */
144
+ .card {
145
+ background: var(--surface);
146
+ border: 1px solid var(--border);
147
+ border-radius: var(--radius);
148
+ padding: 20px;
149
+ }
150
+ .card + .card { margin-top: 12px; }
151
+
152
+ /* ── Stat grid ───────────────────────────────────────────────────────────── */
153
+ .stats-grid {
154
+ display: grid;
155
+ grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
156
+ gap: 14px;
157
+ margin-bottom: 24px;
158
+ }
159
+ .stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; }
160
+ .stat-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; }
161
+ .stat-value { font-size: 26px; font-weight: 700; color: #fff; }
162
+ .stat-sub { font-size: 11px; color: var(--muted); margin-top: 3px; }
163
+
164
+ /* ── Section title ───────────────────────────────────────────────────────── */
165
+ .section-title {
166
+ font-size: 11px;
167
+ font-weight: 600;
168
+ color: var(--dim);
169
+ text-transform: uppercase;
170
+ letter-spacing: 0.06em;
171
+ margin-bottom: 12px;
172
+ }
173
+
174
+ /* ── Account cards ───────────────────────────────────────────────────────── */
175
+ .account-card {
176
+ background: var(--surface);
177
+ border: 1px solid var(--border);
178
+ border-radius: var(--radius);
179
+ padding: 14px 18px;
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 12px;
183
+ }
184
+ .account-card + .account-card { margin-top: 8px; }
185
+ .platform-icon {
186
+ width: 34px;
187
+ height: 34px;
188
+ border-radius: 8px;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ font-size: 15px;
193
+ font-weight: 700;
194
+ flex-shrink: 0;
195
+ }
196
+ .acc-info { flex: 1; min-width: 0; }
197
+ .acc-name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
198
+ .acc-handle { font-size: 11px; color: var(--muted); margin-top: 2px; }
199
+
200
+ /* ── Badges ──────────────────────────────────────────────────────────────── */
201
+ .badge { display: inline-flex; align-items: center; font-size: 11px; padding: 2px 9px; border-radius: 20px; font-weight: 500; }
202
+ .badge-green { background: var(--greenb); color: var(--greenl); }
203
+ .badge-blue { background: var(--blueb); color: var(--bluel); }
204
+ .badge-red { background: var(--redb); color: var(--redl); }
205
+ .badge-purple { background: var(--accentb); color: var(--accentl); }
206
+ .badge-neutral { background: var(--active); color: var(--dim); }
207
+
208
+ /* ── Form elements ───────────────────────────────────────────────────────── */
209
+ .field { margin-bottom: 14px; }
210
+ .field label { display: block; font-size: 11px; font-weight: 600; color: var(--dim); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; }
211
+ .input, .textarea, .select {
212
+ width: 100%;
213
+ background: #09090b;
214
+ border: 1px solid var(--border);
215
+ border-radius: var(--radius-sm);
216
+ padding: 9px 12px;
217
+ color: var(--text);
218
+ font-size: 13px;
219
+ font-family: inherit;
220
+ outline: none;
221
+ transition: border-color 0.1s;
222
+ }
223
+ .input:focus, .textarea:focus, .select:focus { border-color: var(--accent); }
224
+ .textarea { resize: vertical; min-height: 110px; line-height: 1.5; }
225
+ .select { cursor: pointer; }
226
+ .select option { background: #18181b; }
227
+ .char-count { font-size: 11px; color: var(--muted); text-align: right; margin-top: 3px; }
228
+ .char-over { color: var(--redl); }
229
+
230
+ /* ── Platform toggles ────────────────────────────────────────────────────── */
231
+ .plat-row { display: flex; flex-wrap: wrap; gap: 8px; }
232
+ .plat-toggle {
233
+ padding: 5px 13px;
234
+ border-radius: 20px;
235
+ border: 1px solid var(--border);
236
+ background: #09090b;
237
+ color: var(--muted);
238
+ cursor: pointer;
239
+ font-size: 12px;
240
+ font-weight: 500;
241
+ transition: all 0.1s;
242
+ user-select: none;
243
+ }
244
+ .plat-toggle:hover { border-color: var(--accent); color: var(--accentl); }
245
+ .plat-toggle.selected { background: var(--accentb); border-color: var(--accent); color: var(--accentl); }
246
+
247
+ /* ── Post cards ──────────────────────────────────────────────────────────── */
248
+ .post-card {
249
+ background: var(--surface);
250
+ border: 1px solid var(--border);
251
+ border-radius: var(--radius);
252
+ padding: 14px 18px;
253
+ margin-bottom: 8px;
254
+ }
255
+ .post-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
256
+ .post-content { font-size: 13px; color: var(--dim); line-height: 1.55; }
257
+ .post-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }
258
+
259
+ /* ── Queue slots ─────────────────────────────────────────────────────────── */
260
+ .slot-row {
261
+ display: grid;
262
+ grid-template-columns: 110px 100px 1fr auto;
263
+ gap: 8px;
264
+ align-items: center;
265
+ margin-bottom: 8px;
266
+ }
267
+
268
+ /* ── Table ───────────────────────────────────────────────────────────────── */
269
+ .table-wrap { overflow-x: auto; }
270
+ table { width: 100%; border-collapse: collapse; font-size: 13px; }
271
+ th { text-align: left; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; padding: 10px 14px; border-bottom: 1px solid var(--border); }
272
+ td { padding: 11px 14px; border-bottom: 1px solid var(--border); color: var(--dim); vertical-align: top; }
273
+ tr:hover td { background: rgba(255,255,255,0.02); }
274
+ tr:last-child td { border-bottom: none; }
275
+
276
+ /* ── Inbox ───────────────────────────────────────────────────────────────── */
277
+ .inbox-layout { display: grid; grid-template-columns: 300px 1fr; gap: 0; height: calc(100vh - 100px); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
278
+ .inbox-list { border-right: 1px solid var(--border); overflow-y: auto; background: var(--surface); }
279
+ .inbox-item { padding: 14px 16px; border-bottom: 1px solid var(--border); cursor: pointer; transition: background 0.1s; }
280
+ .inbox-item:hover { background: var(--hover); }
281
+ .inbox-item.selected { background: var(--active); }
282
+ .inbox-platform { font-size: 11px; color: var(--muted); margin-bottom: 3px; }
283
+ .inbox-preview { font-size: 13px; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
284
+ .inbox-thread { display: flex; flex-direction: column; height: 100%; background: #0f0f11; }
285
+ .thread-msgs { flex: 1; overflow-y: auto; padding: 18px; display: flex; flex-direction: column; gap: 12px; }
286
+ .msg { padding: 10px 14px; border-radius: 10px; max-width: 70%; font-size: 13px; line-height: 1.5; }
287
+ .msg-in { background: var(--surface); border: 1px solid var(--border); align-self: flex-start; }
288
+ .msg-out { background: var(--accentb); border: 1px solid var(--accent); align-self: flex-end; color: var(--accentl); }
289
+ .msg-meta { font-size: 11px; color: var(--muted); margin-bottom: 3px; }
290
+ .thread-reply { padding: 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; background: var(--surface); }
291
+ .thread-reply textarea { flex: 1; background: #09090b; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 12px; color: var(--text); font-family: inherit; font-size: 13px; resize: none; height: 72px; outline: none; }
292
+ .thread-reply textarea:focus { border-color: var(--accent); }
293
+
294
+ /* ── Agent view ──────────────────────────────────────────────────────────── */
295
+ .agent-layout { display: grid; grid-template-columns: 300px 1fr; gap: 16px; }
296
+ .cmd-ref { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; }
297
+ .cmd-item { margin-bottom: 14px; }
298
+ .cmd-name { font-size: 12px; font-family: 'SF Mono', 'Fira Code', monospace; color: var(--accentl); font-weight: 600; }
299
+ .cmd-desc { font-size: 12px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
300
+ .manifest-panel { display: flex; flex-direction: column; gap: 12px; }
301
+ .manifest-status { display: flex; flex-direction: column; gap: 6px; max-height: 280px; overflow-y: auto; }
302
+ .m-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); font-size: 12px; }
303
+ .m-row.ok { border-color: #166534; background: var(--greenb); color: var(--greenl); }
304
+ .m-row.err { border-color: #7f1d1d; background: var(--redb); color: var(--redl); }
305
+ .m-row.busy { border-color: var(--accent); background: var(--accentb); color: var(--accentl); }
306
+ .context-box { background: #09090b; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 11px; color: var(--dim); white-space: pre-wrap; word-break: break-all; }
307
+
308
+ /* ── Result banner ───────────────────────────────────────────────────────── */
309
+ .banner { padding: 10px 14px; border-radius: var(--radius-sm); font-size: 13px; margin-bottom: 14px; }
310
+ .banner-ok { background: var(--greenb); color: var(--greenl); border: 1px solid #166534; }
311
+ .banner-err { background: var(--redb); color: var(--redl); border: 1px solid #7f1d1d; }
312
+
313
+ /* ── Toast ───────────────────────────────────────────────────────────────── */
314
+ .toast {
315
+ position: fixed;
316
+ bottom: 24px;
317
+ right: 24px;
318
+ border-radius: var(--radius-sm);
319
+ padding: 11px 16px;
320
+ font-size: 13px;
321
+ z-index: 9999;
322
+ max-width: 340px;
323
+ pointer-events: none;
324
+ animation: fadeIn 0.15s ease;
325
+ }
326
+ .toast-ok { background: var(--greenb); color: var(--greenl); border: 1px solid #166534; }
327
+ .toast-err { background: var(--redb); color: var(--redl); border: 1px solid #7f1d1d; }
328
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
329
+
330
+ /* ── Spinner ─────────────────────────────────────────────────────────────── */
331
+ .spinner {
332
+ display: inline-block;
333
+ width: 15px;
334
+ height: 15px;
335
+ border: 2px solid var(--active);
336
+ border-top-color: var(--accent);
337
+ border-radius: 50%;
338
+ animation: spin 0.55s linear infinite;
339
+ flex-shrink: 0;
340
+ }
341
+ @keyframes spin { to { transform: rotate(360deg); } }
342
+ .loading-row { display: flex; align-items: center; gap: 9px; color: var(--muted); padding: 20px 0; }
343
+
344
+ /* ── Empty state ─────────────────────────────────────────────────────────── */
345
+ .empty { text-align: center; padding: 50px 20px; color: var(--muted); }
346
+ .empty-icon { font-size: 36px; margin-bottom: 10px; }
347
+ .empty-msg { font-size: 13px; }
348
+
349
+ /* ── Divider ─────────────────────────────────────────────────────────────── */
350
+ .divider { border: none; border-top: 1px solid var(--border); margin: 20px 0; }
351
+
352
+ /* ── Flex utils ──────────────────────────────────────────────────────────── */
353
+ .row { display: flex; gap: 10px; align-items: center; }
354
+ .row-end { display: flex; gap: 10px; align-items: center; justify-content: flex-end; }
355
+ .col { display: flex; flex-direction: column; gap: 10px; }
356
+ .flex1 { flex: 1; }
357
+ .mb8 { margin-bottom: 8px; }
358
+ .mb16 { margin-bottom: 16px; }
359
+ .mb24 { margin-bottom: 24px; }
360
+ .mt8 { margin-top: 8px; }
361
+ .mt12 { margin-top: 12px; }
362
+
363
+ /* ── Scrollbar ───────────────────────────────────────────────────────────── */
364
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
365
+ ::-webkit-scrollbar-track { background: transparent; }
366
+ ::-webkit-scrollbar-thumb { background: var(--active); border-radius: 3px; }
367
+
368
+ /* ── Template cards ──────────────────────────────────────────────────────── */
369
+ .tpl-grid {
370
+ display: grid;
371
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
372
+ gap: 14px;
373
+ }
374
+ .tpl-card {
375
+ background: var(--surface);
376
+ border: 1px solid var(--border);
377
+ border-radius: var(--radius);
378
+ padding: 18px;
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: 10px;
382
+ position: relative;
383
+ }
384
+ .tpl-card:hover { border-color: #3f3f46; }
385
+ .tpl-card.selected { border-color: var(--accent); }
386
+ .tpl-type-row { display: flex; align-items: center; gap: 8px; }
387
+ .tpl-type-icon { font-size: 18px; }
388
+ .tpl-name { font-size: 14px; font-weight: 600; flex: 1; }
389
+ .tpl-body {
390
+ font-size: 12px;
391
+ color: var(--muted);
392
+ line-height: 1.55;
393
+ white-space: pre-wrap;
394
+ word-break: break-word;
395
+ max-height: 72px;
396
+ overflow: hidden;
397
+ position: relative;
398
+ }
399
+ .tpl-body::after {
400
+ content: '';
401
+ position: absolute;
402
+ bottom: 0; left: 0; right: 0;
403
+ height: 24px;
404
+ background: linear-gradient(transparent, var(--surface));
405
+ }
406
+ .tpl-footer { display: flex; gap: 6px; margin-top: auto; }
407
+ .tpl-var { display: inline-block; padding: 2px 8px; border-radius: 4px; background: var(--accentb); color: var(--accentl); font-size: 11px; font-family: 'SF Mono', 'Fira Code', monospace; cursor: pointer; user-select: none; }
408
+ .tpl-var:hover { background: var(--accent); color: #fff; }
409
+ .preview-box {
410
+ background: #09090b;
411
+ border: 1px solid var(--border);
412
+ border-radius: var(--radius-sm);
413
+ padding: 12px;
414
+ font-size: 13px;
415
+ color: var(--dim);
416
+ line-height: 1.6;
417
+ white-space: pre-wrap;
418
+ word-break: break-word;
419
+ min-height: 80px;
420
+ }
421
+
422
+ /* ── Comment Rules layout ────────────────────────────────────────────────── */
423
+ .cr-layout {
424
+ display: grid;
425
+ grid-template-columns: 300px 1fr;
426
+ gap: 0;
427
+ height: calc(100vh - 100px);
428
+ border: 1px solid var(--border);
429
+ border-radius: var(--radius);
430
+ overflow: hidden;
431
+ }
432
+ .cr-posts {
433
+ border-right: 1px solid var(--border);
434
+ background: var(--surface);
435
+ display: flex;
436
+ flex-direction: column;
437
+ overflow: hidden;
438
+ }
439
+ .cr-posts-header {
440
+ padding: 14px 16px;
441
+ border-bottom: 1px solid var(--border);
442
+ flex-shrink: 0;
443
+ }
444
+ .cr-posts-list { flex: 1; overflow-y: auto; }
445
+ .cr-post-item {
446
+ padding: 12px 16px;
447
+ border-bottom: 1px solid var(--border);
448
+ cursor: pointer;
449
+ transition: background 0.1s;
450
+ }
451
+ .cr-post-item:hover { background: var(--hover); }
452
+ .cr-post-item.selected { background: var(--active); border-left: 3px solid var(--accent); }
453
+ .cr-post-preview { font-size: 12px; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; }
454
+ .cr-post-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
455
+ .cr-post-plat { font-size: 10px; color: var(--muted); }
456
+
457
+ .cr-rules {
458
+ display: flex;
459
+ flex-direction: column;
460
+ background: #0f0f11;
461
+ overflow: hidden;
462
+ }
463
+ .cr-rules-header {
464
+ padding: 14px 20px;
465
+ border-bottom: 1px solid var(--border);
466
+ background: var(--surface);
467
+ display: flex;
468
+ align-items: center;
469
+ justify-content: space-between;
470
+ flex-shrink: 0;
471
+ }
472
+ .cr-rules-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
473
+
474
+ /* ── Rule card ───────────────────────────────────────────────────────────── */
475
+ .rule-card {
476
+ background: var(--surface);
477
+ border: 1px solid var(--border);
478
+ border-radius: var(--radius);
479
+ padding: 16px 18px;
480
+ margin-bottom: 10px;
481
+ }
482
+ .rule-card.active { border-left: 3px solid var(--green); }
483
+ .rule-card.paused { border-left: 3px solid var(--muted); }
484
+ .rule-card.active:hover { border-color: var(--green); }
485
+ .rule-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
486
+ .rule-keyword {
487
+ font-family: 'SF Mono', 'Fira Code', monospace;
488
+ font-size: 14px;
489
+ font-weight: 700;
490
+ color: var(--accentl);
491
+ background: var(--accentb);
492
+ padding: 3px 10px;
493
+ border-radius: 6px;
494
+ }
495
+ .rule-actions-row { display: flex; flex-direction: column; gap: 6px; }
496
+ .rule-action-line { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; color: var(--dim); }
497
+ .rule-action-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
498
+ .rule-action-body { line-height: 1.5; }
499
+ .rule-footer { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
500
+
501
+ /* ── Rule form ───────────────────────────────────────────────────────────── */
502
+ .rule-form {
503
+ background: var(--surface);
504
+ border: 1px solid var(--accent);
505
+ border-radius: var(--radius);
506
+ padding: 20px;
507
+ margin-bottom: 12px;
508
+ }
509
+
510
+ /* ── Sequence cards ──────────────────────────────────────────────────────── */
511
+ .seq-card {
512
+ background: var(--surface);
513
+ border: 1px solid var(--border);
514
+ border-radius: var(--radius);
515
+ padding: 18px 20px;
516
+ margin-bottom: 10px;
517
+ }
518
+ .seq-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
519
+ .seq-meta { font-size: 12px; color: var(--muted); display: flex; gap: 14px; margin-bottom: 10px; }
520
+ .seq-steps { display: flex; flex-direction: column; gap: 6px; }
521
+ .seq-step { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--dim); padding: 6px 10px; background: #09090b; border-radius: var(--radius-sm); }
522
+ .seq-step-num { font-size: 11px; font-weight: 700; color: var(--accentl); width: 18px; text-align: center; flex-shrink: 0; }
523
+
524
+ /* ── Filter bar ──────────────────────────────────────────────────────────── */
525
+ .filter-bar { display: flex; gap: 6px; margin-bottom: 18px; flex-wrap: wrap; }
526
+ .filter-btn { padding: 5px 14px; border-radius: 20px; border: 1px solid var(--border); background: transparent; color: var(--muted); cursor: pointer; font-size: 12px; font-weight: 500; transition: all 0.1s; }
527
+ .filter-btn:hover { border-color: var(--accent); color: var(--accentl); }
528
+ .filter-btn.active { background: var(--accentb); border-color: var(--accent); color: var(--accentl); }
529
+
530
+ /* ── Toggle switch ───────────────────────────────────────────────────────── */
531
+ .toggle-wrap { display: flex; align-items: center; gap: 8px; cursor: pointer; }
532
+ .toggle {
533
+ position: relative;
534
+ width: 36px;
535
+ height: 20px;
536
+ background: var(--active);
537
+ border-radius: 10px;
538
+ transition: background 0.2s;
539
+ flex-shrink: 0;
540
+ }
541
+ .toggle.on { background: var(--accent); }
542
+ .toggle::after {
543
+ content: '';
544
+ position: absolute;
545
+ top: 3px;
546
+ left: 3px;
547
+ width: 14px;
548
+ height: 14px;
549
+ border-radius: 50%;
550
+ background: #fff;
551
+ transition: transform 0.2s;
552
+ }
553
+ .toggle.on::after { transform: translateX(16px); }
554
+ .toggle-label { font-size: 12px; color: var(--dim); }
555
+
556
+ /* ── Two-body template (both type) ───────────────────────────────────────── */
557
+ .both-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
558
+ @media (max-width: 900px) { .both-fields { grid-template-columns: 1fr; } }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Comment Automation Rules — localStorage store
3
+ *
4
+ * /api/v1/automations is not yet live on this plan.
5
+ * Rules are stored locally and will sync to the API when the endpoint
6
+ * becomes available. Each rule carries a `syncStatus` field:
7
+ * 'local' — stored only in localStorage
8
+ * 'synced' — confirmed on Zernio API (has a real automationId)
9
+ * 'error' — last sync attempt failed
10
+ */
11
+
12
+ const STORAGE_KEY = 'zernio_comment_rules_v1';
13
+
14
+ function read() {
15
+ try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); } catch { return []; }
16
+ }
17
+ function write(rules) {
18
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(rules));
19
+ }
20
+
21
+ export function getRules() { return read(); }
22
+
23
+ export function getRulesForPost(postId) {
24
+ return read().filter(r => r.trigger?.postId === postId);
25
+ }
26
+
27
+ export function saveRule(rule) {
28
+ const all = read();
29
+ if (rule.id) {
30
+ const idx = all.findIndex(r => r.id === rule.id);
31
+ const updated = { ...rule, updatedAt: new Date().toISOString() };
32
+ if (idx >= 0) all[idx] = updated; else all.push(updated);
33
+ } else {
34
+ all.push({ ...rule, id: `cr_${Date.now()}`, syncStatus: 'local', createdAt: new Date().toISOString() });
35
+ }
36
+ write(all);
37
+ return getRulesForPost(rule.trigger?.postId);
38
+ }
39
+
40
+ export function deleteRule(id) {
41
+ write(read().filter(r => r.id !== id));
42
+ }
43
+
44
+ export function updateRuleStatus(id, status) {
45
+ const all = read();
46
+ const idx = all.findIndex(r => r.id === id);
47
+ if (idx >= 0) all[idx] = { ...all[idx], status, updatedAt: new Date().toISOString() };
48
+ write(all);
49
+ }
50
+
51
+ export function markSynced(id, automationId) {
52
+ const all = read();
53
+ const idx = all.findIndex(r => r.id === id);
54
+ if (idx >= 0) all[idx] = { ...all[idx], syncStatus: 'synced', automationId };
55
+ write(all);
56
+ }
57
+
58
+ export function exportRules() {
59
+ const blob = new Blob([JSON.stringify(read(), null, 2)], { type: 'application/json' });
60
+ const url = URL.createObjectURL(blob);
61
+ const a = document.createElement('a');
62
+ a.href = url; a.download = `zernio-rules-${new Date().toISOString().slice(0,10)}.json`;
63
+ a.click(); URL.revokeObjectURL(url);
64
+ }