@ezychat/web 0.0.0-stage → 0.1.38
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-lib/assets/font-1be3448e292f.woff2 +0 -0
- package/dist-lib/assets/font-3100e775e861.woff2 +0 -0
- package/dist-lib/assets/font-34b9c504cab7.woff2 +0 -0
- package/dist-lib/assets/font-5c66f9e07e90.woff2 +0 -0
- package/dist-lib/assets/font-6e9e020a25f9.woff2 +0 -0
- package/dist-lib/assets/font-71d5ee93cc1e.woff2 +0 -0
- package/dist-lib/assets/font-ca157063339a.woff2 +0 -0
- package/dist-lib/chunks/admin-BPgvUcQO.js +602 -0
- package/dist-lib/chunks/admin-B_hIZ1Sp.js +603 -0
- package/dist-lib/chunks/app-C2A7ZZsu.js +97 -0
- package/dist-lib/chunks/app-RX8Fy2Oz.js +97 -0
- package/dist-lib/chunks/auth-Cif_yktU.js +127 -0
- package/dist-lib/chunks/auth-DJBQd10l.js +127 -0
- package/dist-lib/chunks/common-BwLWsyiW.js +80 -0
- package/dist-lib/chunks/common-CDytP7HK.js +80 -0
- package/dist-lib/chunks/errors-BIyy5VTn.js +39 -0
- package/dist-lib/chunks/errors-CEweqDjm.js +39 -0
- package/dist-lib/chunks/inbox-IiNeuwOm.js +487 -0
- package/dist-lib/chunks/inbox-VvClcCZP.js +487 -0
- package/dist-lib/chunks/registerSW-Gn8zt2XY.js +16 -0
- package/dist-lib/ezychat-web.css +2 -0
- package/dist-lib/index.js +16732 -0
- package/dist-lib/public/apple-touch-icon.png +0 -0
- package/dist-lib/public/brand/ezychat-logo.png +0 -0
- package/dist-lib/public/favicon.ico +0 -0
- package/dist-lib/public/icon-192.png +0 -0
- package/dist-lib/public/icon-512.png +0 -0
- package/dist-lib/public/icon-maskable-512.png +0 -0
- package/dist-lib/public/illustrations/empty-inbox.png +0 -0
- package/dist-lib/public/illustrations/link-whatsapp.png +0 -0
- package/dist-lib/public/illustrations/no-results.png +0 -0
- package/dist-lib/public/illustrations/tunnel.png +0 -0
- package/dist-lib/public/illustrations/welcome.png +0 -0
- package/dist-lib/public/share-app.svg +16 -0
- package/dist-lib/sw.ts +71 -0
- package/dist-lib/types/App.d.ts +2 -0
- package/dist-lib/types/access/AccessGate.d.ts +24 -0
- package/dist-lib/types/access/BannerSlot.d.ts +35 -0
- package/dist-lib/types/access/events.d.ts +16 -0
- package/dist-lib/types/admin/AdminLayout.d.ts +36 -0
- package/dist-lib/types/admin/AiConnectionBanner.d.ts +7 -0
- package/dist-lib/types/admin/AiConnectionSection.d.ts +3 -0
- package/dist-lib/types/admin/AiContextPanel.d.ts +11 -0
- package/dist-lib/types/admin/AiEditDialog.d.ts +13 -0
- package/dist-lib/types/admin/AiMemberPage.d.ts +1 -0
- package/dist-lib/types/admin/AiQuickConnect.d.ts +22 -0
- package/dist-lib/types/admin/AiTryIt.d.ts +8 -0
- package/dist-lib/types/admin/AiVoiceSection.d.ts +2 -0
- package/dist-lib/types/admin/AuditPage.d.ts +1 -0
- package/dist-lib/types/admin/CloudflareSetup.d.ts +5 -0
- package/dist-lib/types/admin/CreateTokenDialog.d.ts +13 -0
- package/dist-lib/types/admin/CustomerEnrichmentSection.d.ts +1 -0
- package/dist-lib/types/admin/EnrichmentAuditScore.d.ts +4 -0
- package/dist-lib/types/admin/IntegrationsSection.d.ts +7 -0
- package/dist-lib/types/admin/MembersPage.d.ts +2 -0
- package/dist-lib/types/admin/QuickRepliesPage.d.ts +2 -0
- package/dist-lib/types/admin/ResolveAllChatsCard.d.ts +1 -0
- package/dist-lib/types/admin/SettingsPage.d.ts +2 -0
- package/dist-lib/types/admin/TokenConnectSteps.d.ts +12 -0
- package/dist-lib/types/admin/TunnelPage.d.ts +1 -0
- package/dist-lib/types/admin/WhatsAppPage.d.ts +11 -0
- package/dist-lib/types/admin/adminUi.d.ts +62 -0
- package/dist-lib/types/admin/ai-status.d.ts +12 -0
- package/dist-lib/types/admin/audit-actions.d.ts +10 -0
- package/dist-lib/types/admin/line-diff.d.ts +26 -0
- package/dist-lib/types/admin/use-chatgpt-sign-in.d.ts +14 -0
- package/dist-lib/types/api/ai.d.ts +174 -0
- package/dist-lib/types/api/client.d.ts +22 -0
- package/dist-lib/types/api/enrichment.d.ts +8 -0
- package/dist-lib/types/api/queries.d.ts +664 -0
- package/dist-lib/types/api/resume-reconnect.d.ts +15 -0
- package/dist-lib/types/api/socket.d.ts +19 -0
- package/dist-lib/types/api/voice.d.ts +33 -0
- package/dist-lib/types/auth/AuthProvider.d.ts +15 -0
- package/dist-lib/types/auth/AuthShell.d.ts +28 -0
- package/dist-lib/types/auth/ChangePasswordPage.d.ts +2 -0
- package/dist-lib/types/auth/LoginPage.d.ts +2 -0
- package/dist-lib/types/auth/RequireAuth.d.ts +12 -0
- package/dist-lib/types/auth/UserMenu.d.ts +5 -0
- package/dist-lib/types/components/app/AboutDialog.d.ts +7 -0
- package/dist-lib/types/components/app/AppCredits.d.ts +8 -0
- package/dist-lib/types/components/app/DesktopUpdates.d.ts +3 -0
- package/dist-lib/types/components/app/DevBuildBadge.d.ts +5 -0
- package/dist-lib/types/components/app/ErrorBoundary.d.ts +23 -0
- package/dist-lib/types/components/app/FeatureRequestAction.d.ts +7 -0
- package/dist-lib/types/components/app/SearchField.d.ts +7 -0
- package/dist-lib/types/components/app/SegmentedControl.d.ts +19 -0
- package/dist-lib/types/components/app/ShareAppAction.d.ts +5 -0
- package/dist-lib/types/components/app/index.d.ts +81 -0
- package/dist-lib/types/components/ui/alert-dialog.d.ts +18 -0
- package/dist-lib/types/components/ui/avatar.d.ts +11 -0
- package/dist-lib/types/components/ui/badge.d.ts +9 -0
- package/dist-lib/types/components/ui/button.d.ts +10 -0
- package/dist-lib/types/components/ui/card.d.ts +9 -0
- package/dist-lib/types/components/ui/checkbox.d.ts +4 -0
- package/dist-lib/types/components/ui/command.d.ts +18 -0
- package/dist-lib/types/components/ui/dialog.d.ts +17 -0
- package/dist-lib/types/components/ui/drawer.d.ts +13 -0
- package/dist-lib/types/components/ui/dropdown-menu.d.ts +25 -0
- package/dist-lib/types/components/ui/input.d.ts +3 -0
- package/dist-lib/types/components/ui/label.d.ts +4 -0
- package/dist-lib/types/components/ui/popover.d.ts +10 -0
- package/dist-lib/types/components/ui/progress.d.ts +4 -0
- package/dist-lib/types/components/ui/radio-group.d.ts +5 -0
- package/dist-lib/types/components/ui/scroll-area.d.ts +5 -0
- package/dist-lib/types/components/ui/select.d.ts +15 -0
- package/dist-lib/types/components/ui/separator.d.ts +4 -0
- package/dist-lib/types/components/ui/sheet.d.ts +14 -0
- package/dist-lib/types/components/ui/skeleton.d.ts +2 -0
- package/dist-lib/types/components/ui/sonner.d.ts +4 -0
- package/dist-lib/types/components/ui/switch.d.ts +6 -0
- package/dist-lib/types/components/ui/table.d.ts +10 -0
- package/dist-lib/types/components/ui/tabs.d.ts +11 -0
- package/dist-lib/types/components/ui/textarea.d.ts +3 -0
- package/dist-lib/types/components/ui/tooltip.d.ts +7 -0
- package/dist-lib/types/create-app.d.ts +20 -0
- package/dist-lib/types/extensions/context.d.ts +8 -0
- package/dist-lib/types/extensions/i18n.d.ts +15 -0
- package/dist-lib/types/extensions/index.d.ts +4 -0
- package/dist-lib/types/extensions/routes.d.ts +12 -0
- package/dist-lib/types/extensions/types.d.ts +116 -0
- package/dist-lib/types/i18n/LanguageSelect.d.ts +4 -0
- package/dist-lib/types/i18n/LocaleSync.d.ts +2 -0
- package/dist-lib/types/i18n/date-locale.d.ts +5 -0
- package/dist-lib/types/i18n/index.d.ts +17 -0
- package/dist-lib/types/i18n/locale-store.d.ts +26 -0
- package/dist-lib/types/i18n/resources.d.ts +1711 -0
- package/dist-lib/types/i18n/use-change-locale.d.ts +9 -0
- package/dist-lib/types/inbox/ChatAvatar.d.ts +13 -0
- package/dist-lib/types/inbox/ChatFilters.d.ts +6 -0
- package/dist-lib/types/inbox/ChatList.d.ts +9 -0
- package/dist-lib/types/inbox/ChatListItem.d.ts +13 -0
- package/dist-lib/types/inbox/Composer.d.ts +30 -0
- package/dist-lib/types/inbox/Conversation.d.ts +7 -0
- package/dist-lib/types/inbox/ConversationHeader.d.ts +25 -0
- package/dist-lib/types/inbox/CustomerPanel.d.ts +32 -0
- package/dist-lib/types/inbox/EmojiPicker.d.ts +6 -0
- package/dist-lib/types/inbox/EventItem.d.ts +14 -0
- package/dist-lib/types/inbox/GettingStartedCard.d.ts +10 -0
- package/dist-lib/types/inbox/GroupPanel.d.ts +19 -0
- package/dist-lib/types/inbox/InboxPage.d.ts +2 -0
- package/dist-lib/types/inbox/MediaView.d.ts +8 -0
- package/dist-lib/types/inbox/MessageBubble.d.ts +31 -0
- package/dist-lib/types/inbox/MessageList.d.ts +21 -0
- package/dist-lib/types/inbox/NotesPanel.d.ts +15 -0
- package/dist-lib/types/inbox/PanelHeader.d.ts +13 -0
- package/dist-lib/types/inbox/ProfileImageContext.d.ts +5 -0
- package/dist-lib/types/inbox/QuickReplyPicker.d.ts +18 -0
- package/dist-lib/types/inbox/ReconnectBanner.d.ts +8 -0
- package/dist-lib/types/inbox/TagInput.d.ts +14 -0
- package/dist-lib/types/inbox/TypingIndicator.d.ts +6 -0
- package/dist-lib/types/inbox/VoiceNote.d.ts +36 -0
- package/dist-lib/types/inbox/WaBanner.d.ts +9 -0
- package/dist-lib/types/inbox/chat-title.d.ts +4 -0
- package/dist-lib/types/inbox/navigation-guard.d.ts +17 -0
- package/dist-lib/types/inbox/timeline.d.ts +26 -0
- package/dist-lib/types/inbox/useCanonicalChatRedirect.d.ts +2 -0
- package/dist-lib/types/inbox/useDirectory.d.ts +21 -0
- package/dist-lib/types/inbox/useSwipeReply.d.ts +11 -0
- package/dist-lib/types/index.d.ts +43 -0
- package/dist-lib/types/lib/ai-handoff.d.ts +4 -0
- package/dist-lib/types/lib/app-links.d.ts +9 -0
- package/dist-lib/types/lib/desktop-updates.d.ts +15 -0
- package/dist-lib/types/lib/error-reporter.d.ts +6 -0
- package/dist-lib/types/lib/format.d.ts +20 -0
- package/dist-lib/types/lib/jid.d.ts +15 -0
- package/dist-lib/types/lib/links.d.ts +40 -0
- package/dist-lib/types/lib/share-app.d.ts +18 -0
- package/dist-lib/types/lib/theme.d.ts +29 -0
- package/dist-lib/types/lib/use-media-query.d.ts +2 -0
- package/dist-lib/types/lib/utils.d.ts +2 -0
- package/dist-lib/types/lib/version.d.ts +12 -0
- package/dist-lib/types/lib/voice-recorder.d.ts +26 -0
- package/dist-lib/types/pwa/InstallHint.d.ts +9 -0
- package/dist-lib/types/pwa/PushToggle.d.ts +20 -0
- package/dist-lib/types/pwa/desktop-notifications.d.ts +18 -0
- package/dist-lib/types/pwa/push.d.ts +13 -0
- package/dist-lib/types/setup/AiAssistantStep.d.ts +9 -0
- package/dist-lib/types/setup/SetupWizard.d.ts +3 -0
- package/dist-lib/types/setup/WaLinkStep.d.ts +8 -0
- package/dist-lib/types/setup/agent-name.d.ts +8 -0
- package/dist-lib/types/setup/connected-label.d.ts +7 -0
- package/dist-lib/types/setup/username.d.ts +9 -0
- package/dist-lib/types/wa/PhoneLink.d.ts +4 -0
- package/package.json +81 -4
- package/README.md +0 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Locale } from '@ezychat/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Current language plus a setter that applies it here and, once it has loaded, saves it to the
|
|
4
|
+
* account when signed in.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useChangeLocale(): {
|
|
7
|
+
locale: "en" | "ms" | "zh-CN";
|
|
8
|
+
changeLocale: (next: Locale) => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const SIZES: {
|
|
2
|
+
readonly sm: "size-8 text-xs";
|
|
3
|
+
readonly md: "size-10 text-sm";
|
|
4
|
+
readonly lg: "size-12 text-base";
|
|
5
|
+
};
|
|
6
|
+
export declare function ChatAvatar({ name, src, seed, size, className, }: {
|
|
7
|
+
name: string;
|
|
8
|
+
src?: string | null;
|
|
9
|
+
seed?: string;
|
|
10
|
+
size?: keyof typeof SIZES;
|
|
11
|
+
className?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ChatFilters } from '../api/queries';
|
|
2
|
+
import type { Directory } from './useDirectory';
|
|
3
|
+
export interface ChatListProps {
|
|
4
|
+
filters: ChatFilters;
|
|
5
|
+
activeJid: string | null;
|
|
6
|
+
directory: Directory;
|
|
7
|
+
onResetFilters?(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function ChatList({ filters, activeJid, directory, onResetFilters }: ChatListProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Chat } from '@ezychat/shared';
|
|
2
|
+
export interface ChatListItemProps {
|
|
3
|
+
chat: Chat;
|
|
4
|
+
active: boolean;
|
|
5
|
+
/** Assignee display name ("You" for the current user), or null when unassigned. */
|
|
6
|
+
assigneeName: string | null;
|
|
7
|
+
/** The current user owns this chat: the chip gets the accent outline. */
|
|
8
|
+
assignedToMe?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function GroupIcon({ className }: {
|
|
11
|
+
className?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
export declare function ChatListItem({ chat, active, assigneeName, assignedToMe, }: ChatListItemProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { QuickReply } from '@ezychat/shared';
|
|
3
|
+
export interface ComposerProps {
|
|
4
|
+
quickReplies: QuickReply[];
|
|
5
|
+
onSend(text: string): void;
|
|
6
|
+
/** Attach button: file + caption (current text, if any). */
|
|
7
|
+
onAttach(file: File, caption?: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Mic button (shown while the box is empty): a recorded Ogg/Opus voice note with the clientId it
|
|
10
|
+
* keeps across retries. Resolve once it is uploaded; reject to keep it in Review.
|
|
11
|
+
*/
|
|
12
|
+
onVoiceNote?(file: File, clientId: string): void | Promise<void>;
|
|
13
|
+
/** Called while typing; throttled to once per 2s. */
|
|
14
|
+
onTyping?(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Called before every send/attach. Resolve `false` to cancel (the text is kept).
|
|
17
|
+
* Used for the "Assigned to X — reply anyway?" confirmation.
|
|
18
|
+
*/
|
|
19
|
+
confirmSend?(): boolean | Promise<boolean>;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
/** The message being replied to: shown in a bar above the box until sent or cancelled. */
|
|
23
|
+
replyTo?: {
|
|
24
|
+
id: string;
|
|
25
|
+
author: string;
|
|
26
|
+
preview: string;
|
|
27
|
+
} | null;
|
|
28
|
+
onCancelReply?(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function Composer({ quickReplies, onSend, onAttach, onVoiceNote, onTyping, confirmSend, disabled, placeholder, replyTo, onCancelReply, }: ComposerProps): React.JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Chat } from '@ezychat/shared';
|
|
2
|
+
import type { Directory } from './useDirectory';
|
|
3
|
+
export interface ConversationHeaderProps {
|
|
4
|
+
chat: Chat;
|
|
5
|
+
directory: Directory;
|
|
6
|
+
onBack(): void;
|
|
7
|
+
onAssign(userId: number | null): void;
|
|
8
|
+
onToggleStatus(): void;
|
|
9
|
+
notesOpen: boolean;
|
|
10
|
+
notesCount: number;
|
|
11
|
+
onToggleNotes(): void;
|
|
12
|
+
/** Direct chats only: the Customer details button. */
|
|
13
|
+
showCustomer: boolean;
|
|
14
|
+
customerOpen: boolean;
|
|
15
|
+
onToggleCustomer(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Clicking the chat's avatar and name opens its details, like WhatsApp: Group info for a group,
|
|
18
|
+
* Customer details for a direct chat.
|
|
19
|
+
*/
|
|
20
|
+
onOpenInfo?(): void;
|
|
21
|
+
/** Group chats: WhatsApp's member count, shown under the name once known. */
|
|
22
|
+
memberCount?: number | null;
|
|
23
|
+
busy?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function ConversationHeader({ chat, directory, onBack, onAssign, onToggleStatus, notesOpen, notesCount, onToggleNotes, showCustomer, customerOpen, onToggleCustomer, onOpenInfo, memberCount, busy, }: ConversationHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { type CustomerProfile, type CustomerProfileResponse } from '@ezychat/shared';
|
|
3
|
+
import type { Directory } from './useDirectory';
|
|
4
|
+
export interface CustomerPanelProps {
|
|
5
|
+
jid: string;
|
|
6
|
+
open: boolean;
|
|
7
|
+
directory: Directory;
|
|
8
|
+
onClose(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Filled by the panel while editing: run an action (close, switch panels) now, or after the
|
|
11
|
+
* teammate confirms discarding unsaved changes. The conversation header buttons use it.
|
|
12
|
+
*/
|
|
13
|
+
guardRef?: React.RefObject<((next: () => void) => void) | null>;
|
|
14
|
+
}
|
|
15
|
+
type WhatsappFactsData = NonNullable<CustomerProfileResponse['whatsapp']>;
|
|
16
|
+
/** Read-only view of a customer profile (also used in the group sender popover). */
|
|
17
|
+
export declare function CustomerDetails({ profile, whatsappName, whatsappPhone, whatsapp, directory, }: {
|
|
18
|
+
profile: CustomerProfile;
|
|
19
|
+
whatsappName: string | null;
|
|
20
|
+
/** Digits WhatsApp gave us (no `+`), when known. */
|
|
21
|
+
whatsappPhone?: string | null;
|
|
22
|
+
/** Read-only WhatsApp facts; replaces the one-line summary when the server sends them. */
|
|
23
|
+
whatsapp?: WhatsappFactsData;
|
|
24
|
+
/** For "Updated by <teammate>"; that line is left out without it. */
|
|
25
|
+
directory?: Directory;
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* Customer profile of a direct chat, editable by every teammate. Side panel on >= lg,
|
|
29
|
+
* full-height Sheet below (same layout as Notes).
|
|
30
|
+
*/
|
|
31
|
+
export declare function CustomerPanel({ open, onClose, guardRef: outerGuard, ...rest }: CustomerPanelProps): React.JSX.Element | null;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ChatEvent, type Note } from '@ezychat/shared';
|
|
2
|
+
import type { Directory } from './useDirectory';
|
|
3
|
+
/** Localized sentence for a chat event, in the active UI language. */
|
|
4
|
+
export declare function describeEvent(e: ChatEvent, dir: Directory): string;
|
|
5
|
+
/** Centered chip for assignment / status events. */
|
|
6
|
+
export declare function EventItem({ event, directory }: {
|
|
7
|
+
event: ChatEvent;
|
|
8
|
+
directory: Directory;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
/** Amber internal-note card shown inline in the timeline (never looks like a bubble). */
|
|
11
|
+
export declare function NoteItem({ note, directory }: {
|
|
12
|
+
note: Note;
|
|
13
|
+
directory: Directory;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Per-browser memory of the × on the card. Storage can be blocked, so every access is guarded. */
|
|
2
|
+
export declare const GETTING_STARTED_HIDDEN_KEY = "ezychat.getting-started-hidden";
|
|
3
|
+
/**
|
|
4
|
+
* A small "Getting started" card on the empty inbox, for admins only. Lists what is left
|
|
5
|
+
* (AI assistant, teammates, phone access), links each to its admin page and hides finished items;
|
|
6
|
+
* the whole card disappears once everything is done or the admin dismisses it.
|
|
7
|
+
*/
|
|
8
|
+
export declare function GettingStartedCard({ className }: {
|
|
9
|
+
className?: string;
|
|
10
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import type { GroupMember } from '@ezychat/shared';
|
|
4
|
+
/** Rows rendered at a time; large groups show more on request instead of rendering all at once. */
|
|
5
|
+
export declare const MEMBER_PAGE = 100;
|
|
6
|
+
export interface GroupPanelProps {
|
|
7
|
+
jid: string;
|
|
8
|
+
open: boolean;
|
|
9
|
+
onClose(): void;
|
|
10
|
+
}
|
|
11
|
+
type T = ReturnType<typeof useTranslation<'inbox'>>['t'];
|
|
12
|
+
/** What the team can call a member: our name for them, else their number, else their WhatsApp ID. */
|
|
13
|
+
export declare function memberLabel(m: GroupMember, t: T): string;
|
|
14
|
+
/**
|
|
15
|
+
* Group info: the members of a WhatsApp group (read from WhatsApp, view-only), with search, an
|
|
16
|
+
* admins filter and per-member details. Side panel on >= lg, full-height Sheet below.
|
|
17
|
+
*/
|
|
18
|
+
export declare function GroupPanel({ jid, open, onClose }: GroupPanelProps): React.JSX.Element | null;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Message } from '@ezychat/shared';
|
|
2
|
+
export interface MediaViewProps {
|
|
3
|
+
message: Message;
|
|
4
|
+
/** Fired when an image/video finishes loading (lets the list keep its scroll anchored). */
|
|
5
|
+
onLoad?(): void;
|
|
6
|
+
}
|
|
7
|
+
/** Renders the media part of a message (image, sticker, video, audio, document). */
|
|
8
|
+
export declare function MediaView({ message: m, onLoad }: MediaViewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { TFunction } from 'i18next';
|
|
3
|
+
import type { Message } from '@ezychat/shared';
|
|
4
|
+
import type { Directory } from './useDirectory';
|
|
5
|
+
export interface MessageBubbleProps {
|
|
6
|
+
message: Message;
|
|
7
|
+
/** Show the sender name above inbound bubbles (group chats). */
|
|
8
|
+
showSender: boolean;
|
|
9
|
+
/** Outbound label: agent display name, or "via phone" when sent from the phone. */
|
|
10
|
+
outboundLabel: string | null;
|
|
11
|
+
quoted: Message | null;
|
|
12
|
+
onRetry?(m: Message): void;
|
|
13
|
+
retrying?: boolean;
|
|
14
|
+
/** Start a reply that quotes this message (hover button, swipe right or long-press on touch). */
|
|
15
|
+
onReply?(m: Message): void;
|
|
16
|
+
onMediaLoad?(): void;
|
|
17
|
+
/** Team directory, for "Updated by" in a group sender's profile popover. */
|
|
18
|
+
directory?: Directory;
|
|
19
|
+
/** Direct chats: the customer's name as the header shows it (used in quotes). */
|
|
20
|
+
contactName?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Who wrote a quoted message. In a direct chat the customer is named like the chat header
|
|
24
|
+
* (`contactName`), not by their WhatsApp profile name; groups name each sender.
|
|
25
|
+
*/
|
|
26
|
+
export declare function quotedAuthor(q: Message, t: TFunction<'inbox'>, contactName?: string): string;
|
|
27
|
+
export declare function quotedPreview(q: Message, t: TFunction<'inbox'>): string;
|
|
28
|
+
export declare function StatusTicks({ status }: {
|
|
29
|
+
status: Message['status'];
|
|
30
|
+
}): React.JSX.Element;
|
|
31
|
+
export declare function MessageBubble({ message: m, showSender, outboundLabel, quoted, onRetry, retrying, onMediaLoad, directory, onReply, contactName, }: MessageBubbleProps): React.JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { Message } from '@ezychat/shared';
|
|
3
|
+
import type { TimelineItem } from './timeline';
|
|
4
|
+
import type { Directory } from './useDirectory';
|
|
5
|
+
export interface MessageListProps {
|
|
6
|
+
items: TimelineItem[];
|
|
7
|
+
messagesById: Map<string, Message>;
|
|
8
|
+
isGroup: boolean;
|
|
9
|
+
directory: Directory;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
hasOlder: boolean;
|
|
12
|
+
loadingOlder: boolean;
|
|
13
|
+
onLoadOlder(): void;
|
|
14
|
+
onRetry(m: Message): void;
|
|
15
|
+
retryingId: string | null;
|
|
16
|
+
onReply?(m: Message): void;
|
|
17
|
+
/** Direct chats: the customer's display name, for quotes. */
|
|
18
|
+
contactName?: string;
|
|
19
|
+
footer?: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
export declare function MessageList({ items, messagesById, isGroup, directory, loading, hasOlder, loadingOlder, onLoadOlder, onRetry, retryingId, onReply, contactName, footer, }: MessageListProps): React.JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { Note } from '@ezychat/shared';
|
|
3
|
+
import type { Directory } from './useDirectory';
|
|
4
|
+
export interface NotesPanelProps {
|
|
5
|
+
jid: string;
|
|
6
|
+
open: boolean;
|
|
7
|
+
notes: Note[];
|
|
8
|
+
loading: boolean;
|
|
9
|
+
directory: Directory;
|
|
10
|
+
onClose(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Internal notes (never sent to the customer). Side panel on >= lg, full-height Sheet below.
|
|
14
|
+
*/
|
|
15
|
+
export declare function NotesPanel({ open, onClose, ...rest }: NotesPanelProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Header of the conversation side panels (Customer, Notes): icon + title, optional action, Close,
|
|
4
|
+
* and the one-line description inside the same bordered block. Panels use `px-4` throughout.
|
|
5
|
+
*/
|
|
6
|
+
export declare function PanelHeader({ icon, title, description, action, closeLabel, onClose, }: {
|
|
7
|
+
icon: React.ReactNode;
|
|
8
|
+
title: React.ReactNode;
|
|
9
|
+
description: React.ReactNode;
|
|
10
|
+
action?: React.ReactNode;
|
|
11
|
+
closeLabel: string;
|
|
12
|
+
onClose(): void;
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { QuickReply } from '@ezychat/shared';
|
|
2
|
+
/** Quick replies whose shortcut starts with `query` (case-insensitive), sorted by shortcut. */
|
|
3
|
+
export declare function filterQuickReplies(replies: QuickReply[], query: string): QuickReply[];
|
|
4
|
+
export interface QuickReplyPickerProps {
|
|
5
|
+
replies: QuickReply[];
|
|
6
|
+
/** Text typed after the leading `/`. */
|
|
7
|
+
query: string;
|
|
8
|
+
/** Index into the filtered list. */
|
|
9
|
+
activeIndex: number;
|
|
10
|
+
onPick(reply: QuickReply): void;
|
|
11
|
+
onHover?(index: number): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* `/shortcut` picker built on cmdk. Filtering and keyboard handling (↑/↓/Enter/Tab/Escape) live
|
|
15
|
+
* in the Composer, which keeps focus in the textarea and drives `activeIndex`; cmdk renders the
|
|
16
|
+
* listbox/option semantics and the highlighted row.
|
|
17
|
+
*/
|
|
18
|
+
export declare function QuickReplyPicker({ replies, query, activeIndex, onPick, onHover, }: QuickReplyPickerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shown while the realtime (Socket.IO) link to the server is down after having been up once,
|
|
3
|
+
* so agents know the inbox may be stale. Pure on `connected` (exported for tests).
|
|
4
|
+
*/
|
|
5
|
+
export declare function ReconnectBannerView({ connected }: {
|
|
6
|
+
connected: boolean;
|
|
7
|
+
}): import("react").JSX.Element | null;
|
|
8
|
+
export declare function ReconnectBanner(): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export interface TagInputProps {
|
|
3
|
+
value: string[];
|
|
4
|
+
onChange(tags: string[]): void;
|
|
5
|
+
/** Existing tags to offer; filtered by prefix and minus the chosen ones. */
|
|
6
|
+
suggestions?: readonly string[];
|
|
7
|
+
/** Called with the text being typed, e.g. to fetch suggestions for it. */
|
|
8
|
+
onQueryChange?(q: string): void;
|
|
9
|
+
id?: string;
|
|
10
|
+
'aria-describedby'?: string;
|
|
11
|
+
'aria-invalid'?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Free-text tags: Enter or comma adds, Backspace on an empty input removes the last one. */
|
|
14
|
+
export declare function TagInput({ value, onChange, suggestions, onQueryChange, id, ...aria }: TagInputProps): React.JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TypingEntry } from '../api/socket';
|
|
2
|
+
/** "Alice is typing…" for other agents working on the same chat. */
|
|
3
|
+
export declare function TypingIndicator({ entries, meId, }: {
|
|
4
|
+
entries: TypingEntry[];
|
|
5
|
+
meId: number | null;
|
|
6
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type VoiceStartError } from '../lib/voice-recorder';
|
|
2
|
+
/** `lost`: the recording could not be finished (the encoder failed), so there is nothing to send. */
|
|
3
|
+
export type VoicePhase = 'idle' | 'starting' | 'recording' | 'review' | 'lost' | VoiceStartError;
|
|
4
|
+
export declare const formatClock: (s: number) => string;
|
|
5
|
+
interface Review {
|
|
6
|
+
/** One id per recording: every send attempt of it uses this clientId, so retries never duplicate. */
|
|
7
|
+
clientId: string;
|
|
8
|
+
blob: Blob;
|
|
9
|
+
url: string;
|
|
10
|
+
seconds: number;
|
|
11
|
+
bars: number[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Record → (review) → send a voice note. `send` gets the Ogg/Opus file and resolves `true` once it
|
|
15
|
+
* accepted it; until then (a declined confirmation, a disabled composer) the recording is kept.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useVoiceNote(send: (file: File, clientId: string) => boolean | Promise<boolean>): {
|
|
18
|
+
phase: VoicePhase;
|
|
19
|
+
seconds: number;
|
|
20
|
+
liveBars: number[];
|
|
21
|
+
review: Review | null;
|
|
22
|
+
sending: boolean;
|
|
23
|
+
sendFailed: boolean;
|
|
24
|
+
start: () => Promise<void>;
|
|
25
|
+
toReview: () => Promise<void>;
|
|
26
|
+
sendNow: () => Promise<void>;
|
|
27
|
+
sendReviewed: () => Promise<void>;
|
|
28
|
+
discard: () => void;
|
|
29
|
+
dismiss: () => void;
|
|
30
|
+
};
|
|
31
|
+
export type VoiceNoteState = ReturnType<typeof useVoiceNote>;
|
|
32
|
+
/** The composer row while a voice note is being recorded, reviewed, or could not start. */
|
|
33
|
+
export declare function VoiceNoteBar({ voice }: Readonly<{
|
|
34
|
+
voice: VoiceNoteState;
|
|
35
|
+
}>): import("react").JSX.Element;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WaStatus } from '@ezychat/shared';
|
|
2
|
+
type WaStateName = WaStatus['state'];
|
|
3
|
+
/** States where outgoing messages are queued and will go out once reconnected. */
|
|
4
|
+
export declare function waQueuesMessages(state: WaStateName | undefined): boolean;
|
|
5
|
+
/** States where sending is pointless until an admin acts (composer disabled). */
|
|
6
|
+
export declare function waSendingBlocked(state: WaStateName | undefined): boolean;
|
|
7
|
+
/** Top-of-inbox banner shown whenever the WhatsApp connection isn't open. */
|
|
8
|
+
export declare function WaBanner(): import("react").JSX.Element | null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TFunction } from 'i18next';
|
|
2
|
+
import type { Chat } from '@ezychat/shared';
|
|
3
|
+
/** Chat title: its name, else +phone, else a translated placeholder. Never WhatsApp ID digits. */
|
|
4
|
+
export declare function chatTitle(chat: Pick<Chat, 'name' | 'phone' | 'type'>, t: TFunction<'inbox'>): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { type NavigateOptions } from 'react-router-dom';
|
|
3
|
+
/** Runs `next` now, or after the teammate confirms leaving (e.g. discarding unsaved edits). */
|
|
4
|
+
export type NavigationGuard = (next: () => void) => void;
|
|
5
|
+
type GuardRef = React.RefObject<NavigationGuard | null>;
|
|
6
|
+
/** Holds the guard of the open conversation for the whole inbox page. */
|
|
7
|
+
export declare function NavigationGuardProvider({ children }: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
/** The page's guard slot, or a private one outside a provider (tests, standalone use). */
|
|
11
|
+
export declare function useNavigationGuardRef(): GuardRef;
|
|
12
|
+
/**
|
|
13
|
+
* `onClick` for an in-app `<Link>`: a plain click goes through the guard first; modified clicks
|
|
14
|
+
* (new tab / window) are left to the browser.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useGuardedLinkClick(to: string, options?: NavigateOptions): (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ChatEvent, Message, Note } from '@ezychat/shared';
|
|
2
|
+
export type TimelineItem = {
|
|
3
|
+
kind: 'day';
|
|
4
|
+
key: string; /** Local midnight (epoch ms) of that day. */
|
|
5
|
+
date: number;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'message';
|
|
8
|
+
key: string;
|
|
9
|
+
at: number;
|
|
10
|
+
message: Message;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'event';
|
|
13
|
+
key: string;
|
|
14
|
+
at: number;
|
|
15
|
+
event: ChatEvent;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'note';
|
|
18
|
+
key: string;
|
|
19
|
+
at: number;
|
|
20
|
+
note: Note;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Merge messages, chat events and notes into one ascending timeline, inserting a
|
|
24
|
+
* `{ kind: 'day' }` separator before the first item of every local calendar day.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildTimeline(messages: Message[], events: ChatEvent[], notes: Note[]): TimelineItem[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { User } from '@ezychat/shared';
|
|
2
|
+
import { type DirectoryUser } from '../api/queries';
|
|
3
|
+
export interface Directory {
|
|
4
|
+
me: User | null;
|
|
5
|
+
isAdmin: boolean;
|
|
6
|
+
byId: Map<number, DirectoryUser>;
|
|
7
|
+
/** Users that can be picked as assignee (active only). */
|
|
8
|
+
assignable: DirectoryUser[];
|
|
9
|
+
/** Display name for a user id ("You" for the current user when `youLabel`). */
|
|
10
|
+
nameOf(id: number | null | undefined, opts?: {
|
|
11
|
+
youLabel?: boolean;
|
|
12
|
+
}): string | null;
|
|
13
|
+
}
|
|
14
|
+
/** Pure builder (exported for tests). */
|
|
15
|
+
export declare function buildDirectory(me: User | null, isAdmin: boolean, list: readonly DirectoryUser[] | undefined): Directory;
|
|
16
|
+
/**
|
|
17
|
+
* Team member lookup for the inbox. Admins use the full `GET /api/users`; every other role
|
|
18
|
+
* uses the public `GET /api/users/directory` ({id, displayName, role, disabled}). Ids that are
|
|
19
|
+
* still unknown render as "Agent #<id>".
|
|
20
|
+
*/
|
|
21
|
+
export declare function useDirectory(): Directory;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/** Swipe distance (px) that starts a reply, and the furthest the bubble follows the finger. */
|
|
3
|
+
export declare const SWIPE_TRIGGER_PX = 56;
|
|
4
|
+
/**
|
|
5
|
+
* Touch gestures that start a reply, like WhatsApp: swipe the message right, or press and hold.
|
|
6
|
+
* Mouse and pen are ignored (desktop uses the Reply button). Vertical movement keeps scrolling.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useSwipeReply(onReply: (() => void) | undefined): {
|
|
9
|
+
offset: number;
|
|
10
|
+
handlers: React.HTMLAttributes<HTMLElement>;
|
|
11
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public web API (`@ezychat/web`): everything another product build needs to add UI to the inbox
|
|
3
|
+
* without forking Lite or deep-importing its modules.
|
|
4
|
+
*/
|
|
5
|
+
export { createInboxApp, type CreateInboxAppOptions } from './create-app';
|
|
6
|
+
export { EXTENSION_NAME_PATTERN, WEB_EXTENSION_ADMIN_SECTIONS, WebExtensionError, extensionKeyPrefix, extensionPath, useExtensionTranslation, validateWebExtensions, type ExtensionTranslate, type WebExtension, type WebExtensionAdminSection, type WebExtensionAgent, type WebExtensionAgentSection, type WebExtensionCatalogs, type WebExtensionLockScreenProps, type WebExtensionNavBadge, type WebExtensionNavItem, type WebExtensionRoute, type WebExtensionSettingsSection, } from './extensions';
|
|
7
|
+
export { useAppLinks } from './lib/app-links';
|
|
8
|
+
export { LITE_APP_LINKS, type AppLinks } from './lib/links';
|
|
9
|
+
export { PausedScreen } from './access/AccessGate';
|
|
10
|
+
export { SlimBanner } from './access/BannerSlot';
|
|
11
|
+
export { AppMark, AuthShell } from './auth/AuthShell';
|
|
12
|
+
export { useTranslation, Trans } from 'react-i18next';
|
|
13
|
+
export { useAuth, type AuthValue } from './auth/AuthProvider';
|
|
14
|
+
export { api, ApiError, errorMessage } from './api/client';
|
|
15
|
+
export { cn } from './lib/utils';
|
|
16
|
+
export { Banner, EmptyState, PageHeader, ResponsiveDialog, ResponsiveTable, SegmentedControl, StatusDot, type Column, type SegmentedOption, type StatusTone, } from './components/app';
|
|
17
|
+
export { ErrorBoundary } from './components/app/ErrorBoundary';
|
|
18
|
+
export * from './components/ui/alert-dialog';
|
|
19
|
+
export * from './components/ui/avatar';
|
|
20
|
+
export * from './components/ui/badge';
|
|
21
|
+
export * from './components/ui/button';
|
|
22
|
+
export * from './components/ui/card';
|
|
23
|
+
export * from './components/ui/checkbox';
|
|
24
|
+
export * from './components/ui/command';
|
|
25
|
+
export * from './components/ui/dialog';
|
|
26
|
+
export * from './components/ui/drawer';
|
|
27
|
+
export * from './components/ui/dropdown-menu';
|
|
28
|
+
export * from './components/ui/input';
|
|
29
|
+
export * from './components/ui/label';
|
|
30
|
+
export * from './components/ui/popover';
|
|
31
|
+
export * from './components/ui/progress';
|
|
32
|
+
export * from './components/ui/radio-group';
|
|
33
|
+
export * from './components/ui/scroll-area';
|
|
34
|
+
export * from './components/ui/select';
|
|
35
|
+
export * from './components/ui/separator';
|
|
36
|
+
export * from './components/ui/sheet';
|
|
37
|
+
export * from './components/ui/skeleton';
|
|
38
|
+
export * from './components/ui/sonner';
|
|
39
|
+
export * from './components/ui/switch';
|
|
40
|
+
export * from './components/ui/table';
|
|
41
|
+
export * from './components/ui/tabs';
|
|
42
|
+
export * from './components/ui/textarea';
|
|
43
|
+
export * from './components/ui/tooltip';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AiHandoffReason } from '@ezychat/shared';
|
|
2
|
+
/** Short, localized reason why the AI handed a chat to the team (chat events and Try it). */
|
|
3
|
+
export declare function handoffReasonLabel(reason: AiHandoffReason): string;
|
|
4
|
+
export declare function isHandoffReason(value: unknown): value is AiHandoffReason;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type AppLinks } from './links';
|
|
3
|
+
/** Supplies the product's outside links (`createInboxApp({ links })`); Lite's links otherwise. */
|
|
4
|
+
export declare function AppLinksProvider({ links, children }: {
|
|
5
|
+
links: AppLinks;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
/** The product's outside links. A `null` link is hidden by the component that shows it. */
|
|
9
|
+
export declare function useAppLinks(): AppLinks;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DesktopUpdatesBridge, DesktopUpdateState } from '@ezychat/shared';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
watiUpdates?: DesktopUpdatesBridge;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Electron supplies this bridge; browser and phone clients never check GitHub. */
|
|
8
|
+
export declare function useDesktopUpdates(): {
|
|
9
|
+
state: DesktopUpdateState | null;
|
|
10
|
+
error: string | null;
|
|
11
|
+
check: () => Promise<void>;
|
|
12
|
+
open: (action: "download" | "release") => Promise<void>;
|
|
13
|
+
install: () => Promise<void>;
|
|
14
|
+
cancelDownload: () => Promise<void>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ClientErrorBody } from '@ezychat/shared';
|
|
2
|
+
export declare function reportClientError(e: Omit<ClientErrorBody, 'route' | 'appVersion'> & {
|
|
3
|
+
route?: string;
|
|
4
|
+
}): void;
|
|
5
|
+
/** Global handlers for uncaught errors and unhandled promise rejections. Call once at startup. */
|
|
6
|
+
export declare function installGlobalErrorReporting(): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Short list-style time: "14:05" today, "Yesterday", "3 Oct" this year, else "3 Oct 2025". */
|
|
2
|
+
export declare function formatListTime(ts: number | null | undefined, now?: Date): string;
|
|
3
|
+
export declare function formatTime(ts: number): string;
|
|
4
|
+
/** Date only, always with the year: "9 Oct 2026". */
|
|
5
|
+
export declare function formatDate(ts: number): string;
|
|
6
|
+
export declare function formatDateTime(ts: number): string;
|
|
7
|
+
/** Day separator label: "Today", "Yesterday", or "Monday 3 October 2026". */
|
|
8
|
+
export declare function formatDay(ts: number, now?: Date): string;
|
|
9
|
+
/** "5 minutes ago" in the active language; "just now" for the last minute. */
|
|
10
|
+
export declare function formatRelative(ts: number): string;
|
|
11
|
+
/** Number in the active UI language, e.g. "1,234" / "1.234". */
|
|
12
|
+
export declare function formatNumber(n: number, options?: Intl.NumberFormatOptions): string;
|
|
13
|
+
export declare function formatBytes(n: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* Up to two initials from a display name, letters only: "(kingsly) Oyen Jarvis" → "KJ",
|
|
16
|
+
* "陈伟明 Chen Wei Ming" → "陈". Empty when the name has no letters (a phone number, a WhatsApp
|
|
17
|
+
* ID, emoji only): callers show a person icon instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare function initials(name: string): string;
|
|
20
|
+
export declare function truncate(s: string, max: number): string;
|