main
html 245 lines 6.45 KB
Raw
1 <html>
2 <head>
3 <script type="module">
4 import { store } from "/components/chat/message-queue/message-queue-store.js";
5 </script>
6 </head>
7 <body>
8 <div x-data>
9 <template x-if="$store.messageQueue?.hasQueue">
10 <div class="queue-preview">
11 <div class="queue-header">
12 <span class="queue-title">
13 Queued Messages (<span x-text="$store.messageQueue.count"></span>)
14 </span>
15 <div class="queue-header-actions">
16 <button class="queue-header-btn" @click="$store.messageQueue.sendAll()" title="Send all">
17 <x-icon name="keyboard_double_arrow_right"></x-icon>
18 </button>
19 <button class="queue-header-btn delete" @click="$store.messageQueue.clearQueue()" title="Clear all">
20 <x-icon name="delete_sweep"></x-icon>
21 </button>
22 </div>
23 </div>
24 <div class="queue-items no-scrollbar">
25 <template x-for="(item, index) in $store.messageQueue.allItems" :key="item.id">
26 <div class="queue-item" :class="{ 'queue-item-pending': item.pending }">
27 <div class="queue-item-content">
28 <template x-if="item.pending">
29 <span class="queue-item-pending-icon"></span>
30 </template>
31 <template x-if="!item.pending">
32 <span class="queue-item-seq" x-text="index + 1"></span>
33 </template>
34 <span class="queue-item-text" x-text="item.text || '(attachment only)'"></span>
35 <template x-if="item.attachments?.length > 0">
36 <div class="queue-attachments">
37 <template x-for="att in item.attachments" :key="att">
38 <img
39 :src="$store.messageQueue.getAttachmentUrl(att)"
40 :alt="att"
41 class="queue-attachment-thumb"
42 :title="att"
43 />
44 </template>
45 </div>
46 </template>
47 </div>
48 <div class="queue-item-actions">
49 <button class="queue-action-btn send" @click="$store.messageQueue.sendItem(item.id)" title="Send now">
50 <x-icon name="send"></x-icon>
51 </button>
52 <button class="queue-action-btn delete" @click="$store.messageQueue.removeItem(item.id)" title="Remove">
53 <x-icon name="close"></x-icon>
54 </button>
55 </div>
56 </div>
57 </template>
58 </div>
59 </div>
60 </template>
61 </div>
62
63 <style>
64 .queue-preview {
65 border: 1px solid var(--color-border);
66 border-radius: 8px;
67 padding: var(--spacing-sm);
68 margin-bottom: var(--spacing-xs);
69 }
70
71 .queue-header {
72 display: flex;
73 justify-content: space-between;
74 align-items: center;
75 margin-bottom: var(--spacing-xs);
76 }
77
78 .queue-title {
79 font-size: var(--font-size-xs);
80 color: var(--color-text-secondary);
81 }
82
83 .queue-header-actions {
84 display: flex;
85 gap: 2px;
86 }
87
88 .queue-header-btn {
89 background: transparent;
90 border: none;
91 color: var(--color-text-secondary);
92 cursor: pointer;
93 padding: 2px;
94 border-radius: 4px;
95 display: flex;
96 align-items: center;
97 opacity: 0.6;
98 transition: opacity 0.1s ease-in-out, color 0.1s ease-in-out,
99 box-shadow 0.1s ease-in-out;
100 }
101
102 .queue-header-btn:hover {
103 opacity: 1;
104 box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 18%, transparent);
105 }
106
107 .queue-header-btn:active {
108 opacity: 0.5;
109 box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.06);
110 }
111
112 .queue-header-btn .material-symbols-outlined {
113 font-size: var(--font-size-large);
114 }
115
116 .queue-items {
117 display: flex;
118 flex-direction: column;
119 gap: 2px;
120 max-height: 120px;
121 overflow-y: auto;
122 }
123
124 .queue-item {
125 display: flex;
126 align-items: center;
127 justify-content: space-between;
128 gap: var(--spacing-sm);
129 padding: 6px 8px;
130 border-radius: 4px;
131 cursor: default;
132 }
133
134 .queue-item:hover {
135 background-color: var(--color-background-hover);
136 }
137
138 .queue-item-content {
139 display: flex;
140 align-items: center;
141 gap: var(--spacing-sm);
142 flex: 1;
143 min-width: 0;
144 }
145
146 .queue-item-seq {
147 display: inline-flex;
148 align-items: center;
149 justify-content: center;
150 width: 1rem;
151 height: 1rem;
152 font-size: 0.5rem;
153 color: var(--color-text-secondary);
154 background: var(--color-background);
155 border-radius: 50%;
156 }
157
158 .queue-item-text {
159 font-size: var(--font-size-small);
160 color: var(--color-text);
161 white-space: nowrap;
162 overflow: hidden;
163 text-overflow: ellipsis;
164 flex: 1;
165 min-width: 0;
166 }
167
168 .queue-attachments {
169 display: flex;
170 gap: 4px;
171 flex-shrink: 0;
172 }
173
174 .queue-attachment-thumb {
175 width: 20px;
176 height: 20px;
177 object-fit: contain;
178 border-radius: 3px;
179 border: 1px solid var(--color-border);
180 }
181
182 .queue-item-actions {
183 display: flex;
184 gap: 2px;
185 flex-shrink: 0;
186 }
187
188 .device-pointer .queue-item-actions {
189 opacity: 0;
190 visibility: hidden;
191 pointer-events: none;
192 transition: opacity 0.15s;
193 }
194
195 .device-pointer .queue-item:hover .queue-item-actions {
196 opacity: 1;
197 visibility: visible;
198 pointer-events: auto;
199 }
200
201 .device-touch .queue-item-actions {
202 opacity: 1;
203 visibility: visible;
204 pointer-events: auto;
205 }
206
207 .queue-action-btn {
208 background: transparent;
209 border: none;
210 color: var(--color-primary);
211 cursor: pointer;
212 padding: 4px;
213 border-radius: 4px;
214 display: flex;
215 align-items: center;
216 }
217
218 .queue-action-btn .material-symbols-outlined {
219 font-size: var(--font-size-smaller);
220 }
221
222 .queue-item-pending {
223 opacity: 0.6;
224 }
225
226 .queue-item-pending .queue-action-btn.send {
227 visibility: hidden !important;
228 pointer-events: none !important;
229 }
230
231 .queue-item-pending-icon {
232 width: 1rem;
233 height: 1rem;
234 border: 2px solid var(--color-border);
235 border-top-color: var(--color-primary);
236 border-radius: 50%;
237 animation: pending-spin 0.8s linear infinite;
238 }
239
240 @keyframes pending-spin {
241 to { transform: rotate(360deg); }
242 }
243 </style>
244 </body>
245 </html>