@jiggai/kitchen-plugin-marketing 0.4.0 → 0.4.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.
@@ -274,137 +274,249 @@
274
274
  return h(
275
275
  "div",
276
276
  { className: "space-y-3" },
277
- // ---- Composer ----
277
+ // ---- Composer (two-column: compose left, preview right) ----
278
278
  h(
279
279
  "div",
280
280
  { style: t.card },
281
281
  h("div", { className: "text-sm font-medium mb-3", style: t.text }, "Compose"),
282
282
  h(
283
283
  "div",
284
- { className: "space-y-3" },
285
- h("textarea", {
286
- value: content,
287
- onChange: (e) => setContent(e.target.value),
288
- placeholder: "Write your post\u2026",
289
- rows: 5,
290
- style: { ...t.input, resize: "vertical", minHeight: "110px", fontFamily: "inherit" }
291
- }),
292
- // Character count
293
- charLimit && content.length > 0 && h(
294
- "div",
295
- { style: t.charWarn(content.length / charLimit * 100) },
296
- `${content.length} / ${charLimit} characters`,
297
- content.length > charLimit && " \u26A0 over limit"
298
- ),
299
- !charLimit && content.length > 0 && h("div", { className: "text-xs", style: t.faint }, `${content.length} chars`),
300
- // Platform selector — connected
284
+ { style: { display: "flex", gap: "1rem" } },
285
+ // LEFT — compose pane
301
286
  h(
302
287
  "div",
303
- null,
304
- h("div", { className: "text-xs font-medium mb-2", style: t.faint }, "Publish to"),
305
- connectedDrivers.length > 0 ? h(
288
+ { style: { flex: 1, minWidth: 0 }, className: "space-y-3" },
289
+ h("textarea", {
290
+ value: content,
291
+ onChange: (e) => setContent(e.target.value),
292
+ placeholder: "Write your post\u2026",
293
+ rows: 5,
294
+ style: { ...t.input, resize: "vertical", minHeight: "160px", fontFamily: "inherit" }
295
+ }),
296
+ // Character count
297
+ charLimit && content.length > 0 && h(
298
+ "div",
299
+ { style: t.charWarn(content.length / charLimit * 100) },
300
+ `${content.length} / ${charLimit} characters`,
301
+ content.length > charLimit && " \u26A0 over limit"
302
+ ),
303
+ !charLimit && content.length > 0 && h("div", { className: "text-xs", style: t.faint }, `${content.length} chars`),
304
+ // Platform selector — connected
305
+ h(
306
306
  "div",
307
- { className: "flex flex-wrap gap-2" },
308
- ...connectedDrivers.map(
309
- (d) => h(
310
- "span",
311
- {
307
+ null,
308
+ h("div", { className: "text-xs font-medium mb-2", style: t.faint }, "Publish to"),
309
+ connectedDrivers.length > 0 ? h(
310
+ "div",
311
+ { className: "flex flex-wrap gap-2" },
312
+ ...connectedDrivers.map(
313
+ (d) => h(
314
+ "span",
315
+ {
316
+ key: d.platform,
317
+ onClick: () => togglePlatform(d.platform),
318
+ style: t.pill(selectedPlatforms.includes(d.platform), true),
319
+ role: "button",
320
+ tabIndex: 0,
321
+ title: `${d.displayName} via ${d.backend}`
322
+ },
323
+ `${d.icon} ${d.label}`,
324
+ h("span", { style: t.backendBadge(d.backend) }, d.backend)
325
+ )
326
+ ),
327
+ ...disconnectedDrivers.map(
328
+ (d) => h("span", {
312
329
  key: d.platform,
313
- onClick: () => togglePlatform(d.platform),
314
- style: t.pill(selectedPlatforms.includes(d.platform), true),
315
- role: "button",
316
- tabIndex: 0,
317
- title: `${d.displayName} via ${d.backend}`
318
- },
319
- `${d.icon} ${d.label}`,
320
- h("span", { style: t.backendBadge(d.backend) }, d.backend)
330
+ style: t.pill(false, false),
331
+ title: `${d.label} \u2014 not connected`
332
+ }, `${d.icon} ${d.label}`)
333
+ )
334
+ ) : h(
335
+ "div",
336
+ { className: "flex flex-wrap gap-2" },
337
+ ...drivers.map(
338
+ (d) => h(
339
+ "span",
340
+ { key: d.platform, style: t.pill(false, false), title: "Not connected" },
341
+ `${d.icon} ${d.label}`
342
+ )
343
+ ),
344
+ h(
345
+ "div",
346
+ { className: "text-xs mt-1", style: t.faint },
347
+ "No platforms connected. Go to Accounts tab to set up Postiz or add accounts."
321
348
  )
322
- ),
323
- ...disconnectedDrivers.map(
324
- (d) => h("span", {
325
- key: d.platform,
326
- style: t.pill(false, false),
327
- title: `${d.label} \u2014 not connected`
328
- }, `${d.icon} ${d.label}`)
329
349
  )
330
- ) : h(
350
+ ),
351
+ // Media URL (collapsible)
352
+ h(
331
353
  "div",
332
- { className: "flex flex-wrap gap-2" },
333
- ...drivers.map(
334
- (d) => h(
335
- "span",
336
- { key: d.platform, style: t.pill(false, false), title: "Not connected" },
337
- `${d.icon} ${d.label}`
338
- )
339
- ),
354
+ null,
355
+ h("button", {
356
+ type: "button",
357
+ onClick: () => setShowMedia(!showMedia),
358
+ style: { ...t.btnGhost, padding: "0.3rem 0.55rem", fontSize: "0.8rem" }
359
+ }, showMedia ? "\u2212 Media" : "+ Media"),
360
+ showMedia && h(
361
+ "div",
362
+ { className: "mt-2" },
363
+ h("input", {
364
+ type: "url",
365
+ value: mediaUrl,
366
+ onChange: (e) => setMediaUrl(e.target.value),
367
+ placeholder: "Paste image or video URL\u2026",
368
+ style: t.input
369
+ })
370
+ )
371
+ ),
372
+ // Schedule (only if any selected platform supports it)
373
+ (canSchedule || !hasSelection) && h(
374
+ "div",
375
+ { className: "grid grid-cols-1 gap-2 sm:grid-cols-2" },
340
376
  h(
341
377
  "div",
342
- { className: "text-xs mt-1", style: t.faint },
343
- "No platforms connected. Go to Accounts tab to set up Postiz or add accounts."
378
+ null,
379
+ h(
380
+ "div",
381
+ { className: "text-xs font-medium mb-1", style: t.faint },
382
+ canSchedule ? "Schedule (optional)" : "Schedule (connect Postiz for scheduling)"
383
+ ),
384
+ h("input", {
385
+ type: "datetime-local",
386
+ value: scheduledAt,
387
+ onChange: (e) => setScheduledAt(e.target.value),
388
+ style: { ...t.input, opacity: canSchedule || !hasSelection ? 1 : 0.5 },
389
+ disabled: hasSelection && !canSchedule
390
+ })
344
391
  )
345
- )
392
+ ),
393
+ // Actions
394
+ h(
395
+ "div",
396
+ { className: "flex flex-wrap gap-2 items-center" },
397
+ h("button", {
398
+ type: "button",
399
+ onClick: () => void onSaveDraft(),
400
+ style: { ...t.btnGhost, opacity: saving ? 0.7 : 1 },
401
+ disabled: saving || !content.trim()
402
+ }, saving ? "Saving\u2026" : "Save draft"),
403
+ hasConnected && hasSelection && h("button", {
404
+ type: "button",
405
+ onClick: () => void onPublish(),
406
+ style: { ...t.btnPublish, opacity: publishing ? 0.7 : 1 },
407
+ disabled: publishing || !content.trim()
408
+ }, publishing ? "Publishing\u2026" : scheduledAt ? "\u23F1 Schedule" : "\u{1F4E4} Publish now")
409
+ ),
410
+ error && h("div", { className: "text-xs", style: { color: "rgba(248,113,113,0.95)" } }, error),
411
+ success && h("div", { className: "text-xs", style: { color: "rgba(74,222,128,0.9)" } }, success)
346
412
  ),
347
- // Media URL (collapsible)
413
+ // RIGHT live preview pane
348
414
  h(
349
415
  "div",
350
- null,
351
- h("button", {
352
- type: "button",
353
- onClick: () => setShowMedia(!showMedia),
354
- style: { ...t.btnGhost, padding: "0.3rem 0.55rem", fontSize: "0.8rem" }
355
- }, showMedia ? "\u2212 Media" : "+ Media"),
356
- showMedia && h(
416
+ {
417
+ style: {
418
+ width: "320px",
419
+ flexShrink: 0,
420
+ background: "rgba(255,255,255,0.02)",
421
+ border: "1px solid var(--ck-border-subtle)",
422
+ borderRadius: "10px",
423
+ padding: "1rem",
424
+ display: "flex",
425
+ flexDirection: "column",
426
+ alignSelf: "flex-start"
427
+ }
428
+ },
429
+ h("div", { className: "text-sm font-medium mb-3", style: t.text }, "Post Preview"),
430
+ // Selected platforms
431
+ selectedPlatforms.length > 0 && h(
357
432
  "div",
358
- { className: "mt-2" },
359
- h("input", {
360
- type: "url",
361
- value: mediaUrl,
362
- onChange: (e) => setMediaUrl(e.target.value),
363
- placeholder: "Paste image or video URL\u2026",
364
- style: t.input
433
+ { className: "flex flex-wrap gap-1 mb-3" },
434
+ ...selectedPlatforms.map((pl) => {
435
+ const drv = drivers.find((d) => d.platform === pl);
436
+ return h("span", {
437
+ key: pl,
438
+ style: {
439
+ background: "rgba(127,90,240,0.15)",
440
+ border: "1px solid rgba(127,90,240,0.3)",
441
+ borderRadius: "999px",
442
+ padding: "0.12rem 0.45rem",
443
+ fontSize: "0.72rem",
444
+ color: "var(--ck-text-secondary)"
445
+ }
446
+ }, drv ? `${drv.icon} ${drv.label}` : pl);
365
447
  })
366
- )
367
- ),
368
- // Schedule (only if any selected platform supports it)
369
- (canSchedule || !hasSelection) && h(
370
- "div",
371
- { className: "grid grid-cols-1 gap-2 sm:grid-cols-2" },
372
- h(
448
+ ),
449
+ // Scheduling info
450
+ scheduledAt && h("div", {
451
+ className: "text-xs mb-3",
452
+ style: { color: "rgba(251,191,36,0.85)" }
453
+ }, `\u23F1 Scheduled: ${new Date(scheduledAt).toLocaleString()}`),
454
+ // Content preview
455
+ content.trim() ? h("div", {
456
+ style: {
457
+ background: "rgba(255,255,255,0.03)",
458
+ border: "1px solid var(--ck-border-subtle)",
459
+ borderRadius: "10px",
460
+ padding: "0.85rem",
461
+ whiteSpace: "pre-wrap",
462
+ fontSize: "0.85rem",
463
+ color: "var(--ck-text-primary)",
464
+ lineHeight: "1.55",
465
+ maxHeight: "300px",
466
+ overflowY: "auto",
467
+ wordBreak: "break-word"
468
+ }
469
+ }, content) : h("div", {
470
+ style: {
471
+ color: "var(--ck-text-tertiary)",
472
+ fontSize: "0.85rem",
473
+ fontStyle: "italic",
474
+ padding: "2rem 0.5rem",
475
+ textAlign: "center"
476
+ }
477
+ }, "Start writing to see a preview"),
478
+ // Media preview
479
+ mediaUrl && showMedia && h(
373
480
  "div",
374
- null,
481
+ { className: "mt-3" },
482
+ h("img", {
483
+ src: mediaUrl,
484
+ style: {
485
+ maxWidth: "100%",
486
+ borderRadius: "8px",
487
+ border: "1px solid var(--ck-border-subtle)"
488
+ },
489
+ onError: (e) => {
490
+ e.target.style.display = "none";
491
+ }
492
+ })
493
+ ),
494
+ // Character limit bar
495
+ charLimit && content.length > 0 && h(
496
+ "div",
497
+ { className: "mt-3" },
375
498
  h(
376
499
  "div",
377
- { className: "text-xs font-medium mb-1", style: t.faint },
378
- canSchedule ? "Schedule (optional)" : "Schedule (connect Postiz for scheduling)"
500
+ { style: { height: "4px", borderRadius: "2px", background: "rgba(255,255,255,0.06)", overflow: "hidden" } },
501
+ h("div", {
502
+ style: {
503
+ height: "100%",
504
+ borderRadius: "2px",
505
+ width: `${Math.min(content.length / charLimit * 100, 100)}%`,
506
+ background: content.length > charLimit ? "rgba(248,113,113,0.8)" : content.length > charLimit * 0.9 ? "rgba(251,191,36,0.8)" : "rgba(127,90,240,0.6)",
507
+ transition: "width 0.2s, background 0.2s"
508
+ }
509
+ })
379
510
  ),
380
- h("input", {
381
- type: "datetime-local",
382
- value: scheduledAt,
383
- onChange: (e) => setScheduledAt(e.target.value),
384
- style: { ...t.input, opacity: canSchedule || !hasSelection ? 1 : 0.5 },
385
- disabled: hasSelection && !canSchedule
386
- })
511
+ h("div", {
512
+ className: "text-xs mt-1",
513
+ style: {
514
+ color: content.length > charLimit ? "rgba(248,113,113,0.9)" : "var(--ck-text-tertiary)",
515
+ textAlign: "right"
516
+ }
517
+ }, `${content.length} / ${charLimit}`)
387
518
  )
388
- ),
389
- // Actions
390
- h(
391
- "div",
392
- { className: "flex flex-wrap gap-2 items-center" },
393
- h("button", {
394
- type: "button",
395
- onClick: () => void onSaveDraft(),
396
- style: { ...t.btnGhost, opacity: saving ? 0.7 : 1 },
397
- disabled: saving || !content.trim()
398
- }, saving ? "Saving\u2026" : "Save draft"),
399
- hasConnected && hasSelection && h("button", {
400
- type: "button",
401
- onClick: () => void onPublish(),
402
- style: { ...t.btnPublish, opacity: publishing ? 0.7 : 1 },
403
- disabled: publishing || !content.trim()
404
- }, publishing ? "Publishing\u2026" : scheduledAt ? "\u23F1 Schedule" : "\u{1F4E4} Publish now")
405
- ),
406
- error && h("div", { className: "text-xs", style: { color: "rgba(248,113,113,0.95)" } }, error),
407
- success && h("div", { className: "text-xs", style: { color: "rgba(74,222,128,0.9)" } }, success)
519
+ )
408
520
  )
409
521
  ),
410
522
  // ---- Posts list ----
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/kitchen-plugin-marketing",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Marketing Suite plugin for ClawKitchen",
5
5
  "main": "dist/index.js",
6
6
  "files": [