@tangle-network/sandbox-ui 0.131.1 → 0.131.2

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/assets.d.ts CHANGED
@@ -237,6 +237,8 @@ interface CopyPreviewProps {
237
237
  content: CopyContent;
238
238
  className?: string;
239
239
  }
240
+ export declare const COPY_PLATFORM_LABEL: Record<CopyPlatform, string>;
241
+ /** Copy as it will read: the channel and its length budget, the headline, the body and its hashtags. */
240
242
  export declare function CopyPreview({ content, className }: CopyPreviewProps): import("react").JSX.Element;
241
243
  //#endregion
242
244
  //#region src/assets/preview/email-preview.d.ts
@@ -246,7 +248,25 @@ interface EmailPreviewProps {
246
248
  previewUrl?: string;
247
249
  className?: string;
248
250
  }
249
- export declare function EmailPreview({ content, brand, previewUrl, className }: EmailPreviewProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
251
+ /**
252
+ * The colours an email body renders in. A brand whose text colour is light
253
+ * writes on a dark page; any brand colour that would not read on that page
254
+ * falls back to the page's own ink.
255
+ */
256
+ export declare function emailPalette(brand: BrandTokens): {
257
+ scheme: string;
258
+ paper: "#111218" | "#ffffff";
259
+ ink: string;
260
+ accent: string;
261
+ button: string;
262
+ buttonText: string;
263
+ quote: string;
264
+ };
265
+ /**
266
+ * An email as a reader meets it: who it is from, the subject and the inbox
267
+ * preview line, then the body on the brand's page and in its typeface.
268
+ */
269
+ export declare function EmailPreview({ content, brand, previewUrl, className }: EmailPreviewProps): import("react").JSX.Element;
250
270
  //#endregion
251
271
  //#region src/assets/preview/image-preview.d.ts
252
272
  type ImageFormat = "feed" | "story" | "carousel";
package/dist/assets.js CHANGED
@@ -118,6 +118,283 @@ function ImagePreview({ content, brand, format = "feed", className }) {
118
118
  });
119
119
  }
120
120
  //#endregion
121
+ //#region src/assets/preview/email-preview.tsx
122
+ /** sRGB relative luminance of a #rgb or #rrggbb colour; null for anything else. */
123
+ function luminance(color) {
124
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim())?.[1];
125
+ if (!hex) return null;
126
+ const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
127
+ const [r, g, b] = [
128
+ 0,
129
+ 2,
130
+ 4
131
+ ].map((i) => {
132
+ const channel = parseInt(full.slice(i, i + 2), 16) / 255;
133
+ return channel <= .03928 ? channel / 12.92 : ((channel + .055) / 1.055) ** 2.4;
134
+ });
135
+ return .2126 * r + .7152 * g + .0722 * b;
136
+ }
137
+ function contrast(a, b) {
138
+ return (Math.max(a, b) + .05) / (Math.min(a, b) + .05);
139
+ }
140
+ const PAPER = {
141
+ light: "#ffffff",
142
+ dark: "#111218"
143
+ };
144
+ const INK = {
145
+ light: "#16171d",
146
+ dark: "#f4f4f6"
147
+ };
148
+ /**
149
+ * The colours an email body renders in. A brand whose text colour is light
150
+ * writes on a dark page; any brand colour that would not read on that page
151
+ * falls back to the page's own ink.
152
+ */
153
+ function emailPalette(brand) {
154
+ const text = luminance(brand.textColor);
155
+ const scheme = text !== null && text > .5 ? "dark" : "light";
156
+ const paper = PAPER[scheme];
157
+ const paperLum = luminance(paper);
158
+ const readable = (color, minimum) => {
159
+ const lum = luminance(color);
160
+ return lum !== null && contrast(lum, paperLum) >= minimum;
161
+ };
162
+ const ink = readable(brand.textColor, 4.5) ? brand.textColor : INK[scheme];
163
+ const accent = readable(brand.primaryColor, 3) ? brand.primaryColor : ink;
164
+ const primary = luminance(brand.primaryColor);
165
+ const buttonText = primary !== null && contrast(primary, 1) < contrast(primary, 0) ? "#111111" : "#ffffff";
166
+ return {
167
+ scheme,
168
+ paper,
169
+ ink,
170
+ accent,
171
+ button: brand.primaryColor,
172
+ buttonText,
173
+ quote: brand.accentColor
174
+ };
175
+ }
176
+ function Button$1({ href, label, palette }) {
177
+ return /* @__PURE__ */ jsx("a", {
178
+ href,
179
+ onClick: (event) => event.preventDefault(),
180
+ className: "inline-block rounded-md px-5 py-2.5 text-sm font-semibold no-underline",
181
+ style: {
182
+ background: palette.button,
183
+ color: palette.buttonText
184
+ },
185
+ children: label
186
+ });
187
+ }
188
+ function Section({ section, palette }) {
189
+ const muted = {
190
+ color: palette.ink,
191
+ opacity: .72
192
+ };
193
+ switch (section.type) {
194
+ case "hero": return /* @__PURE__ */ jsxs("div", {
195
+ className: "flex flex-col items-center gap-3 py-2 text-center",
196
+ children: [
197
+ section.imageUrl && /* @__PURE__ */ jsx("img", {
198
+ src: section.imageUrl,
199
+ alt: "",
200
+ className: "w-full rounded-md object-cover"
201
+ }),
202
+ /* @__PURE__ */ jsx("h2", {
203
+ className: "text-2xl font-bold leading-tight tracking-tight",
204
+ style: { color: palette.accent },
205
+ children: section.headline
206
+ }),
207
+ section.subheadline && /* @__PURE__ */ jsx("p", {
208
+ className: "text-base leading-relaxed",
209
+ style: muted,
210
+ children: section.subheadline
211
+ }),
212
+ section.ctaLabel && /* @__PURE__ */ jsx("div", {
213
+ className: "pt-1",
214
+ children: /* @__PURE__ */ jsx(Button$1, {
215
+ href: section.ctaUrl ?? "#",
216
+ label: section.ctaLabel,
217
+ palette
218
+ })
219
+ })
220
+ ]
221
+ });
222
+ case "body": return /* @__PURE__ */ jsx("p", {
223
+ className: "text-base leading-7 whitespace-pre-wrap",
224
+ children: section.text
225
+ });
226
+ case "feature": return /* @__PURE__ */ jsxs("div", {
227
+ className: "flex items-start gap-4",
228
+ children: [section.imageUrl && /* @__PURE__ */ jsx("img", {
229
+ src: section.imageUrl,
230
+ alt: "",
231
+ className: "size-16 shrink-0 rounded-md object-cover"
232
+ }), /* @__PURE__ */ jsxs("div", {
233
+ className: "flex flex-col gap-1",
234
+ children: [/* @__PURE__ */ jsx("div", {
235
+ className: "text-base font-semibold",
236
+ children: section.headline
237
+ }), /* @__PURE__ */ jsx("div", {
238
+ className: "text-sm leading-relaxed",
239
+ style: muted,
240
+ children: section.description
241
+ })]
242
+ })]
243
+ });
244
+ case "testimonial": return /* @__PURE__ */ jsxs("blockquote", {
245
+ className: "border-l-4 pl-4",
246
+ style: { borderColor: palette.quote },
247
+ children: [/* @__PURE__ */ jsxs("p", {
248
+ className: "text-base italic leading-relaxed",
249
+ children: [
250
+ "“",
251
+ section.quote,
252
+ "”"
253
+ ]
254
+ }), /* @__PURE__ */ jsxs("footer", {
255
+ className: "mt-2 text-sm font-medium",
256
+ style: muted,
257
+ children: [section.author, section.role ? `, ${section.role}` : ""]
258
+ })]
259
+ });
260
+ case "cta": return /* @__PURE__ */ jsxs("div", {
261
+ className: "flex flex-col items-center gap-2 py-2 text-center",
262
+ children: [/* @__PURE__ */ jsx(Button$1, {
263
+ href: section.url,
264
+ label: section.label,
265
+ palette
266
+ }), section.subtext && /* @__PURE__ */ jsx("p", {
267
+ className: "text-sm",
268
+ style: muted,
269
+ children: section.subtext
270
+ })]
271
+ });
272
+ case "divider": return /* @__PURE__ */ jsx("hr", {
273
+ className: "border-0 border-t",
274
+ style: {
275
+ borderColor: palette.ink,
276
+ opacity: .15
277
+ }
278
+ });
279
+ default: return null;
280
+ }
281
+ }
282
+ /**
283
+ * An email as a reader meets it: who it is from, the subject and the inbox
284
+ * preview line, then the body on the brand's page and in its typeface.
285
+ */
286
+ function EmailPreview({ content, brand, previewUrl, className }) {
287
+ const palette = emailPalette(brand);
288
+ return /* @__PURE__ */ jsxs("article", {
289
+ className: cn("overflow-hidden rounded-lg border border-border bg-card shadow-sm", className),
290
+ children: [/* @__PURE__ */ jsxs("header", {
291
+ className: "flex flex-col gap-1 border-b border-border px-5 py-4",
292
+ children: [
293
+ /* @__PURE__ */ jsxs("div", {
294
+ className: "flex items-center gap-2 text-sm",
295
+ children: [/* @__PURE__ */ jsx("span", {
296
+ "aria-hidden": true,
297
+ className: "flex size-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold",
298
+ style: {
299
+ background: palette.button,
300
+ color: palette.buttonText
301
+ },
302
+ children: brand.businessName.trim().charAt(0).toUpperCase() || "?"
303
+ }), /* @__PURE__ */ jsx("span", {
304
+ className: "font-medium",
305
+ children: brand.businessName
306
+ })]
307
+ }),
308
+ /* @__PURE__ */ jsx("h3", {
309
+ className: "text-base font-semibold leading-snug",
310
+ children: content.subject
311
+ }),
312
+ content.preheader && /* @__PURE__ */ jsx("p", {
313
+ className: "text-sm text-muted-foreground",
314
+ children: content.preheader
315
+ })
316
+ ]
317
+ }), previewUrl ? /* @__PURE__ */ jsx("iframe", {
318
+ src: previewUrl,
319
+ title: "Email preview",
320
+ sandbox: "allow-same-origin",
321
+ className: "block h-[480px] w-full bg-white"
322
+ }) : /* @__PURE__ */ jsx("div", {
323
+ className: "px-5 py-8 sm:px-8",
324
+ style: {
325
+ background: palette.paper,
326
+ color: palette.ink,
327
+ fontFamily: brand.fontFamily
328
+ },
329
+ children: /* @__PURE__ */ jsx("div", {
330
+ className: "mx-auto flex max-w-[560px] flex-col gap-5",
331
+ children: content.sections.map((section, index) => /* @__PURE__ */ jsx(Section, {
332
+ section,
333
+ palette
334
+ }, index))
335
+ })
336
+ })]
337
+ });
338
+ }
339
+ //#endregion
340
+ //#region src/assets/preview/copy-preview.tsx
341
+ const PLATFORM_LIMITS = {
342
+ instagram: 2200,
343
+ tiktok: 2200,
344
+ x: 280,
345
+ linkedin: 3e3,
346
+ sms: 160,
347
+ "email-subject": 60
348
+ };
349
+ const COPY_PLATFORM_LABEL = {
350
+ instagram: "Instagram",
351
+ tiktok: "TikTok",
352
+ x: "X",
353
+ linkedin: "LinkedIn",
354
+ sms: "SMS",
355
+ "email-subject": "Email subject"
356
+ };
357
+ /** Copy as it will read: the channel and its length budget, the headline, the body and its hashtags. */
358
+ function CopyPreview({ content, className }) {
359
+ const limit = PLATFORM_LIMITS[content.platform];
360
+ const length = content.body.length;
361
+ const over = limit !== null && length > limit;
362
+ const near = !over && limit !== null && length >= limit * .9;
363
+ return /* @__PURE__ */ jsxs("article", {
364
+ className: cn("flex flex-col gap-4 rounded-xl border border-border bg-card p-6 shadow-sm", className),
365
+ children: [
366
+ /* @__PURE__ */ jsxs("div", {
367
+ className: "flex items-center justify-between gap-3",
368
+ children: [/* @__PURE__ */ jsx(Badge, {
369
+ variant: "secondary",
370
+ className: "text-xs",
371
+ children: COPY_PLATFORM_LABEL[content.platform] ?? content.platform
372
+ }), limit !== null && /* @__PURE__ */ jsxs("span", {
373
+ className: cn("text-xs tabular-nums", over ? "font-medium text-destructive" : near ? "text-warning" : "text-muted-foreground"),
374
+ children: [
375
+ length,
376
+ " / ",
377
+ limit,
378
+ " characters"
379
+ ]
380
+ })]
381
+ }),
382
+ content.headline && /* @__PURE__ */ jsx("h3", {
383
+ className: "text-xl font-semibold leading-snug tracking-tight",
384
+ children: content.headline
385
+ }),
386
+ /* @__PURE__ */ jsx("p", {
387
+ className: "text-base leading-7 whitespace-pre-wrap [overflow-wrap:anywhere]",
388
+ children: content.body
389
+ }),
390
+ content.hashtags && content.hashtags.length > 0 && /* @__PURE__ */ jsx("div", {
391
+ className: "flex flex-wrap gap-x-2 gap-y-1 text-sm text-primary",
392
+ children: content.hashtags.map((tag) => /* @__PURE__ */ jsxs("span", { children: ["#", tag.replace(/^#/, "")] }, tag))
393
+ })
394
+ ]
395
+ });
396
+ }
397
+ //#endregion
121
398
  //#region src/assets/labels.ts
122
399
  /** Reader-facing name for each asset format. */
123
400
  const ASSET_FORMAT_LABEL = {
@@ -177,15 +454,46 @@ function Thumbnail({ spec }) {
177
454
  const { format, brand, content } = spec;
178
455
  if (format === "email") {
179
456
  const ec = content;
180
- return /* @__PURE__ */ jsxs("div", {
181
- className: "flex h-full flex-col gap-1 p-3 text-sm",
182
- children: [/* @__PURE__ */ jsx("div", {
183
- className: "line-clamp-3 font-medium",
184
- children: ec.subject
185
- }), /* @__PURE__ */ jsxs("div", {
186
- className: "text-xs text-muted-foreground",
187
- children: [ec.sections.length, " sections"]
188
- })]
457
+ const palette = emailPalette(brand);
458
+ const hero = ec.sections.find((section) => section.type === "hero");
459
+ const body = ec.sections.find((section) => section.type === "body");
460
+ return /* @__PURE__ */ jsx("div", {
461
+ className: "flex h-full flex-col p-3",
462
+ children: /* @__PURE__ */ jsxs("div", {
463
+ className: "flex h-full flex-col overflow-hidden rounded-md border border-border bg-card shadow-sm",
464
+ children: [/* @__PURE__ */ jsxs("div", {
465
+ className: "flex flex-col gap-0.5 border-b border-border px-3 py-2",
466
+ children: [
467
+ /* @__PURE__ */ jsx("div", {
468
+ className: "truncate text-xs text-muted-foreground",
469
+ children: brand.businessName
470
+ }),
471
+ /* @__PURE__ */ jsx("div", {
472
+ className: "line-clamp-2 text-sm font-semibold leading-snug",
473
+ children: ec.subject
474
+ }),
475
+ ec.preheader && /* @__PURE__ */ jsx("div", {
476
+ className: "truncate text-xs text-muted-foreground",
477
+ children: ec.preheader
478
+ })
479
+ ]
480
+ }), /* @__PURE__ */ jsxs("div", {
481
+ className: "flex flex-1 flex-col gap-1 overflow-hidden px-3 py-2",
482
+ style: {
483
+ background: palette.paper,
484
+ color: palette.ink,
485
+ fontFamily: brand.fontFamily
486
+ },
487
+ children: [hero?.type === "hero" && /* @__PURE__ */ jsx("div", {
488
+ className: "line-clamp-2 text-sm font-bold leading-snug",
489
+ style: { color: palette.accent },
490
+ children: hero.headline
491
+ }), body?.type === "body" && /* @__PURE__ */ jsx("div", {
492
+ className: "line-clamp-4 text-xs leading-relaxed opacity-75",
493
+ children: body.text
494
+ })]
495
+ })]
496
+ })
189
497
  });
190
498
  }
191
499
  if (format.startsWith("image:")) {
@@ -196,7 +504,7 @@ function Thumbnail({ spec }) {
196
504
  alt: "",
197
505
  loading: "lazy",
198
506
  decoding: "async",
199
- className: "h-full w-full object-contain"
507
+ className: "h-full w-full object-contain p-2"
200
508
  });
201
509
  return /* @__PURE__ */ jsx("div", {
202
510
  className: "h-full overflow-hidden",
@@ -218,7 +526,7 @@ function Thumbnail({ spec }) {
218
526
  alt: "",
219
527
  loading: "lazy",
220
528
  decoding: "async",
221
- className: "h-full w-full object-contain"
529
+ className: "h-full w-full object-contain p-2"
222
530
  }) : /* @__PURE__ */ jsx("div", {
223
531
  className: "flex h-full flex-col gap-1 p-3 text-sm",
224
532
  children: /* @__PURE__ */ jsxs("div", {
@@ -244,14 +552,21 @@ function Thumbnail({ spec }) {
244
552
  if (format.startsWith("copy:")) {
245
553
  const cc = content;
246
554
  return /* @__PURE__ */ jsxs("div", {
247
- className: "flex h-full flex-col gap-1 p-3 text-sm",
248
- children: [/* @__PURE__ */ jsx("div", {
249
- className: "line-clamp-2 font-medium",
250
- children: cc.headline
251
- }), /* @__PURE__ */ jsx("div", {
252
- className: "line-clamp-4 text-xs text-muted-foreground",
253
- children: cc.body
254
- })]
555
+ className: "flex h-full flex-col gap-2 overflow-hidden p-4",
556
+ children: [
557
+ /* @__PURE__ */ jsx("span", {
558
+ className: "text-xs font-medium text-muted-foreground",
559
+ children: COPY_PLATFORM_LABEL[cc.platform] ?? cc.platform
560
+ }),
561
+ cc.headline && /* @__PURE__ */ jsx("div", {
562
+ className: "line-clamp-3 text-base font-semibold leading-snug",
563
+ children: cc.headline
564
+ }),
565
+ /* @__PURE__ */ jsx("div", {
566
+ className: "line-clamp-5 text-sm leading-relaxed text-muted-foreground",
567
+ children: cc.body
568
+ })
569
+ ]
255
570
  });
256
571
  }
257
572
  return null;
@@ -325,125 +640,6 @@ function AssetCard({ spec, variantCount, onApprove, onReject, onEdit, onOpen, cl
325
640
  });
326
641
  }
327
642
  //#endregion
328
- //#region src/assets/preview/email-preview.tsx
329
- function EmailPreview({ content, brand, previewUrl, className }) {
330
- if (previewUrl) return jsxs("div", {
331
- className: cn("flex flex-col gap-1", className),
332
- children: [
333
- jsx("div", {
334
- className: "text-xs text-muted-foreground font-medium truncate",
335
- children: content.subject
336
- }),
337
- content.preheader && jsx("div", {
338
- className: "text-xs text-muted-foreground/60 truncate",
339
- children: content.preheader
340
- }),
341
- jsx("iframe", {
342
- src: previewUrl,
343
- className: "w-full rounded border border-border",
344
- style: {
345
- height: 480,
346
- background: "#fff"
347
- },
348
- title: "Email preview",
349
- sandbox: "allow-same-origin"
350
- })
351
- ]
352
- });
353
- return jsxs("div", {
354
- className: cn("flex flex-col gap-2", className),
355
- children: [
356
- jsx("div", {
357
- className: "text-sm font-semibold truncate",
358
- children: content.subject
359
- }),
360
- content.preheader && jsx("div", {
361
- className: "text-xs text-muted-foreground truncate",
362
- children: content.preheader
363
- }),
364
- jsx("div", {
365
- className: "rounded border border-border p-4 space-y-3 overflow-y-auto",
366
- style: {
367
- maxHeight: 480,
368
- fontFamily: brand.fontFamily,
369
- color: brand.textColor
370
- },
371
- children: content.sections.map((section, i) => {
372
- if (section.type === "hero") return jsxs("div", {
373
- className: "text-center py-4 space-y-2",
374
- children: [
375
- section.imageUrl && jsx("img", {
376
- src: section.imageUrl,
377
- alt: "",
378
- className: "mx-auto max-h-40 object-cover rounded"
379
- }),
380
- jsx("div", {
381
- className: "text-xl font-bold",
382
- style: { color: brand.primaryColor },
383
- children: section.headline
384
- }),
385
- section.subheadline && jsx("div", {
386
- className: "text-sm text-muted-foreground",
387
- children: section.subheadline
388
- }),
389
- section.ctaLabel && jsx("a", {
390
- href: section.ctaUrl ?? "#",
391
- className: "inline-block px-4 py-2 rounded text-sm font-medium text-white",
392
- style: { background: brand.primaryColor },
393
- children: section.ctaLabel
394
- })
395
- ]
396
- }, i);
397
- if (section.type === "body") return jsx("p", {
398
- className: "text-sm leading-relaxed whitespace-pre-wrap",
399
- children: section.text
400
- }, i);
401
- if (section.type === "feature") return jsxs("div", {
402
- className: "flex gap-3 items-start",
403
- children: [section.imageUrl && jsx("img", {
404
- src: section.imageUrl,
405
- alt: "",
406
- className: "w-16 h-16 object-cover rounded shrink-0"
407
- }), jsxs("div", { children: [jsx("div", {
408
- className: "text-sm font-semibold",
409
- children: section.headline
410
- }), jsx("div", {
411
- className: "text-xs text-muted-foreground mt-0.5",
412
- children: section.description
413
- })] })]
414
- }, i);
415
- if (section.type === "testimonial") return jsxs("blockquote", {
416
- className: "border-l-2 pl-3 italic text-sm text-muted-foreground",
417
- style: { borderColor: brand.accentColor },
418
- children: [jsxs("p", { children: [
419
- "\"",
420
- section.quote,
421
- "\""
422
- ] }), jsxs("footer", {
423
- className: "mt-1 text-xs not-italic font-medium",
424
- children: [section.author, section.role ? `, ${section.role}` : ""]
425
- })]
426
- }, i);
427
- if (section.type === "cta") return jsxs("div", {
428
- className: "text-center py-3 space-y-1",
429
- children: [jsx("a", {
430
- href: section.url,
431
- className: "inline-block px-5 py-2.5 rounded font-medium text-white text-sm",
432
- style: { background: brand.primaryColor },
433
- children: section.label
434
- }), section.subtext && jsx("p", {
435
- className: "text-xs text-muted-foreground",
436
- children: section.subtext
437
- })]
438
- }, i);
439
- if (section.type === "divider") return jsx("hr", { className: "border-border" }, i);
440
- return null;
441
- })
442
- })
443
- ]
444
- });
445
- }
446
- //#endregion
447
643
  //#region src/assets/preview/video-preview.tsx
448
644
  function VideoPreview({ content, brand, onRenderRequest, isRendering, className }) {
449
645
  if (content.renderedUrl) return /* @__PURE__ */ jsx("div", {
@@ -534,64 +730,6 @@ function VideoPreview({ content, brand, onRenderRequest, isRendering, className
534
730
  });
535
731
  }
536
732
  //#endregion
537
- //#region src/assets/preview/copy-preview.tsx
538
- const PLATFORM_LIMITS = {
539
- instagram: 2200,
540
- tiktok: 2200,
541
- x: 280,
542
- linkedin: 3e3,
543
- sms: 160,
544
- "email-subject": 60
545
- };
546
- const PLATFORM_LABELS = {
547
- instagram: "Instagram",
548
- tiktok: "TikTok",
549
- x: "X / Twitter",
550
- linkedin: "LinkedIn",
551
- sms: "SMS",
552
- "email-subject": "Email Subject"
553
- };
554
- function CopyPreview({ content, className }) {
555
- const limit = PLATFORM_LIMITS[content.platform];
556
- const bodyLen = content.body.length;
557
- const isOverLimit = limit !== null && bodyLen > limit;
558
- const warningThreshold = limit !== null ? limit * .9 : null;
559
- const isNearLimit = !isOverLimit && warningThreshold !== null && bodyLen >= warningThreshold;
560
- return /* @__PURE__ */ jsxs("div", {
561
- className: cn("flex flex-col gap-2", className),
562
- children: [/* @__PURE__ */ jsxs("div", {
563
- className: "flex items-center justify-between",
564
- children: [/* @__PURE__ */ jsx("span", {
565
- className: "text-xs font-medium text-muted-foreground",
566
- children: PLATFORM_LABELS[content.platform]
567
- }), limit !== null && /* @__PURE__ */ jsxs("span", {
568
- className: cn("text-xs tabular-nums", isOverLimit ? "text-destructive font-medium" : isNearLimit ? "text-warning" : "text-muted-foreground"),
569
- children: [
570
- bodyLen,
571
- " / ",
572
- limit
573
- ]
574
- })]
575
- }), /* @__PURE__ */ jsxs("div", {
576
- className: "rounded border border-border p-3 space-y-2 bg-card",
577
- children: [
578
- content.headline && /* @__PURE__ */ jsx("div", {
579
- className: "text-sm font-semibold",
580
- children: content.headline
581
- }),
582
- /* @__PURE__ */ jsx("p", {
583
- className: "text-sm whitespace-pre-wrap leading-relaxed",
584
- children: content.body
585
- }),
586
- content.hashtags && content.hashtags.length > 0 && /* @__PURE__ */ jsx("div", {
587
- className: "text-sm text-blue-500 flex flex-wrap gap-1",
588
- children: content.hashtags.map((tag) => /* @__PURE__ */ jsxs("span", { children: ["#", tag] }, tag))
589
- })
590
- ]
591
- })]
592
- });
593
- }
594
- //#endregion
595
733
  //#region src/assets/asset-editor.tsx
596
734
  function BrandPanel({ brand, onChange }) {
597
735
  const [open, setOpen] = React$1.useState(false);
@@ -1126,4 +1264,4 @@ function ApprovalQueue({ assets, variantCounts = {}, onApprove, onReject, onEdit
1126
1264
  });
1127
1265
  }
1128
1266
  //#endregion
1129
- export { ASSET_FORMAT_LABEL, ASSET_STATUS_LABEL, ApprovalQueue, AssetCard, AssetEditor, CopyPreview, EmailPreview, ImagePreview, VideoPreview, imageOnlyUrl, videoPosterUrl };
1267
+ export { ASSET_FORMAT_LABEL, ASSET_STATUS_LABEL, ApprovalQueue, AssetCard, AssetEditor, COPY_PLATFORM_LABEL, CopyPreview, EmailPreview, ImagePreview, VideoPreview, emailPalette, imageOnlyUrl, videoPosterUrl };
package/dist/globals.css CHANGED
@@ -1007,9 +1007,6 @@
1007
1007
  .h-screen {
1008
1008
  height: 100vh;
1009
1009
  }
1010
- .max-h-40 {
1011
- max-height: calc(var(--spacing) * 40);
1012
- }
1013
1010
  .max-h-64 {
1014
1011
  max-height: calc(var(--spacing) * 64);
1015
1012
  }
@@ -2184,9 +2181,9 @@
2184
2181
  border-left-style: var(--tw-border-style);
2185
2182
  border-left-width: 0px;
2186
2183
  }
2187
- .border-l-2 {
2184
+ .border-l-4 {
2188
2185
  border-left-style: var(--tw-border-style);
2189
- border-left-width: 2px;
2186
+ border-left-width: 4px;
2190
2187
  }
2191
2188
  .border-dashed {
2192
2189
  --tw-border-style: dashed;
@@ -3747,9 +3744,6 @@
3747
3744
  .text-blue-400 {
3748
3745
  color: var(--color-blue-400);
3749
3746
  }
3750
- .text-blue-500 {
3751
- color: var(--color-blue-500);
3752
- }
3753
3747
  .text-border {
3754
3748
  color: hsl(var(--border));
3755
3749
  }
@@ -3894,13 +3888,13 @@
3894
3888
  .italic {
3895
3889
  font-style: italic;
3896
3890
  }
3897
- .not-italic {
3898
- font-style: normal;
3899
- }
3900
3891
  .tabular-nums {
3901
3892
  --tw-numeric-spacing: tabular-nums;
3902
3893
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
3903
3894
  }
3895
+ .no-underline {
3896
+ text-decoration-line: none;
3897
+ }
3904
3898
  .underline {
3905
3899
  text-decoration-line: underline;
3906
3900
  }
@@ -5376,6 +5370,9 @@
5376
5370
  .sm\:px-6 {
5377
5371
  padding-inline: calc(var(--spacing) * 6);
5378
5372
  }
5373
+ .sm\:px-8 {
5374
+ padding-inline: calc(var(--spacing) * 8);
5375
+ }
5379
5376
  .sm\:py-1 {
5380
5377
  padding-block: var(--spacing);
5381
5378
  }
package/dist/styles.css CHANGED
@@ -1007,9 +1007,6 @@
1007
1007
  .h-screen {
1008
1008
  height: 100vh;
1009
1009
  }
1010
- .max-h-40 {
1011
- max-height: calc(var(--spacing) * 40);
1012
- }
1013
1010
  .max-h-64 {
1014
1011
  max-height: calc(var(--spacing) * 64);
1015
1012
  }
@@ -2184,9 +2181,9 @@
2184
2181
  border-left-style: var(--tw-border-style);
2185
2182
  border-left-width: 0px;
2186
2183
  }
2187
- .border-l-2 {
2184
+ .border-l-4 {
2188
2185
  border-left-style: var(--tw-border-style);
2189
- border-left-width: 2px;
2186
+ border-left-width: 4px;
2190
2187
  }
2191
2188
  .border-dashed {
2192
2189
  --tw-border-style: dashed;
@@ -3747,9 +3744,6 @@
3747
3744
  .text-blue-400 {
3748
3745
  color: var(--color-blue-400);
3749
3746
  }
3750
- .text-blue-500 {
3751
- color: var(--color-blue-500);
3752
- }
3753
3747
  .text-border {
3754
3748
  color: hsl(var(--border));
3755
3749
  }
@@ -3894,13 +3888,13 @@
3894
3888
  .italic {
3895
3889
  font-style: italic;
3896
3890
  }
3897
- .not-italic {
3898
- font-style: normal;
3899
- }
3900
3891
  .tabular-nums {
3901
3892
  --tw-numeric-spacing: tabular-nums;
3902
3893
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
3903
3894
  }
3895
+ .no-underline {
3896
+ text-decoration-line: none;
3897
+ }
3904
3898
  .underline {
3905
3899
  text-decoration-line: underline;
3906
3900
  }
@@ -5376,6 +5370,9 @@
5376
5370
  .sm\:px-6 {
5377
5371
  padding-inline: calc(var(--spacing) * 6);
5378
5372
  }
5373
+ .sm\:px-8 {
5374
+ padding-inline: calc(var(--spacing) * 8);
5375
+ }
5379
5376
  .sm\:py-1 {
5380
5377
  padding-block: var(--spacing);
5381
5378
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.131.1",
3
+ "version": "0.131.2",
4
4
  "license": "MIT",
5
5
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
6
6
  "repository": {