@agentiffai/design 1.3.10 → 1.3.12

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/index.d.cts CHANGED
@@ -5,7 +5,7 @@ import React__default, { ReactNode } from 'react';
5
5
  export { ActionButtons, ActionButtonsProps, BrandType, CategoryItem, CategoryNav, CategoryNavProps, CategorySubItem, Icon, IconName, IconNames, IconProps, Layout, LayoutProps, NavHorizontal, NavHorizontalProps, NavVertical, NavVerticalProps, OAuthConnectionData, PaneMenus, PaneMenusProps, PaneSectionHeader, PaneSectionHeaderProps, ServiceBrand, ServiceIcon, ServiceIconProps, WorkflowStatusCard, WorkflowStatusCardProps } from './layout/index.cjs';
6
6
  import { AriaButtonProps } from '@react-aria/button';
7
7
  export { GlobalStyle, darkTheme, lightTheme, tokens } from './theme/index.cjs';
8
- export { A as Action, c as ActionVariant, d as Actions, a as ActionsLayout, b as ActionsProps, f as AgentState, e as AgentStateProps, q as AssistantMessage, m as AssistantMessageProps, i as Button, B as ButtonProps, g as ButtonSize, h as ButtonVariant, w as CopilotUserMessage, U as CopilotUserMessageProps, r as FileAttachment, n as FileAttachmentProps, j as Footer, F as FooterProps, k as Header, H as HeaderProps, l as Input, I as InputProps, M as Message, s as Messages, t as MessagesList, u as MessagesListContainer, v as MessagesListContent, o as MessagesListProps, p as MessagesProps, x as Response, R as ResponseProps, y as StreamErrorMessage, S as StreamErrorMessageProps, E as StreamStatusIndicator, D as StreamStatusIndicatorProps, C as StreamingText, z as StreamingTextProps, J as Suggestions, G as SuggestionsProps, K as Window, W as WindowProps } from './Window-BcTRumpc.cjs';
8
+ export { A as Action, c as ActionVariant, d as Actions, a as ActionsLayout, b as ActionsProps, f as AgentState, e as AgentStateProps, q as AssistantMessage, m as AssistantMessageProps, i as Button, B as ButtonProps, g as ButtonSize, h as ButtonVariant, w as CopilotUserMessage, U as CopilotUserMessageProps, r as FileAttachment, n as FileAttachmentProps, j as Footer, F as FooterProps, k as Header, H as HeaderProps, l as Input, I as InputProps, M as Message, s as Messages, t as MessagesList, u as MessagesListContainer, v as MessagesListContent, o as MessagesListProps, p as MessagesProps, x as Response, R as ResponseProps, y as StreamErrorMessage, S as StreamErrorMessageProps, E as StreamStatusIndicator, D as StreamStatusIndicatorProps, C as StreamingText, z as StreamingTextProps, J as Suggestions, G as SuggestionsProps, K as Window, W as WindowProps } from './Window-pJb3Z5_P.cjs';
9
9
  export { FacebookIcon, FacebookIconProps, FacebookIconVariant, GmailIcon, GmailIconProps, GmailIconVariant, InstagramIcon, InstagramIconProps, InstagramVariant, LinkedInIcon, LinkedInIconProps, LinkedInIconVariant, PostizIcon, PostizIconProps, PostizIconVariant, RedditIcon, RedditIconProps, RedditIconVariant, SlackIcon, SlackIconProps, SlackIconVariant, TelegramIcon, TelegramIconProps, TelegramIconVariant, WhatsAppIcon, WhatsAppIconProps, WhatsAppIconVariant, XIcon, XIconProps, XIconVariant, YouTubeIcon, YouTubeIconProps, YouTubeIconVariant } from './icons/index.cjs';
10
10
  export { WorkflowCard, WorkflowCardProps, WorkflowErrorAlert, WorkflowErrorAlertProps, WorkflowIntegration, WorkflowProgressBar, WorkflowProgressBarProps, WorkflowResultPanel, WorkflowResultPanelProps, WorkflowStatusBadge, WorkflowStatusBadgeProps } from './workflow/index.cjs';
11
11
  import '@react-types/button';
@@ -298,7 +298,7 @@ interface PlatformConfig {
298
298
  /**
299
299
  * Categories for workflow progress tracking
300
300
  */
301
- type ProgressCategory = 'notion' | 'setup' | 'integration' | 'generation' | 'image' | 'scheduling' | 'completion';
301
+ type ProgressCategory = 'notion' | 'setup' | 'integration' | 'generation' | 'image' | 'scheduling' | 'completion' | 'processing' | 'custom';
302
302
  /**
303
303
  * Configuration for a progress category
304
304
  */
@@ -356,20 +356,22 @@ declare const PLATFORM_CONFIGS: Record<Platform, PlatformConfig>;
356
356
  declare const CATEGORY_CONFIGS: Record<ProgressCategory, CategoryConfig>;
357
357
  /**
358
358
  * Categorize a progress message into a ProgressCategory
359
+ * Handles undefined/null message gracefully
359
360
  */
360
- declare function categorizeProgress(message: string): ProgressCategory;
361
+ declare function categorizeProgress(message: string | undefined | null): ProgressCategory;
361
362
  /**
362
- * Get the icon for a progress category
363
+ * Get the icon for a progress category (with fallback for unknown categories)
363
364
  */
364
365
  declare function getCategoryIcon(category: ProgressCategory): string;
365
366
  /**
366
- * Get the color class for a progress category
367
+ * Get the color class for a progress category (with fallback for unknown categories)
367
368
  */
368
369
  declare function getCategoryColor(category: ProgressCategory): string;
369
370
  /**
370
371
  * Normalize platform name (e.g., 'twitter' -> 'x')
372
+ * Handles undefined/null platform gracefully
371
373
  */
372
- declare function normalizePlatform(platform: string): Platform | null;
374
+ declare function normalizePlatform(platform: string | undefined | null): Platform | null;
373
375
  /**
374
376
  * Calculate character count including hashtags
375
377
  */
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import React__default, { ReactNode } from 'react';
5
5
  export { ActionButtons, ActionButtonsProps, BrandType, CategoryItem, CategoryNav, CategoryNavProps, CategorySubItem, Icon, IconName, IconNames, IconProps, Layout, LayoutProps, NavHorizontal, NavHorizontalProps, NavVertical, NavVerticalProps, OAuthConnectionData, PaneMenus, PaneMenusProps, PaneSectionHeader, PaneSectionHeaderProps, ServiceBrand, ServiceIcon, ServiceIconProps, WorkflowStatusCard, WorkflowStatusCardProps } from './layout/index.js';
6
6
  import { AriaButtonProps } from '@react-aria/button';
7
7
  export { GlobalStyle, darkTheme, lightTheme, tokens } from './theme/index.js';
8
- export { A as Action, c as ActionVariant, d as Actions, a as ActionsLayout, b as ActionsProps, f as AgentState, e as AgentStateProps, q as AssistantMessage, m as AssistantMessageProps, i as Button, B as ButtonProps, g as ButtonSize, h as ButtonVariant, w as CopilotUserMessage, U as CopilotUserMessageProps, r as FileAttachment, n as FileAttachmentProps, j as Footer, F as FooterProps, k as Header, H as HeaderProps, l as Input, I as InputProps, M as Message, s as Messages, t as MessagesList, u as MessagesListContainer, v as MessagesListContent, o as MessagesListProps, p as MessagesProps, x as Response, R as ResponseProps, y as StreamErrorMessage, S as StreamErrorMessageProps, E as StreamStatusIndicator, D as StreamStatusIndicatorProps, C as StreamingText, z as StreamingTextProps, J as Suggestions, G as SuggestionsProps, K as Window, W as WindowProps } from './Window-BcTRumpc.js';
8
+ export { A as Action, c as ActionVariant, d as Actions, a as ActionsLayout, b as ActionsProps, f as AgentState, e as AgentStateProps, q as AssistantMessage, m as AssistantMessageProps, i as Button, B as ButtonProps, g as ButtonSize, h as ButtonVariant, w as CopilotUserMessage, U as CopilotUserMessageProps, r as FileAttachment, n as FileAttachmentProps, j as Footer, F as FooterProps, k as Header, H as HeaderProps, l as Input, I as InputProps, M as Message, s as Messages, t as MessagesList, u as MessagesListContainer, v as MessagesListContent, o as MessagesListProps, p as MessagesProps, x as Response, R as ResponseProps, y as StreamErrorMessage, S as StreamErrorMessageProps, E as StreamStatusIndicator, D as StreamStatusIndicatorProps, C as StreamingText, z as StreamingTextProps, J as Suggestions, G as SuggestionsProps, K as Window, W as WindowProps } from './Window-pJb3Z5_P.js';
9
9
  export { FacebookIcon, FacebookIconProps, FacebookIconVariant, GmailIcon, GmailIconProps, GmailIconVariant, InstagramIcon, InstagramIconProps, InstagramVariant, LinkedInIcon, LinkedInIconProps, LinkedInIconVariant, PostizIcon, PostizIconProps, PostizIconVariant, RedditIcon, RedditIconProps, RedditIconVariant, SlackIcon, SlackIconProps, SlackIconVariant, TelegramIcon, TelegramIconProps, TelegramIconVariant, WhatsAppIcon, WhatsAppIconProps, WhatsAppIconVariant, XIcon, XIconProps, XIconVariant, YouTubeIcon, YouTubeIconProps, YouTubeIconVariant } from './icons/index.js';
10
10
  export { WorkflowCard, WorkflowCardProps, WorkflowErrorAlert, WorkflowErrorAlertProps, WorkflowIntegration, WorkflowProgressBar, WorkflowProgressBarProps, WorkflowResultPanel, WorkflowResultPanelProps, WorkflowStatusBadge, WorkflowStatusBadgeProps } from './workflow/index.js';
11
11
  import '@react-types/button';
@@ -298,7 +298,7 @@ interface PlatformConfig {
298
298
  /**
299
299
  * Categories for workflow progress tracking
300
300
  */
301
- type ProgressCategory = 'notion' | 'setup' | 'integration' | 'generation' | 'image' | 'scheduling' | 'completion';
301
+ type ProgressCategory = 'notion' | 'setup' | 'integration' | 'generation' | 'image' | 'scheduling' | 'completion' | 'processing' | 'custom';
302
302
  /**
303
303
  * Configuration for a progress category
304
304
  */
@@ -356,20 +356,22 @@ declare const PLATFORM_CONFIGS: Record<Platform, PlatformConfig>;
356
356
  declare const CATEGORY_CONFIGS: Record<ProgressCategory, CategoryConfig>;
357
357
  /**
358
358
  * Categorize a progress message into a ProgressCategory
359
+ * Handles undefined/null message gracefully
359
360
  */
360
- declare function categorizeProgress(message: string): ProgressCategory;
361
+ declare function categorizeProgress(message: string | undefined | null): ProgressCategory;
361
362
  /**
362
- * Get the icon for a progress category
363
+ * Get the icon for a progress category (with fallback for unknown categories)
363
364
  */
364
365
  declare function getCategoryIcon(category: ProgressCategory): string;
365
366
  /**
366
- * Get the color class for a progress category
367
+ * Get the color class for a progress category (with fallback for unknown categories)
367
368
  */
368
369
  declare function getCategoryColor(category: ProgressCategory): string;
369
370
  /**
370
371
  * Normalize platform name (e.g., 'twitter' -> 'x')
372
+ * Handles undefined/null platform gracefully
371
373
  */
372
- declare function normalizePlatform(platform: string): Platform | null;
374
+ declare function normalizePlatform(platform: string | undefined | null): Platform | null;
373
375
  /**
374
376
  * Calculate character count including hashtags
375
377
  */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { memo, useRef, useEffect, useState, useCallback, useId, useMemo } from 'react';
1
+ import { memo, useRef, useEffect, useMemo, useState, useCallback, useId } from 'react';
2
2
  import styled47, { keyframes, css, createGlobalStyle } from 'styled-components';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useButton } from '@react-aria/button';
@@ -2814,6 +2814,24 @@ function ItemWithLogs({
2814
2814
  isExpanded && /* @__PURE__ */ jsx(ItemDisclosurePanel, { children: run.customContent ? (
2815
2815
  // Render custom content directly
2816
2816
  /* @__PURE__ */ jsx("div", { style: { padding: "16px" }, children: run.customContent })
2817
+ ) : run.logsLoading ? (
2818
+ // Show loading spinner while logs are being fetched
2819
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", padding: "16px" }, children: [
2820
+ /* @__PURE__ */ jsx(
2821
+ "div",
2822
+ {
2823
+ style: {
2824
+ width: "24px",
2825
+ height: "24px",
2826
+ border: `2px solid ${tokens.colors.border.default}`,
2827
+ borderTopColor: tokens.colors.primary,
2828
+ borderRadius: "50%",
2829
+ animation: "spin 1s linear infinite"
2830
+ }
2831
+ }
2832
+ ),
2833
+ /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
2834
+ ] })
2817
2835
  ) : run.logs && run.logs.length > 0 ? /* @__PURE__ */ jsx(DarkNotificationCard, { sections: logSections }) : /* @__PURE__ */ jsx("div", { style: { color: tokens.colors.text.tertiary, fontSize: "13px", padding: "8px" }, children: "No action logs available" }) })
2818
2836
  ] });
2819
2837
  }
@@ -5440,6 +5458,59 @@ var Input2 = ({
5440
5458
  )
5441
5459
  ] }) });
5442
5460
  };
5461
+ function parseMarkdown(text) {
5462
+ if (!text) return [];
5463
+ const elements = [];
5464
+ let key = 0;
5465
+ const paragraphs = text.split(/\n\n+/);
5466
+ paragraphs.forEach((paragraph, pIndex) => {
5467
+ if (pIndex > 0) {
5468
+ elements.push(/* @__PURE__ */ jsx("br", {}, key++));
5469
+ elements.push(/* @__PURE__ */ jsx("br", {}, key++));
5470
+ }
5471
+ const lines = paragraph.split("\n");
5472
+ lines.forEach((line, lineIndex) => {
5473
+ if (lineIndex > 0) {
5474
+ elements.push(/* @__PURE__ */ jsx("br", {}, key++));
5475
+ }
5476
+ const parsed = parseInlineMarkdown(line, key);
5477
+ elements.push(...parsed.elements);
5478
+ key = parsed.nextKey;
5479
+ });
5480
+ });
5481
+ return elements;
5482
+ }
5483
+ function parseInlineMarkdown(text, startKey) {
5484
+ const elements = [];
5485
+ let key = startKey;
5486
+ const inlineRegex = /(\*\*(.+?)\*\*)|(\*(.+?)\*)|(`([^`]+)`)|(\[([^\]]+)\]\(([^)]+)\))/g;
5487
+ let lastIndex = 0;
5488
+ let match;
5489
+ while ((match = inlineRegex.exec(text)) !== null) {
5490
+ if (match.index > lastIndex) {
5491
+ elements.push(text.slice(lastIndex, match.index));
5492
+ }
5493
+ if (match[1]) {
5494
+ elements.push(/* @__PURE__ */ jsx("strong", { children: match[2] }, key++));
5495
+ } else if (match[3]) {
5496
+ elements.push(/* @__PURE__ */ jsx("em", { children: match[4] }, key++));
5497
+ } else if (match[5]) {
5498
+ elements.push(/* @__PURE__ */ jsx("code", { children: match[6] }, key++));
5499
+ } else if (match[7]) {
5500
+ elements.push(
5501
+ /* @__PURE__ */ jsx("a", { href: match[9], target: "_blank", rel: "noopener noreferrer", children: match[8] }, key++)
5502
+ );
5503
+ }
5504
+ lastIndex = match.index + match[0].length;
5505
+ }
5506
+ if (lastIndex < text.length) {
5507
+ elements.push(text.slice(lastIndex));
5508
+ }
5509
+ if (elements.length === 0) {
5510
+ elements.push(text);
5511
+ }
5512
+ return { elements, nextKey: key };
5513
+ }
5443
5514
  var Container12 = styled47.div`
5444
5515
  font-family: ${(props) => props.$variant === "code" ? tokens.typography.fontFamily.monospace : tokens.typography.fontFamily.primary};
5445
5516
  white-space: pre-wrap;
@@ -5448,6 +5519,33 @@ var Container12 = styled47.div`
5448
5519
  /* Performance optimizations for streaming text */
5449
5520
  text-rendering: optimizeSpeed;
5450
5521
  contain: content;
5522
+
5523
+ /* Markdown element styles */
5524
+ strong {
5525
+ font-weight: ${tokens.typography.fontWeight.bold};
5526
+ color: ${tokens.colors.text.primary};
5527
+ }
5528
+
5529
+ em {
5530
+ font-style: italic;
5531
+ }
5532
+
5533
+ code {
5534
+ font-family: ${tokens.typography.fontFamily.monospace};
5535
+ background: ${tokens.colors.surface.overlay};
5536
+ padding: 0.15em 0.4em;
5537
+ border-radius: ${tokens.borderRadius.sm};
5538
+ font-size: 0.9em;
5539
+ }
5540
+
5541
+ a {
5542
+ color: ${tokens.colors.primary};
5543
+ text-decoration: none;
5544
+
5545
+ &:hover {
5546
+ text-decoration: underline;
5547
+ }
5548
+ }
5451
5549
  `;
5452
5550
  var Cursor = styled47.span`
5453
5551
  display: inline-block;
@@ -5492,8 +5590,14 @@ var StreamingTextBase = ({
5492
5590
  wasStreamingRef.current = isStreaming;
5493
5591
  }, [isStreaming, onStreamComplete]);
5494
5592
  const showCursor = isStreaming && cursor;
5593
+ const renderedContent = useMemo(() => {
5594
+ if (variant === "markdown") {
5595
+ return parseMarkdown(content);
5596
+ }
5597
+ return content;
5598
+ }, [content, variant]);
5495
5599
  return /* @__PURE__ */ jsxs(Container12, { $variant: variant, className, children: [
5496
- content,
5600
+ renderedContent,
5497
5601
  showCursor && /* @__PURE__ */ jsx(Cursor, {})
5498
5602
  ] });
5499
5603
  };
@@ -6097,27 +6201,6 @@ var ActionsContainer3 = styled47.div`
6097
6201
  gap: ${tokens.spacing.xs};
6098
6202
  flex-wrap: wrap;
6099
6203
  `;
6100
- var StreamingIndicator2 = styled47.span`
6101
- display: inline-block;
6102
- width: ${tokens.spacing.xs};
6103
- height: ${tokens.spacing.xs};
6104
- margin-left: ${tokens.spacing.xs};
6105
- background-color: ${tokens.colors.text.tertiary};
6106
- border-radius: ${tokens.borderRadius.full};
6107
- animation: pulse 1.5s ease-in-out infinite;
6108
-
6109
- @keyframes pulse {
6110
- 0%,
6111
- 100% {
6112
- opacity: 0.3;
6113
- transform: scale(0.8);
6114
- }
6115
- 50% {
6116
- opacity: 1;
6117
- transform: scale(1.2);
6118
- }
6119
- }
6120
- `;
6121
6204
  var Avatar3 = styled47.img`
6122
6205
  width: ${tokens.spacing.xl};
6123
6206
  height: ${tokens.spacing.xl};
@@ -6133,15 +6216,21 @@ var UserMessage2 = ({
6133
6216
  avatarUrl,
6134
6217
  username,
6135
6218
  isStreaming = false,
6136
- actions = []
6219
+ actions = [],
6220
+ enableMarkdown = true
6137
6221
  }) => {
6138
6222
  return /* @__PURE__ */ jsxs(StyledUserMessage2, { className, children: [
6139
6223
  /* @__PURE__ */ jsxs(MessageBubble, { children: [
6140
6224
  username && /* @__PURE__ */ jsx("strong", { children: username }),
6141
- /* @__PURE__ */ jsxs(MessageContent2, { children: [
6142
- content,
6143
- isStreaming && /* @__PURE__ */ jsx(StreamingIndicator2, {})
6144
- ] }),
6225
+ /* @__PURE__ */ jsx(MessageContent2, { children: /* @__PURE__ */ jsx(
6226
+ StreamingText2,
6227
+ {
6228
+ content,
6229
+ isStreaming,
6230
+ variant: enableMarkdown ? "markdown" : "default",
6231
+ cursor: false
6232
+ }
6233
+ ) }),
6145
6234
  timestamp && /* @__PURE__ */ jsx(MessageTime2, { children: timestamp }),
6146
6235
  actions.length > 0 && /* @__PURE__ */ jsx(ActionsContainer3, { children: actions.map((action, index) => /* @__PURE__ */ jsxs(ActionButton4, { onClick: action.onClick, children: [
6147
6236
  action.icon,
@@ -7855,9 +7944,20 @@ var CATEGORY_CONFIGS = {
7855
7944
  icon: "\u2705",
7856
7945
  label: "Done",
7857
7946
  color: "bg-emerald-500"
7947
+ },
7948
+ processing: {
7949
+ icon: "\u2699\uFE0F",
7950
+ label: "Processing",
7951
+ color: "bg-blue-500"
7952
+ },
7953
+ custom: {
7954
+ icon: "\u{1F527}",
7955
+ label: "Custom",
7956
+ color: "bg-gray-500"
7858
7957
  }
7859
7958
  };
7860
7959
  function categorizeProgress(message) {
7960
+ if (!message) return "setup";
7861
7961
  if (message.includes("Notion")) return "notion";
7862
7962
  if (message.includes("prompts") || message.includes("Loading")) return "setup";
7863
7963
  if (message.includes("accounts") || message.includes("Fetching connected")) return "integration";
@@ -7868,12 +7968,13 @@ function categorizeProgress(message) {
7868
7968
  return "setup";
7869
7969
  }
7870
7970
  function getCategoryIcon(category) {
7871
- return CATEGORY_CONFIGS[category].icon;
7971
+ return CATEGORY_CONFIGS[category]?.icon ?? "\u2699\uFE0F";
7872
7972
  }
7873
7973
  function getCategoryColor(category) {
7874
- return CATEGORY_CONFIGS[category].color;
7974
+ return CATEGORY_CONFIGS[category]?.color ?? "bg-gray-500";
7875
7975
  }
7876
7976
  function normalizePlatform(platform) {
7977
+ if (!platform) return null;
7877
7978
  const normalized = platform.toLowerCase();
7878
7979
  if (normalized === "twitter") return "x";
7879
7980
  if (Object.keys(PLATFORM_CONFIGS).includes(normalized)) {
@@ -9081,7 +9182,12 @@ var TONE_CONFIG = {
9081
9182
  }
9082
9183
  };
9083
9184
  function getToneConfig(tone) {
9084
- return TONE_CONFIG[tone];
9185
+ return TONE_CONFIG[tone] ?? {
9186
+ label: tone || "Unknown",
9187
+ color: tokens.colors.text.secondary,
9188
+ icon: "/assets/icon-set/Icon-chat-1-fill.svg",
9189
+ description: "Unknown tone"
9190
+ };
9085
9191
  }
9086
9192
  ({
9087
9193
  pending: {
@@ -9117,12 +9223,15 @@ var SUBREDDIT_COLORS = {
9117
9223
  startups: tokens.colors.seaGreen
9118
9224
  };
9119
9225
  function getSubredditColor(subreddit) {
9226
+ if (!subreddit) return REDDIT_COLORS.orange;
9120
9227
  const lower = subreddit.toLowerCase();
9121
9228
  return SUBREDDIT_COLORS[lower] || REDDIT_COLORS.orange;
9122
9229
  }
9123
9230
  function formatRelativeTime(timestamp) {
9231
+ if (!timestamp) return "unknown";
9124
9232
  const now = /* @__PURE__ */ new Date();
9125
9233
  const date = new Date(timestamp);
9234
+ if (Number.isNaN(date.getTime())) return "unknown";
9126
9235
  const diffMs = now.getTime() - date.getTime();
9127
9236
  const diffMins = Math.floor(diffMs / 6e4);
9128
9237
  const diffHours = Math.floor(diffMins / 60);
@@ -9134,13 +9243,14 @@ function formatRelativeTime(timestamp) {
9134
9243
  return date.toLocaleDateString();
9135
9244
  }
9136
9245
  function formatNumber(num) {
9246
+ if (num === void 0 || num === null) return "0";
9137
9247
  if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
9138
9248
  if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
9139
9249
  return num.toString();
9140
9250
  }
9141
9251
  function truncateText(text, maxLength) {
9142
9252
  if (text.length <= maxLength) return text;
9143
- return text.slice(0, maxLength - 3) + "...";
9253
+ return `${text.slice(0, maxLength - 3)}...`;
9144
9254
  }
9145
9255
  function getSubredditUrl(subreddit) {
9146
9256
  return `https://www.reddit.com/r/${subreddit}`;