@nuraly/lumenui 0.2.2 → 0.3.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/cdn.js +1 -0
- package/dist/nuralyui.bundle.js +2467 -1857
- package/dist/nuralyui.bundle.js.gz +0 -0
- package/dist/src/components/canvas/bundle.js +569 -204
- package/dist/src/components/canvas/bundle.js.gz +0 -0
- package/dist/src/components/canvas/canvas.constants.d.ts +1 -1
- package/dist/src/components/canvas/canvas.constants.js +1 -1
- package/dist/src/components/canvas/workflow-canvas.component.d.ts +7 -0
- package/dist/src/components/canvas/workflow-canvas.component.js +46 -2
- package/dist/src/components/canvas/workflow-canvas.types.d.ts +8 -1
- package/dist/src/components/canvas/workflow-canvas.types.js +157 -0
- package/dist/src/components/chat-panel/bundle.js +216 -0
- package/dist/src/components/chat-panel/bundle.js.gz +0 -0
- package/dist/src/components/chat-panel/chat-panel.component.d.ts +85 -0
- package/dist/src/components/chat-panel/chat-panel.component.js +510 -0
- package/dist/src/components/chat-panel/chat-panel.style.d.ts +2 -0
- package/dist/src/components/chat-panel/chat-panel.style.js +127 -0
- package/dist/src/components/chat-panel/chat-panel.types.d.ts +54 -0
- package/dist/src/components/chat-panel/chat-panel.types.js +2 -0
- package/dist/src/components/chat-panel/index.d.ts +3 -0
- package/dist/src/components/chat-panel/index.js +2 -0
- package/dist/src/components/chatbot/bundle.js +1 -1
- package/dist/src/components/chatbot/bundle.js.gz +0 -0
- package/dist/src/components/chatbot/providers/workflow-socket-provider.js +1 -1
- package/dist/src/components/presence/bundle.js +69 -42
- package/dist/src/components/presence/bundle.js.gz +0 -0
- package/dist/src/components/presence/presence-chat.component.d.ts +2 -0
- package/dist/src/components/presence/presence-chat.component.js +12 -2
- package/dist/src/components/presence/presence.component.d.ts +30 -6
- package/dist/src/components/presence/presence.component.js +277 -30
- package/dist/src/components/presence/presence.types.d.ts +2 -0
- package/dist/src/components/toast/bundle.js +11 -9
- package/dist/src/components/toast/bundle.js.gz +0 -0
- package/dist/src/components/toast/toast.component.d.ts +8 -0
- package/dist/src/components/toast/toast.component.js +17 -7
- package/dist/src/components/video/bundle.js +13 -12
- package/dist/src/components/video/bundle.js.gz +0 -0
- package/dist/src/components/video/video.component.d.ts +15 -1
- package/dist/src/components/video/video.component.js +131 -3
- package/package.json +5 -10
- package/packages/common/dist/VERSIONS.md +1 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import{css as t,html as e,LitElement as i,nothing as o}from"lit";import{property as s,state as n,customElement as a}from"lit/decorators.js";const r=t`
|
|
2
|
+
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; }
|
|
3
|
+
|
|
4
|
+
/* Messages */
|
|
5
|
+
.chat-messages { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 2px; }
|
|
6
|
+
.chat-messages::before { content: ''; flex: 1; }
|
|
7
|
+
.date-sep { align-self: center; font-size: 11px; color: var(--text-secondary, #536471); background: var(--input-bg, #f2f2f7); padding: 3px 10px; border-radius: 9999px; margin: 8px 0; }
|
|
8
|
+
.msg-row { display: flex; gap: 6px; align-items: flex-end; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
|
|
9
|
+
.msg-row .msg { -webkit-user-select: text; user-select: text; }
|
|
10
|
+
.msg-row.me { flex-direction: row-reverse; }
|
|
11
|
+
.msg-avatar { width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 600; color: #fff; flex-shrink: 0; margin-bottom: 16px; }
|
|
12
|
+
.msg-sender-name { font-size: 10px; font-weight: 600; padding: 0 4px; margin-bottom: 1px; }
|
|
13
|
+
.msg-bubble { max-width: 75%; }
|
|
14
|
+
.msg { padding: 7px 12px; border-radius: 16px; font-size: 13px; line-height: 1.4; word-wrap: break-word; }
|
|
15
|
+
.msg.me { background: var(--accent, #7c3aed); color: #fff; border-bottom-right-radius: 4px; }
|
|
16
|
+
.msg.them { background: var(--input-bg, #f2f2f7); color: var(--text, #1d1d1f); border-bottom-left-radius: 4px; }
|
|
17
|
+
.msg-footer { display: flex; align-items: center; gap: 4px; margin-top: 1px; padding: 0 4px; }
|
|
18
|
+
.msg-footer.me { justify-content: flex-end; }
|
|
19
|
+
.msg-time { font-size: 10px; color: var(--text-secondary, #536471); }
|
|
20
|
+
.msg-read { display: flex; align-items: center; }
|
|
21
|
+
.msg-read svg { width: 14px; height: 14px; }
|
|
22
|
+
|
|
23
|
+
/* Attachments */
|
|
24
|
+
.msg-attachment { margin-top: 4px; }
|
|
25
|
+
.msg-attachment img { max-width: 200px; border-radius: 8px; cursor: pointer; display: block; }
|
|
26
|
+
.msg-attachment .file-card { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--input-bg, #f2f2f7); border-radius: 10px; font-size: 12px; text-decoration: none; color: var(--text); cursor: pointer; min-width: 160px; }
|
|
27
|
+
.msg-attachment .file-card:hover { background: var(--border, #e5e7eb); }
|
|
28
|
+
|
|
29
|
+
/* Audio player */
|
|
30
|
+
.audio-msg { display: flex; align-items: center; gap: 6px; padding: 6px 10px; background: var(--input-bg, #f2f2f7); border-radius: 16px; min-width: 180px; }
|
|
31
|
+
.audio-play-btn { width: 28px; height: 28px; border-radius: 50%; border: none; background: var(--accent, #7c3aed); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
32
|
+
.audio-bars { flex: 1; display: flex; align-items: center; gap: 1.5px; height: 24px; }
|
|
33
|
+
.a-bar { width: 2.5px; min-height: 3px; border-radius: 1.5px; background: rgba(0,0,0,0.2); transition: background 0.1s; }
|
|
34
|
+
.a-bar.played { background: var(--accent, #7c3aed); }
|
|
35
|
+
.audio-dur { font-size: 10px; color: var(--text-secondary); font-variant-numeric: tabular-nums; flex-shrink: 0; min-width: 28px; text-align: right; }
|
|
36
|
+
.audio-msg audio { display: none; }
|
|
37
|
+
.me .audio-msg { background: rgba(255,255,255,0.15); }
|
|
38
|
+
.me .audio-play-btn { background: #fff; color: var(--accent, #7c3aed); }
|
|
39
|
+
.me .a-bar { background: rgba(255,255,255,0.3); }
|
|
40
|
+
.me .a-bar.played { background: #fff; }
|
|
41
|
+
.me .audio-dur { color: rgba(255,255,255,0.7); }
|
|
42
|
+
|
|
43
|
+
/* Call logs */
|
|
44
|
+
.msg-call-log { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: var(--input-bg, #f2f2f7); border-radius: 10px; min-width: 160px; }
|
|
45
|
+
.msg-call-info { flex: 1; }
|
|
46
|
+
.msg-call-type { font-size: 12px; font-weight: 600; }
|
|
47
|
+
.msg-call-status { font-size: 10px; color: var(--text-secondary); }
|
|
48
|
+
.msg-call-status.missed, .msg-call-status.declined { color: #ef4444; }
|
|
49
|
+
.msg-call-status.completed { color: #22c55e; }
|
|
50
|
+
|
|
51
|
+
/* Reactions */
|
|
52
|
+
.msg-reactions { display: flex; gap: 3px; margin-top: 2px; padding: 0 4px; flex-wrap: wrap; }
|
|
53
|
+
.msg-reactions .reaction { display: inline-flex; align-items: center; gap: 2px; padding: 1px 6px; border-radius: 10px; background: var(--input-bg, #f2f2f7); font-size: 11px; cursor: pointer; border: 1px solid transparent; transition: transform 0.12s; }
|
|
54
|
+
.msg-reactions .reaction:hover { border-color: var(--accent, #7c3aed); transform: scale(1.05); }
|
|
55
|
+
.msg-reactions .reaction.mine { background: color-mix(in srgb, var(--accent, #7c3aed) 15%, transparent); border-color: var(--accent, #7c3aed); }
|
|
56
|
+
.msg-reactions .reaction svg { width: 12px; height: 12px; }
|
|
57
|
+
.msg-reactions .reaction .count { font-size: 10px; color: var(--text-secondary); }
|
|
58
|
+
|
|
59
|
+
/* Reply quote */
|
|
60
|
+
.msg-reply-quote { font-size: 10px; color: var(--text-secondary); padding: 3px 6px; margin-bottom: 2px; border-left: 2px solid var(--accent, #7c3aed); border-radius: 2px; background: rgba(124,58,237,0.05); }
|
|
61
|
+
.reply-bar { display: flex; align-items: center; gap: 6px; padding: 4px 12px; background: var(--input-bg, #f2f2f7); border-left: 3px solid var(--accent, #7c3aed); font-size: 11px; color: var(--text-secondary); }
|
|
62
|
+
.reply-bar .reply-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
63
|
+
.reply-bar button { border: none; background: none; cursor: pointer; color: var(--text-secondary); font-size: 14px; padding: 2px; }
|
|
64
|
+
|
|
65
|
+
/* Edited/deleted/encrypted */
|
|
66
|
+
.msg-edited { font-size: 9px; color: var(--text-secondary); font-style: italic; }
|
|
67
|
+
.msg-deleted { font-style: italic; color: var(--text-secondary); }
|
|
68
|
+
.msg-e2e { font-size: 9px; margin-left: 3px; opacity: 0.6; }
|
|
69
|
+
|
|
70
|
+
/* Inline edit */
|
|
71
|
+
.edit-inline { display: flex; align-items: center; gap: 3px; }
|
|
72
|
+
.edit-input { flex: 1; border: 1px solid var(--accent, #7c3aed); border-radius: 6px; padding: 3px 6px; font-size: 12px; outline: none; background: var(--bg, #fff); color: var(--text); min-width: 80px; }
|
|
73
|
+
.edit-confirm, .edit-cancel { border: none; background: none; cursor: pointer; font-size: 13px; padding: 2px 3px; border-radius: 3px; }
|
|
74
|
+
.edit-confirm { color: #16a34a; }
|
|
75
|
+
.edit-cancel { color: #dc2626; }
|
|
76
|
+
|
|
77
|
+
/* Context menu */
|
|
78
|
+
.ctx-menu { position: fixed; background: var(--bg, #fff); border: 1px solid var(--border, #e5e7eb); border-radius: 12px; padding: 3px 0; box-shadow: 0 6px 24px rgba(0,0,0,0.12); z-index: 20; min-width: 120px; backdrop-filter: blur(12px); }
|
|
79
|
+
.ctx-menu button { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 12px; border: none; background: none; font-size: 12px; color: var(--text); cursor: pointer; text-align: left; }
|
|
80
|
+
.ctx-menu button:hover { background: var(--input-bg); }
|
|
81
|
+
.ctx-menu button.danger { color: #dc2626; }
|
|
82
|
+
.ctx-reactions { display: flex; gap: 3px; padding: 6px 8px; border-bottom: 1px solid var(--border); }
|
|
83
|
+
.ctx-reactions button { width: 30px; height: 30px; padding: 0; min-width: auto; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: transform 0.12s, background 0.12s; }
|
|
84
|
+
.ctx-reactions button:hover { background: var(--input-bg); transform: scale(1.2); }
|
|
85
|
+
.ctx-reactions button svg { width: 16px; height: 16px; }
|
|
86
|
+
|
|
87
|
+
/* Emoji picker */
|
|
88
|
+
.emoji-picker { position: absolute; bottom: 50px; left: 8px; background: var(--bg, #fff); border: 1px solid var(--border); border-radius: 12px; padding: 5px; box-shadow: 0 6px 24px rgba(0,0,0,0.12); z-index: 10; max-height: 200px; overflow-y: auto; }
|
|
89
|
+
.emoji-picker .emoji-quick { display: flex; gap: 3px; padding: 3px 2px 5px; border-bottom: 1px solid var(--border); margin-bottom: 3px; }
|
|
90
|
+
.emoji-picker .emoji-quick button { width: 30px; height: 30px; border: none; background: none; cursor: pointer; border-radius: 50%; display: flex; align-items: center; justify-content: center; padding: 0; }
|
|
91
|
+
.emoji-picker .emoji-quick button:hover { background: var(--input-bg); transform: scale(1.2); }
|
|
92
|
+
.emoji-picker .emoji-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 1px; }
|
|
93
|
+
.emoji-picker .emoji-grid button { width: 28px; height: 28px; border: none; background: none; font-size: 16px; cursor: pointer; border-radius: 5px; display: flex; align-items: center; justify-content: center; }
|
|
94
|
+
.emoji-picker .emoji-grid button:hover { background: var(--input-bg); transform: scale(1.1); }
|
|
95
|
+
|
|
96
|
+
/* Typing indicator */
|
|
97
|
+
.typing-indicator { padding: 3px 12px; font-size: 11px; color: var(--text-secondary); font-style: italic; }
|
|
98
|
+
|
|
99
|
+
/* Input bar */
|
|
100
|
+
.chat-input { display: flex; gap: 6px; padding: 8px 10px; border-top: 1px solid var(--border, #e5e7eb); align-items: center; flex-shrink: 0; position: relative; }
|
|
101
|
+
.chat-input textarea { flex: 1; padding: 8px 12px; border: 1px solid var(--border, #e5e7eb); border-radius: 18px; font-size: 13px; outline: none; background: var(--input-bg, #f2f2f7); resize: none; font-family: inherit; line-height: 1.4; max-height: 80px; overflow-y: auto; }
|
|
102
|
+
.chat-input textarea:focus { border-color: var(--accent, #7c3aed); background: var(--bg, #fff); }
|
|
103
|
+
.input-tools { display: flex; gap: 1px; }
|
|
104
|
+
.input-tools button { width: 30px; height: 30px; border: none; background: none; color: var(--text-secondary); cursor: pointer; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
|
|
105
|
+
.input-tools button:hover { background: var(--input-bg); color: var(--accent); }
|
|
106
|
+
.send-btn { width: 32px; height: 32px; border: none; background: var(--accent, #7c3aed); color: #fff; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
107
|
+
.send-btn:hover { background: var(--accent-hover, #6d28d9); }
|
|
108
|
+
|
|
109
|
+
/* Empty state */
|
|
110
|
+
.empty-state { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--text-secondary); gap: 6px; padding: 24px; }
|
|
111
|
+
.empty-state svg { width: 36px; height: 36px; opacity: 0.4; }
|
|
112
|
+
.empty-state span { font-size: 12px; }
|
|
113
|
+
|
|
114
|
+
/* Loading */
|
|
115
|
+
.loading { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); font-size: 12px; }
|
|
116
|
+
|
|
117
|
+
/* Full-size mode overrides */
|
|
118
|
+
:host(:not([compact])) .msg { font-size: 14px; padding: 8px 14px; border-radius: 18px; }
|
|
119
|
+
:host(:not([compact])) .msg-bubble { max-width: 65%; }
|
|
120
|
+
:host(:not([compact])) .msg-avatar { width: 24px; height: 24px; font-size: 10px; }
|
|
121
|
+
:host(:not([compact])) .chat-messages { padding: 16px; }
|
|
122
|
+
:host(:not([compact])) .chat-input textarea { font-size: 14px; padding: 10px 16px; }
|
|
123
|
+
:host(:not([compact])) .chat-input { padding: 10px 16px; }
|
|
124
|
+
:host(:not([compact])) .send-btn { width: 38px; height: 38px; }
|
|
125
|
+
`
|
|
126
|
+
/**
|
|
127
|
+
* @license
|
|
128
|
+
* Copyright 2024 Nuraly, Laabidi Aymen
|
|
129
|
+
* SPDX-License-Identifier: MIT
|
|
130
|
+
*/;var d=function(t,e,i,o){for(var s,n=arguments.length,a=n<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,i):o,r=t.length-1;r>=0;r--)(s=t[r])&&(a=(n<3?s(a):n>3?s(e,i,a):s(e,i))||a);return n>3&&a&&Object.defineProperty(e,i,a),a};const l=[{key:"👍",label:"Like"},{key:"❤️",label:"Love"},{key:"😂",label:"Haha"},{key:"😮",label:"Wow"},{key:"😢",label:"Sad"},{key:"🔥",label:"Fire"}],c=["😀","😂","🥰","😎","🤔","🎉","✨","🙏","💯","🚀","👏","😍","🤣","💪","😊","🥳","😤","💀","🫡","👀","💜","🤝","⭐","🙌"],p=e`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>`,g=e`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 6 7 17 2 12"/><polyline points="23 6 12 17" opacity="0.5"/></svg>`,u=e`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"><polyline points="18 6 7 17 2 12"/><polyline points="23 6 12 17"/></svg>`,h=e`<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>`,x=e`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>`,m=e`<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>`,v=e`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`,b=e`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`,f=e`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>`,y=e`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>`,w=e`<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>`,k=e`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>`,$=e`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;let j=class extends i{constructor(){super(...arguments),this.messages=[],this.currentUser=null,this.conversationId="",this.isGroup=!1,this.loading=!1,this.compact=!1,this.emptyText="No messages yet",this.hideInput=!1,this._showEmoji=!1,this._contextMenu=null,this._replyTo=null,this._editingIdx=null,this._typing=[],this._typingTimer=null,this._longPressTimer=null,this._touchMoved=!1}updated(t){t.has("messages")&&requestAnimationFrame(()=>{var t;const e=null===(t=this.shadowRoot)||void 0===t?void 0:t.querySelector(".chat-messages");e&&(e.scrollTop=e.scrollHeight)})}addTyping(t){this._typing.includes(t)||(this._typing=[...this._typing,t])}removeTyping(t){this._typing=this._typing.filter(e=>e!==t)}_sendMessage(){var t;const e=null===(t=this.shadowRoot)||void 0===t?void 0:t.querySelector(".chat-input textarea");if(!(null==e?void 0:e.value.trim()))return;const i=e.value.trim();e.value="",e.focus(),this.dispatchEvent(new CustomEvent("message-send",{detail:{text:i,replyTo:this._replyTo||void 0},bubbles:!0,composed:!0})),this._replyTo=null,this._showEmoji=!1,this._playSound()}_onInputKeydown(t){if("Enter"===t.key&&!t.shiftKey)return t.preventDefault(),void this._sendMessage();this.dispatchEvent(new CustomEvent("typing-start",{bubbles:!0,composed:!0})),clearTimeout(this._typingTimer),this._typingTimer=setTimeout(()=>{this.dispatchEvent(new CustomEvent("typing-stop",{bubbles:!0,composed:!0}))},2e3)}_toggleEmoji(){this._showEmoji=!this._showEmoji}_insertEmoji(t){var e;const i=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector(".chat-input textarea");i&&(i.value+=t,i.focus()),this._showEmoji=!1}_menuPos(t){const e=t.closest(".msg-bubble")||t.closest(".msg-row");if(!e)return{y:0,left:0};const i=e.getBoundingClientRect(),o=!!t.closest(".msg-row.me"),s=o?140:80,n=i.top>s+12?i.top-s-4:i.bottom+4;return o?{y:n,right:Math.max(8,window.innerWidth-i.right)}:{y:n,left:Math.max(8,i.left)}}_showContextMenuHandler(t,e){t.preventDefault(),this._contextMenu=Object.assign({msgIdx:e},this._menuPos(t.target))}_onTouchStart(t,e){this._touchMoved=!1,this._longPressTimer=setTimeout(()=>{var i;t.preventDefault(),null===(i=window.getSelection())||void 0===i||i.removeAllRanges(),this._contextMenu=Object.assign({msgIdx:e},this._menuPos(t.target)),this._touchMoved=!0},500)}_onTouchEnd(){clearTimeout(this._longPressTimer)}_onTouchMove(){this._touchMoved=!0,clearTimeout(this._longPressTimer)}_onMsgTap(t,e){if(!window.matchMedia("(max-width: 640px)").matches)return;if(this._touchMoved)return;const i=t.target;i.closest(".reaction")||i.closest(".audio-play-btn")||i.closest(".file-card")||i.closest(".edit-inline")||(t.stopPropagation(),this._contextMenu=Object.assign({msgIdx:e},this._menuPos(i)))}_hideContextMenu(){this._contextMenu=null}_deleteMsg(t){const e=this.messages[t];(null==e?void 0:e.id)&&this.dispatchEvent(new CustomEvent("message-delete",{detail:{messageId:e.id},bubbles:!0,composed:!0})),this._contextMenu=null}_replyMsg(t){var e,i;const o=this.messages[t];o&&(this._replyTo={text:o.text,from:o.from},null===(i=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector(".chat-input textarea"))||void 0===i||i.focus()),this._contextMenu=null}_editMsg(t){this._editingIdx=t,this._contextMenu=null,requestAnimationFrame(()=>{var t;const e=null===(t=this.shadowRoot)||void 0===t?void 0:t.querySelector(".edit-input");e&&(e.focus(),e.select())})}_confirmEdit(t){var e;const i=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector(".edit-input"),o=null==i?void 0:i.value.trim();if(!o)return void(this._editingIdx=null);const s=this.messages[t];(null==s?void 0:s.id)&&this.dispatchEvent(new CustomEvent("message-edit",{detail:{messageId:s.id,content:o},bubbles:!0,composed:!0})),this._editingIdx=null}_onEditKeydown(t,e){"Enter"!==t.key||t.shiftKey?"Escape"===t.key&&(this._editingIdx=null):(t.preventDefault(),this._confirmEdit(e))}_reactMsg(t,e){const i=this.messages[t];(null==i?void 0:i.id)&&this.dispatchEvent(new CustomEvent("message-react",{detail:{messageId:i.id,emoji:e},bubbles:!0,composed:!0})),this._contextMenu=null}_toggleAudioPlay(t){var e;const i=t.currentTarget.closest(".audio-msg"),o=null==i?void 0:i.querySelector("audio");o&&(o.paused?(null===(e=this.shadowRoot)||void 0===e||e.querySelectorAll(".audio-msg audio").forEach(t=>{t===o||t.paused||(t.pause(),t.currentTime=0)}),o.play().catch(()=>{})):o.pause())}_onAudioTimeUpdate(t){const e=t.target,i=e.closest(".audio-msg");if(!i||!e.duration)return;const o=i.querySelectorAll(".a-bar"),s=e.currentTime/e.duration,n=Math.floor(s*o.length);o.forEach((t,e)=>t.classList.toggle("played",e<=n))}_onAudioEnded(t){const e=t.target.closest(".audio-msg");null==e||e.querySelectorAll(".a-bar").forEach(t=>t.classList.remove("played"))}_pickFile(t=!1){const e=document.createElement("input");e.type="file",t&&(e.accept="image/jpeg,image/png,image/gif,image/webp,image/heic,image/heif"),e.style.cssText="position:fixed;top:-9999px;left:-9999px;opacity:0",document.body.appendChild(e),e.onchange=()=>{var i;const o=null===(i=e.files)||void 0===i?void 0:i[0];o&&this.dispatchEvent(new CustomEvent("file-select",{detail:{file:o,imageOnly:t},bubbles:!0,composed:!0}));try{document.body.removeChild(e)}catch(t){}},e.click()}_playSound(){try{const t=new AudioContext,e=t.createGain();e.connect(t.destination),e.gain.value=.12;const i=t.createOscillator();i.connect(e),i.type="sine",i.frequency.setValueAtTime(600,t.currentTime),i.frequency.setValueAtTime(900,t.currentTime+.08),e.gain.exponentialRampToValueAtTime(.001,t.currentTime+.15),i.start(t.currentTime),i.stop(t.currentTime+.15),setTimeout(()=>t.close(),500)}catch(t){}}render(){return this.loading?e`<div class="loading">Loading...</div>`:e`
|
|
131
|
+
${0===this.messages.length?e`<div class="empty-state">${w}<span>${this.emptyText}</span></div>`:e`
|
|
132
|
+
<div class="chat-messages" @click=${()=>this._hideContextMenu()} @touchstart=${()=>{this._contextMenu&&this._hideContextMenu()}}>
|
|
133
|
+
${this.messages.map((t,e,i)=>this._renderMessage(t,e,i))}
|
|
134
|
+
</div>
|
|
135
|
+
`}
|
|
136
|
+
|
|
137
|
+
${this._contextMenu?this._renderContextMenu():o}
|
|
138
|
+
${this._typing.length>0?e`<div class="typing-indicator">${this._typing.join(", ")} typing...</div>`:o}
|
|
139
|
+
${this._replyTo?e`
|
|
140
|
+
<div class="reply-bar">
|
|
141
|
+
<span class="reply-text">Replying to: ${this._replyTo.text}</span>
|
|
142
|
+
<button @click=${()=>{this._replyTo=null}}>✕</button>
|
|
143
|
+
</div>
|
|
144
|
+
`:o}
|
|
145
|
+
${this._showEmoji?this._renderEmojiPicker():o}
|
|
146
|
+
${this.hideInput?o:this._renderInputBar()}
|
|
147
|
+
`}_renderMessage(t,i,s){var n;if("date"===t.from)return e`<div class="date-sep">${t.text}</div>`;if(t.deleted)return e`<div class="msg-row system"><div class="msg-bubble"><div class="msg them msg-deleted">This message was deleted</div></div></div>`;const a=i>0?s[i-1]:null,r=this.isGroup&&"them"===t.from&&(!a||"them"!==a.from||a.senderId!==t.senderId);return e`
|
|
148
|
+
<div class="msg-row ${t.from}"
|
|
149
|
+
@contextmenu=${t=>this._showContextMenuHandler(t,i)}
|
|
150
|
+
@touchstart=${t=>this._onTouchStart(t,i)}
|
|
151
|
+
@touchend=${()=>this._onTouchEnd()}
|
|
152
|
+
@touchmove=${()=>this._onTouchMove()}
|
|
153
|
+
@click=${t=>this._onMsgTap(t,i)}>
|
|
154
|
+
${"them"===t.from?e`<div class="msg-avatar" style="background:${t.senderColor||"#7c3aed"}">${(t.senderInitials||"?").charAt(0)}</div>`:o}
|
|
155
|
+
<div class="msg-bubble" @dblclick=${"me"!==t.from||t.deleted?void 0:()=>this._editMsg(i)}>
|
|
156
|
+
${r?e`<div class="msg-sender-name" style="color:${t.senderColor||"#7c3aed"}">${t.senderName}</div>`:o}
|
|
157
|
+
${t.replyTo?e`<div class="msg-reply-quote">${t.replyTo.text}</div>`:o}
|
|
158
|
+
${this._renderAttachment(t)}
|
|
159
|
+
${this._editingIdx===i?e`<div class="edit-inline"><input class="edit-input" type="text" .value=${t.text||""} @keydown=${t=>this._onEditKeydown(t,i)}><button class="edit-confirm" @click=${()=>this._confirmEdit(i)}>✓</button><button class="edit-cancel" @click=${()=>{this._editingIdx=null}}>✕</button></div>`:t.text?e`<div class="msg ${t.from}">${t.text}${t.edited?e` <span class="msg-edited">(edited)</span>`:o}${t.encrypted?e`<span class="msg-e2e" title="End-to-end encrypted">🔒</span>`:o}</div>`:o}
|
|
160
|
+
<div class="msg-footer ${t.from}">
|
|
161
|
+
<span class="msg-time">${t.time||""}</span>
|
|
162
|
+
${"me"===t.from?e`<span class="msg-read">${"read"===t.status?u:"delivered"===t.status?g:p}</span>`:o}
|
|
163
|
+
</div>
|
|
164
|
+
${(null===(n=t.reactions)||void 0===n?void 0:n.length)?e`
|
|
165
|
+
<div class="msg-reactions">
|
|
166
|
+
${t.reactions.map(t=>{var o;return e`
|
|
167
|
+
<span class="reaction ${(null===(o=t.users)||void 0===o?void 0:o.includes("me"))?"mine":""}" @click=${()=>this._reactMsg(i,t.emoji)}>${t.emoji} <span class="count">${t.count}</span></span>
|
|
168
|
+
`})}
|
|
169
|
+
</div>
|
|
170
|
+
`:o}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
`}_renderAttachment(t){var i;const s=t.attachment;if(!s)return o;if("call"===s.type){const t="video"===s.callType?y:f,i="missed"===s.callStatus||"declined"===s.callStatus?"#ef4444":"#22c55e";return e`<div class="msg-call-log">
|
|
174
|
+
<span style="color:${i}">${t}</span>
|
|
175
|
+
<div class="msg-call-info">
|
|
176
|
+
<div class="msg-call-type">${"video"===s.callType?"Video call":"Voice call"}</div>
|
|
177
|
+
<div class="msg-call-status ${s.callStatus}">${"completed"===s.callStatus?s.duration||"Ended":"declined"===s.callStatus?"Declined":"Missed"}</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>`}if("image"===s.type)return e`<div class="msg-attachment"><img src="${s.decryptedUrl||s.url}" alt="${s.name||"Image"}"></div>`;if("audio"===s.type){const t=(null===(i=s.waveform)||void 0===i?void 0:i.length)?s.waveform:Array(36).fill(.15);return e`<div class="msg-attachment"><div class="audio-msg">
|
|
180
|
+
<button class="audio-play-btn" @click=${t=>this._toggleAudioPlay(t)}>${m}</button>
|
|
181
|
+
<div class="audio-bars">${t.map(t=>e`<div class="a-bar" style="height:${Math.round(22*t+3)}px"></div>`)}</div>
|
|
182
|
+
<span class="audio-dur">${s.duration||"0:00"}</span>
|
|
183
|
+
<audio src="${s.decryptedUrl||s.url}" preload="metadata" @timeupdate=${t=>this._onAudioTimeUpdate(t)} @ended=${t=>this._onAudioEnded(t)}></audio>
|
|
184
|
+
</div></div>`}return e`<div class="msg-attachment"><a class="file-card" href="${s.decryptedUrl||s.url}" target="_blank" download="${s.name||"file"}">
|
|
185
|
+
${v}
|
|
186
|
+
<div style="flex:1;min-width:0"><div style="font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${s.name||"File"}</div>${s.fileSize?e`<div style="font-size:10px;color:var(--text-secondary);margin-top:1px">${(s.fileSize/1024).toFixed(0)} KB</div>`:o}</div>
|
|
187
|
+
${b}
|
|
188
|
+
</a></div>`}_renderContextMenu(){const t=this._contextMenu,i=this.messages[t.msgIdx],s="me"===(null==i?void 0:i.from);return e`
|
|
189
|
+
<div class="ctx-menu" @mousedown=${t=>t.preventDefault()} @touchstart=${t=>t.stopPropagation()} style="top:${t.y}px;${null!=t.right?`right:${t.right}px`:`left:${t.left}px`}">
|
|
190
|
+
<div class="ctx-reactions">
|
|
191
|
+
${l.map(i=>e`<button @click=${()=>this._reactMsg(t.msgIdx,i.key)} title=${i.label}>${i.key}</button>`)}
|
|
192
|
+
</div>
|
|
193
|
+
<button @click=${()=>this._replyMsg(t.msgIdx)}>↩ Reply</button>
|
|
194
|
+
${s&&!(null==i?void 0:i.deleted)?e`<button @click=${()=>this._editMsg(t.msgIdx)}>✏️ Edit</button>`:o}
|
|
195
|
+
${s&&!(null==i?void 0:i.deleted)?e`<button class="danger" @click=${()=>this._deleteMsg(t.msgIdx)}>🗑 Delete</button>`:o}
|
|
196
|
+
</div>
|
|
197
|
+
`}_renderEmojiPicker(){return e`
|
|
198
|
+
<div class="emoji-picker" @mousedown=${t=>t.preventDefault()}>
|
|
199
|
+
<div class="emoji-quick">
|
|
200
|
+
${l.map(t=>e`<button @click=${()=>this._insertEmoji(t.key)} title=${t.label}>${t.key}</button>`)}
|
|
201
|
+
</div>
|
|
202
|
+
<div class="emoji-grid">
|
|
203
|
+
${c.map(t=>e`<button @click=${()=>this._insertEmoji(t)}>${t}</button>`)}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
`}_renderInputBar(){return e`
|
|
207
|
+
<div class="chat-input">
|
|
208
|
+
<div class="input-tools">
|
|
209
|
+
<button @click=${()=>this._pickFile()} title="Attach file">${k}</button>
|
|
210
|
+
<button @click=${()=>this._pickFile(!0)} title="Send image">${$}</button>
|
|
211
|
+
<button @click=${()=>this._toggleEmoji()} title="Emoji">${x}</button>
|
|
212
|
+
</div>
|
|
213
|
+
<textarea rows="1" placeholder="Type a message..." @keydown=${t=>this._onInputKeydown(t)}></textarea>
|
|
214
|
+
<button class="send-btn" @mousedown=${t=>t.preventDefault()} @click=${()=>this._sendMessage()} title="Send">${h}</button>
|
|
215
|
+
</div>
|
|
216
|
+
`}};j.styles=r,d([s({type:Array})],j.prototype,"messages",void 0),d([s({type:Object})],j.prototype,"currentUser",void 0),d([s({type:String,attribute:"conversation-id"})],j.prototype,"conversationId",void 0),d([s({type:Boolean})],j.prototype,"isGroup",void 0),d([s({type:Boolean})],j.prototype,"loading",void 0),d([s({type:Boolean,reflect:!0})],j.prototype,"compact",void 0),d([s({type:String,attribute:"empty-text"})],j.prototype,"emptyText",void 0),d([s({type:Boolean,attribute:"hide-input"})],j.prototype,"hideInput",void 0),d([n()],j.prototype,"_showEmoji",void 0),d([n()],j.prototype,"_contextMenu",void 0),d([n()],j.prototype,"_replyTo",void 0),d([n()],j.prototype,"_editingIdx",void 0),d([n()],j.prototype,"_typing",void 0),j=d([a("nr-chat-panel")],j);export{j as NrChatPanelElement};
|
|
Binary file
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2024 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { LitElement } from 'lit';
|
|
7
|
+
import type { ChatMessage, ChatUser } from './chat-panel.types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Shared chat panel — renders messages, input bar, reactions, context menu.
|
|
10
|
+
* Used inside both the floating presence chat and the full messages page.
|
|
11
|
+
*
|
|
12
|
+
* @fires message-send - User sends a message. Detail: `{ text, replyTo? }`
|
|
13
|
+
* @fires message-edit - User edits a message. Detail: `{ messageId, content }`
|
|
14
|
+
* @fires message-delete - User deletes a message. Detail: `{ messageId }`
|
|
15
|
+
* @fires message-react - User reacts to a message. Detail: `{ messageId, emoji }`
|
|
16
|
+
* @fires typing-start
|
|
17
|
+
* @fires typing-stop
|
|
18
|
+
* @fires file-select - User selected a file. Detail: `{ file: File, imageOnly: boolean }`
|
|
19
|
+
*/
|
|
20
|
+
export declare class NrChatPanelElement extends LitElement {
|
|
21
|
+
static styles: import("lit").CSSResult;
|
|
22
|
+
/** Messages to display */
|
|
23
|
+
messages: ChatMessage[];
|
|
24
|
+
/** Current user info */
|
|
25
|
+
currentUser: ChatUser | null;
|
|
26
|
+
/** Conversation ID */
|
|
27
|
+
conversationId: string;
|
|
28
|
+
/** Whether this is a group conversation */
|
|
29
|
+
isGroup: boolean;
|
|
30
|
+
/** Loading state */
|
|
31
|
+
loading: boolean;
|
|
32
|
+
/** Compact mode for floating panels */
|
|
33
|
+
compact: boolean;
|
|
34
|
+
/** Empty state text */
|
|
35
|
+
emptyText: string;
|
|
36
|
+
/** Hide the built-in input bar (for pages that provide their own) */
|
|
37
|
+
hideInput: boolean;
|
|
38
|
+
private _showEmoji;
|
|
39
|
+
private _contextMenu;
|
|
40
|
+
private _replyTo;
|
|
41
|
+
private _editingIdx;
|
|
42
|
+
private _typing;
|
|
43
|
+
private _typingTimer;
|
|
44
|
+
private _longPressTimer;
|
|
45
|
+
private _touchMoved;
|
|
46
|
+
updated(changed: Map<string, unknown>): void;
|
|
47
|
+
/** Add a typing indicator */
|
|
48
|
+
addTyping(userName: string): void;
|
|
49
|
+
/** Remove a typing indicator */
|
|
50
|
+
removeTyping(userName: string): void;
|
|
51
|
+
private _sendMessage;
|
|
52
|
+
private _onInputKeydown;
|
|
53
|
+
private _toggleEmoji;
|
|
54
|
+
private _insertEmoji;
|
|
55
|
+
private _menuPos;
|
|
56
|
+
private _showContextMenuHandler;
|
|
57
|
+
private _onTouchStart;
|
|
58
|
+
private _onTouchEnd;
|
|
59
|
+
private _onTouchMove;
|
|
60
|
+
private _onMsgTap;
|
|
61
|
+
private _hideContextMenu;
|
|
62
|
+
private _deleteMsg;
|
|
63
|
+
private _replyMsg;
|
|
64
|
+
private _editMsg;
|
|
65
|
+
private _confirmEdit;
|
|
66
|
+
private _onEditKeydown;
|
|
67
|
+
private _reactMsg;
|
|
68
|
+
private _toggleAudioPlay;
|
|
69
|
+
private _onAudioTimeUpdate;
|
|
70
|
+
private _onAudioEnded;
|
|
71
|
+
private _pickFile;
|
|
72
|
+
private _playSound;
|
|
73
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
74
|
+
private _renderMessage;
|
|
75
|
+
private _renderAttachment;
|
|
76
|
+
private _renderContextMenu;
|
|
77
|
+
private _renderEmojiPicker;
|
|
78
|
+
private _renderInputBar;
|
|
79
|
+
}
|
|
80
|
+
declare global {
|
|
81
|
+
interface HTMLElementTagNameMap {
|
|
82
|
+
'nr-chat-panel': NrChatPanelElement;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=chat-panel.component.d.ts.map
|