@nuraly/lumenui 0.3.5 → 0.3.7

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.
Files changed (86) hide show
  1. package/dist/nuralyui.bundle.js +2577 -1486
  2. package/dist/nuralyui.bundle.js.gz +0 -0
  3. package/dist/src/components/button/bundle.js +130 -39
  4. package/dist/src/components/button/bundle.js.gz +0 -0
  5. package/dist/src/components/button/button.component.js +7 -4
  6. package/dist/src/components/button/button.style.js +92 -2
  7. package/dist/src/components/canvas/base-canvas.component.d.ts +8 -0
  8. package/dist/src/components/canvas/base-canvas.component.js +75 -3
  9. package/dist/src/components/canvas/bundle.js +2540 -1201
  10. package/dist/src/components/canvas/bundle.js.gz +0 -0
  11. package/dist/src/components/canvas/controllers/collaboration.controller.d.ts +24 -11
  12. package/dist/src/components/canvas/controllers/collaboration.controller.js +176 -120
  13. package/dist/src/components/canvas/controllers/selection.controller.js +20 -0
  14. package/dist/src/components/canvas/interfaces/collaboration.interface.d.ts +8 -0
  15. package/dist/src/components/canvas/templates/index.d.ts +1 -0
  16. package/dist/src/components/canvas/templates/index.js +2 -0
  17. package/dist/src/components/canvas/templates/lock-overlay.template.d.ts +20 -0
  18. package/dist/src/components/canvas/templates/lock-overlay.template.js +33 -0
  19. package/dist/src/components/canvas/workflow-canvas.component.js +52 -24
  20. package/dist/src/components/canvas/workflow-canvas.style.js +62 -1
  21. package/dist/src/components/canvas/workflow-canvas.types.js +50 -4
  22. package/dist/src/components/chat-panel/bundle.js +10 -10
  23. package/dist/src/components/chat-panel/bundle.js.gz +0 -0
  24. package/dist/src/components/chat-panel/chat-panel.component.js +8 -8
  25. package/dist/src/components/chatbot/bundle.js +400 -242
  26. package/dist/src/components/chatbot/bundle.js.gz +0 -0
  27. package/dist/src/components/chatbot/chatbot.style.js +156 -22
  28. package/dist/src/components/chatbot/chatbot.types.d.ts +1 -0
  29. package/dist/src/components/chatbot/core/chatbot-core.controller.js +13 -7
  30. package/dist/src/components/chatbot/plugins/artifact-plugin.js +0 -19
  31. package/dist/src/components/chatbot/plugins/flight-card-plugin.js +0 -35
  32. package/dist/src/components/chatbot/plugins/markdown-plugin.js +0 -4
  33. package/dist/src/components/chatbot/plugins/print-job-card-plugin.js +0 -36
  34. package/dist/src/components/chatbot/plugins/selection-card-plugin.js +0 -34
  35. package/dist/src/components/chatbot/providers/workflow-socket-provider.js +1 -2
  36. package/dist/src/components/chatbot/templates/input-box.template.js +58 -30
  37. package/dist/src/components/chatbot/templates/message.template.js +41 -31
  38. package/dist/src/components/chatbot/templates/thread-sidebar.template.js +38 -39
  39. package/dist/src/components/colorpicker/bundle.js +15 -10
  40. package/dist/src/components/colorpicker/bundle.js.gz +0 -0
  41. package/dist/src/components/colorpicker/color-picker.component.js +15 -10
  42. package/dist/src/components/datepicker/bundle.js +10 -10
  43. package/dist/src/components/datepicker/bundle.js.gz +0 -0
  44. package/dist/src/components/datepicker/datepicker.component.js +14 -22
  45. package/dist/src/components/dropdown/bundle.js +15 -14
  46. package/dist/src/components/dropdown/bundle.js.gz +0 -0
  47. package/dist/src/components/dropdown/dropdown.component.js +10 -9
  48. package/dist/src/components/dropdown/dropdown.style.js +2 -2
  49. package/dist/src/components/file-upload/bundle.js +15 -14
  50. package/dist/src/components/file-upload/bundle.js.gz +0 -0
  51. package/dist/src/components/file-upload/file-upload.component.js +15 -14
  52. package/dist/src/components/icon/bundle.js +7 -7
  53. package/dist/src/components/icon/bundle.js.gz +0 -0
  54. package/dist/src/components/icon/icon-paths.js +15 -0
  55. package/dist/src/components/iconpicker/bundle.js +216 -124
  56. package/dist/src/components/iconpicker/bundle.js.gz +0 -0
  57. package/dist/src/components/iconpicker/icon-picker.component.js +4 -4
  58. package/dist/src/components/menu/bundle.js +5 -2
  59. package/dist/src/components/menu/bundle.js.gz +0 -0
  60. package/dist/src/components/menu/menu.component.js +5 -2
  61. package/dist/src/components/modal/bundle.js +16 -13
  62. package/dist/src/components/modal/bundle.js.gz +0 -0
  63. package/dist/src/components/modal/modal.component.js +16 -13
  64. package/dist/src/components/panel/bundle.js +28 -28
  65. package/dist/src/components/panel/bundle.js.gz +0 -0
  66. package/dist/src/components/popconfirm/bundle.js +135 -41
  67. package/dist/src/components/popconfirm/bundle.js.gz +0 -0
  68. package/dist/src/components/popconfirm/popconfirm.component.d.ts +15 -119
  69. package/dist/src/components/popconfirm/popconfirm.component.js +158 -162
  70. package/dist/src/components/popconfirm/popconfirm.style.js +94 -0
  71. package/dist/src/components/presence/bundle.js +2 -1
  72. package/dist/src/components/presence/bundle.js.gz +0 -0
  73. package/dist/src/components/presence/presence.component.js +2 -1
  74. package/dist/src/components/table/bundle.js +3 -2
  75. package/dist/src/components/table/bundle.js.gz +0 -0
  76. package/dist/src/components/table/table.component.js +3 -2
  77. package/dist/src/components/tabs/bundle.js +3 -3
  78. package/dist/src/components/tabs/bundle.js.gz +0 -0
  79. package/dist/src/components/timepicker/bundle.js +3 -3
  80. package/dist/src/components/timepicker/bundle.js.gz +0 -0
  81. package/package.json +1 -1
  82. package/packages/common/dist/VERSIONS.md +1 -1
  83. package/packages/common/dist/shared/controllers/dropdown.controller.d.ts +4 -0
  84. package/packages/common/dist/shared/controllers/dropdown.controller.d.ts.map +1 -1
  85. package/packages/common/dist/shared/controllers/dropdown.controller.js +29 -3
  86. package/packages/common/dist/shared/controllers/dropdown.controller.js.map +1 -1
@@ -259,14 +259,13 @@ export default css `
259
259
  }
260
260
 
261
261
  .thread-item--active {
262
- background-color: #f4f0fd;
263
- color: #7c3aed;
264
- border-color: #7c3aed;
262
+ background-color: #ededed;
263
+ color: #161616;
264
+ border-color: transparent;
265
265
  }
266
266
 
267
267
  .thread-item--active:hover {
268
- background-color: #f4f0fd;
269
- opacity: 0.95;
268
+ background-color: #e5e5e5;
270
269
  }
271
270
 
272
271
  .thread-item__header {
@@ -292,14 +291,18 @@ export default css `
292
291
  align-items: center;
293
292
  gap: 2px;
294
293
  flex-shrink: 0;
294
+ }
295
+
296
+ .thread-item__actions .thread-item__menu {
295
297
  opacity: 0;
296
298
  pointer-events: none;
297
299
  transition: opacity 0.15s ease;
298
300
  }
299
301
 
300
- .thread-item:hover .thread-item__actions,
301
- .thread-item--active .thread-item__actions,
302
- .thread-item:focus-within .thread-item__actions {
302
+ .thread-item:hover .thread-item__menu,
303
+ .thread-item--active .thread-item__menu,
304
+ .thread-item:focus-within .thread-item__menu,
305
+ .thread-item__actions nr-dropdown[open] .thread-item__menu {
303
306
  opacity: 1;
304
307
  pointer-events: auto;
305
308
  }
@@ -329,27 +332,23 @@ export default css `
329
332
  background-color: rgba(59, 130, 246, 0.1);
330
333
  }
331
334
 
332
- .thread-item__actions nr-popconfirm {
333
- margin-top: 4px;
334
- }
335
-
336
335
  .thread-item__delete:hover {
337
336
  color: #ef4444;
338
337
  background-color: rgba(239, 68, 68, 0.1);
339
338
  }
340
339
 
341
340
  .thread-item--active .thread-item__action-btn {
342
- color: rgba(255, 255, 255, 0.55);
341
+ color: rgba(22, 22, 22, 0.6);
343
342
  }
344
343
 
345
344
  .thread-item--active .thread-item__action-btn:hover {
346
- color: #3b82f6;
347
- background-color: rgba(59, 130, 246, 0.15);
345
+ color: #161616;
346
+ background-color: rgba(0, 0, 0, 0.06);
348
347
  }
349
348
 
350
349
  .thread-item--active .thread-item__delete:hover {
351
350
  color: #ef4444;
352
- background-color: rgba(239, 68, 68, 0.15);
351
+ background-color: rgba(239, 68, 68, 0.1);
353
352
  }
354
353
 
355
354
  .thread-item__bookmark--active {
@@ -382,7 +381,7 @@ export default css `
382
381
  }
383
382
 
384
383
  .thread-item--active .thread-item__rename-input {
385
- background: rgba(255, 255, 255, 0.1);
384
+ background: #ffffff;
386
385
  }
387
386
 
388
387
  .thread-item__preview {
@@ -421,11 +420,13 @@ export default css `
421
420
  .messages {
422
421
  flex: 1;
423
422
  overflow-y: auto;
423
+ overflow-x: hidden;
424
424
  display: flex;
425
425
  flex-direction: column;
426
426
  gap: 0;
427
427
  background-color: #ffffff;
428
- padding: 8px 12px;
428
+ padding: 8px 1rem;
429
+ box-sizing: border-box;
429
430
  justify-content: flex-start; /* Always align messages to top */
430
431
  }
431
432
 
@@ -485,16 +486,22 @@ export default css `
485
486
  border: 0 solid transparent;
486
487
  }
487
488
 
488
- /* Message attachments (file tags) */
489
+ /* Message attachments (file thumbs) */
489
490
  .message__attachments {
490
491
  display: flex;
491
492
  flex-wrap: wrap;
492
- gap: 0.25rem;
493
- margin-top: 0.25rem;
493
+ gap: 0.375rem;
494
+ margin-top: 0.375rem;
494
495
  position: relative;
495
496
  z-index: 1;
496
497
  }
497
498
 
499
+ .file-thumb--message {
500
+ width: 48px;
501
+ height: 48px;
502
+ border-radius: 8px;
503
+ }
504
+
498
505
  .message-file-preview-dropdown {
499
506
  display: inline-block;
500
507
  position: relative;
@@ -992,6 +999,134 @@ export default css `
992
999
  cursor: help;
993
1000
  }
994
1001
 
1002
+ /* Claude-style upload thumbnail chip */
1003
+ .file-thumb {
1004
+ position: relative;
1005
+ display: inline-flex;
1006
+ align-items: center;
1007
+ justify-content: center;
1008
+ width: 56px;
1009
+ height: 56px;
1010
+ border-radius: 10px;
1011
+ overflow: hidden;
1012
+ background-color: #f3f4f6;
1013
+ border: 1px solid #e5e7eb;
1014
+ cursor: pointer;
1015
+ outline: none;
1016
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
1017
+ flex-shrink: 0;
1018
+ }
1019
+
1020
+ .file-thumb:hover,
1021
+ .file-thumb:focus-visible {
1022
+ border-color: #7c3aed;
1023
+ box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);
1024
+ }
1025
+
1026
+ .file-thumb__image {
1027
+ width: 100%;
1028
+ height: 100%;
1029
+ object-fit: cover;
1030
+ display: block;
1031
+ }
1032
+
1033
+ .file-thumb__ext {
1034
+ width: 100%;
1035
+ height: 100%;
1036
+ display: flex;
1037
+ align-items: center;
1038
+ justify-content: center;
1039
+ background: linear-gradient(135deg, #ede9fe, #ddd6fe);
1040
+ color: #5b21b6;
1041
+ font-weight: 600;
1042
+ font-size: 0.7rem;
1043
+ letter-spacing: 0.04em;
1044
+ text-transform: uppercase;
1045
+ }
1046
+
1047
+ .file-thumb__ext-label {
1048
+ padding: 0 4px;
1049
+ max-width: 100%;
1050
+ overflow: hidden;
1051
+ text-overflow: ellipsis;
1052
+ white-space: nowrap;
1053
+ }
1054
+
1055
+ .file-thumb__spinner {
1056
+ position: absolute;
1057
+ inset: 0;
1058
+ display: flex;
1059
+ align-items: center;
1060
+ justify-content: center;
1061
+ background-color: rgba(255, 255, 255, 0.55);
1062
+ backdrop-filter: blur(1px);
1063
+ }
1064
+
1065
+ .file-thumb__spinner-ring {
1066
+ width: 20px;
1067
+ height: 20px;
1068
+ border-radius: 50%;
1069
+ border: 2px solid rgba(124, 58, 237, 0.25);
1070
+ border-top-color: #7c3aed;
1071
+ animation: file-thumb-spin 0.8s linear infinite;
1072
+ }
1073
+
1074
+ .file-thumb--uploading {
1075
+ pointer-events: none;
1076
+ }
1077
+
1078
+ .file-thumb--uploading .file-thumb__remove {
1079
+ display: none;
1080
+ }
1081
+
1082
+ @keyframes file-thumb-spin {
1083
+ to { transform: rotate(360deg); }
1084
+ }
1085
+
1086
+ .file-thumb__remove {
1087
+ position: absolute;
1088
+ top: 2px;
1089
+ right: 2px;
1090
+ width: 16px;
1091
+ height: 16px;
1092
+ padding: 0;
1093
+ border: none;
1094
+ border-radius: 50%;
1095
+ background-color: rgba(17, 24, 39, 0.7);
1096
+ color: #ffffff;
1097
+ display: inline-flex;
1098
+ align-items: center;
1099
+ justify-content: center;
1100
+ cursor: pointer;
1101
+ opacity: 0;
1102
+ transition: opacity 0.15s ease, background-color 0.15s ease;
1103
+ }
1104
+
1105
+ .file-thumb:hover .file-thumb__remove,
1106
+ .file-thumb:focus-within .file-thumb__remove {
1107
+ opacity: 1;
1108
+ }
1109
+
1110
+ .file-thumb__remove:hover {
1111
+ background-color: rgba(17, 24, 39, 0.9);
1112
+ }
1113
+
1114
+ /* Dropdown preview extension tile (non-image files) */
1115
+ .file-preview-ext {
1116
+ width: 72px;
1117
+ height: 72px;
1118
+ display: flex;
1119
+ align-items: center;
1120
+ justify-content: center;
1121
+ border-radius: 10px;
1122
+ background: linear-gradient(135deg, #ede9fe, #ddd6fe);
1123
+ color: #5b21b6;
1124
+ font-weight: 600;
1125
+ font-size: 0.875rem;
1126
+ letter-spacing: 0.04em;
1127
+ text-transform: uppercase;
1128
+ }
1129
+
995
1130
  /* File preview dropdown content */
996
1131
  .file-preview-content {
997
1132
  display: flex;
@@ -1503,7 +1638,6 @@ export default css `
1503
1638
  justify-content: center;
1504
1639
  background: #e0e0e0;
1505
1640
  transition: background 0.15s;
1506
- z-index: 1;
1507
1641
  }
1508
1642
  .artifact-panel__resize-handle:hover,
1509
1643
  .artifact-panel__resize-handle--active {
@@ -84,6 +84,7 @@ export interface ChatbotFile {
84
84
  url?: string;
85
85
  previewUrl?: string;
86
86
  uploadProgress?: number;
87
+ isUploading?: boolean;
87
88
  error?: string;
88
89
  metadata?: Record<string, any>;
89
90
  }
@@ -348,14 +348,20 @@ export class ChatbotCoreController {
348
348
  continue;
349
349
  }
350
350
  const chatbotFile = yield this.fileHandler.createChatbotFile(file);
351
- const uploaded = yield this.providerService.uploadFileToProvider(file);
352
- if (uploaded) {
353
- Object.assign(chatbotFile, uploaded);
354
- }
355
- uploadedFiles.push(chatbotFile);
356
351
  this.fileHandler.addFile(chatbotFile);
357
- if (this.ui.showFilePreview) {
358
- this.ui.showFilePreview(chatbotFile);
352
+ try {
353
+ const uploaded = yield this.providerService.uploadFileToProvider(file);
354
+ const finalUpdates = Object.assign({ isUploading: false, uploadProgress: 100 }, (uploaded || {}));
355
+ this.fileHandler.updateFile(chatbotFile.id, finalUpdates);
356
+ Object.assign(chatbotFile, finalUpdates);
357
+ uploadedFiles.push(chatbotFile);
358
+ if (this.ui.showFilePreview) {
359
+ this.ui.showFilePreview(chatbotFile);
360
+ }
361
+ }
362
+ catch (uploadError) {
363
+ this.fileHandler.removeFile(chatbotFile.id);
364
+ throw uploadError;
359
365
  }
360
366
  }
361
367
  catch (error) {
@@ -288,25 +288,6 @@ export class ArtifactPlugin extends ChatPluginBase {
288
288
  flex-shrink: 0;
289
289
  color: #6c757d;
290
290
  }
291
- @media (prefers-color-scheme: dark) {
292
- .nr-artifact-card {
293
- background: #161b22;
294
- border-color: #30363d;
295
- }
296
- .nr-artifact-card:hover {
297
- background: #1c2128;
298
- border-color: #484f58;
299
- }
300
- .nr-artifact-card__icon {
301
- color: #c9d1d9;
302
- }
303
- .nr-artifact-card__title {
304
- color: #c9d1d9;
305
- }
306
- .nr-artifact-card__chevron {
307
- color: #8b949e;
308
- }
309
- }
310
291
  `;
311
292
  }
312
293
  }
@@ -524,41 +524,6 @@ export class FlightCardPlugin extends ChatPluginBase {
524
524
  color: #1a1a1a;
525
525
  }
526
526
 
527
- /* Dark mode support */
528
- @media (prefers-color-scheme: dark) {
529
- .${this.cssPrefix} {
530
- background: #2a2a2a;
531
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
532
- }
533
-
534
- .${this.cssPrefix}__airport-code,
535
- .${this.cssPrefix}__city,
536
- .${this.cssPrefix}__info-value {
537
- color: #ffffff;
538
- }
539
-
540
- .${this.cssPrefix}__flight-line::before {
541
- background: linear-gradient(to right, #444 0%, #444 100%);
542
- }
543
-
544
- .${this.cssPrefix}__plane-icon {
545
- background: #2a2a2a;
546
- color: #999;
547
- }
548
-
549
- .${this.cssPrefix}__duration {
550
- background: #2a2a2a;
551
- }
552
-
553
- .${this.cssPrefix}__divider {
554
- background: #444;
555
- }
556
-
557
- .${this.cssPrefix}__footer {
558
- border-top-color: #444;
559
- }
560
- }
561
-
562
527
  /* Responsive design */
563
528
  @media (max-width: 640px) {
564
529
  .${this.cssPrefix} {
@@ -58,10 +58,6 @@ export class MarkdownPlugin extends ChatPluginBase {
58
58
  p { margin: 0.5em 0; }
59
59
  ul { margin: 0.5em 0 0.5em 1.2em; }
60
60
  a { color: #0b5fff; text-decoration: underline; }
61
- @media (prefers-color-scheme: dark) {
62
- .md-code { background: #0f1115; color: #eaeef2; }
63
- .md-inline-code { background: rgba(255,255,255,.08); }
64
- }
65
61
  `;
66
62
  }
67
63
  }
@@ -444,42 +444,6 @@ export class PrintJobCardPlugin extends ChatPluginBase {
444
444
  color: #999;
445
445
  }
446
446
 
447
- /* Dark mode support */
448
- @media (prefers-color-scheme: dark) {
449
- .${this.cssPrefix} {
450
- background: #2a2a2a;
451
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
452
- border-left-color: #42a5f5;
453
- }
454
-
455
- .${this.cssPrefix}__title,
456
- .${this.cssPrefix}__detail-value {
457
- color: #ffffff;
458
- }
459
-
460
- .${this.cssPrefix}__subtitle,
461
- .${this.cssPrefix}__detail-label {
462
- color: #b0b0b0;
463
- }
464
-
465
- .${this.cssPrefix}__job-id {
466
- background: #3a3a3a;
467
- color: #b0b0b0;
468
- }
469
-
470
- .${this.cssPrefix}__details {
471
- background: #333;
472
- }
473
-
474
- .${this.cssPrefix}__progress-bar {
475
- background: #444;
476
- }
477
-
478
- .${this.cssPrefix}__footer {
479
- border-top-color: #444;
480
- }
481
- }
482
-
483
447
  /* Responsive design */
484
448
  @media (max-width: 480px) {
485
449
  .${this.cssPrefix} {
@@ -271,40 +271,6 @@ export class SelectionCardPlugin extends ChatPluginBase {
271
271
  line-height: 1.3;
272
272
  }
273
273
 
274
- /* Dark mode support */
275
- @media (prefers-color-scheme: dark) {
276
- .${this.cssPrefix}__title {
277
- color: #ffffff;
278
- }
279
-
280
- .${this.cssPrefix}__option {
281
- background: #2a2a2a;
282
- border-color: #444;
283
- }
284
-
285
- .${this.cssPrefix}__option:hover {
286
- border-color: #5a9aff;
287
- background: #1e2d44;
288
- box-shadow: 0 1px 4px rgba(90, 154, 255, 0.15);
289
- }
290
-
291
- .${this.cssPrefix}__option:active:not([aria-disabled="true"]) {
292
- background: #1a2638;
293
- }
294
-
295
- .${this.cssPrefix}__option-icon {
296
- color: #aaa;
297
- }
298
-
299
- .${this.cssPrefix}__option-label {
300
- color: #ffffff;
301
- }
302
-
303
- .${this.cssPrefix}__option-description {
304
- color: #999;
305
- }
306
- }
307
-
308
274
  /* Responsive: force single column on mobile */
309
275
  @media (max-width: 480px) {
310
276
  .${this.cssPrefix}__grid--cols-2,
@@ -88,7 +88,6 @@ export class WorkflowSocketProvider {
88
88
  this.socket = io(socketNs, {
89
89
  path: this.config.socketPath,
90
90
  query: { __params: JSON.stringify({ workflowId: this.config.workflowId }) },
91
- transports: ['websocket', 'polling'],
92
91
  autoConnect: true,
93
92
  reconnection: true,
94
93
  reconnectionAttempts: 5
@@ -96,7 +95,7 @@ export class WorkflowSocketProvider {
96
95
  return new Promise((resolve, reject) => {
97
96
  const timeout = setTimeout(() => {
98
97
  reject(new Error('Socket connection timeout'));
99
- }, 10000);
98
+ }, 30000);
100
99
  this.socket.on('connect', () => {
101
100
  clearTimeout(timeout);
102
101
  this.connected = true;
@@ -8,7 +8,9 @@ import { repeat } from 'lit/directives/repeat.js';
8
8
  import { styleMap } from 'lit/directives/style-map.js';
9
9
  import { msg } from '@lit/localize';
10
10
  /**
11
- * Renders context tags for uploaded files with hover preview
11
+ * Renders thumbnail chips for uploaded files (image thumb for images,
12
+ * extension badge for other file types). While a file is uploading, a
13
+ * spinner overlay is shown on top of the thumbnail.
12
14
  */
13
15
  function renderContextTags(files, onRemove, onFileClick) {
14
16
  const formatFileSize = (bytes) => {
@@ -19,57 +21,83 @@ function renderContextTags(files, onRemove, onFileClick) {
19
21
  const i = Math.floor(Math.log(bytes) / Math.log(k));
20
22
  return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
21
23
  };
22
- const getFileIcon = (mimeType) => {
23
- if (mimeType.startsWith('image/'))
24
- return 'image';
25
- if (mimeType.startsWith('video/'))
26
- return 'video';
27
- if (mimeType.startsWith('audio/'))
28
- return 'music';
29
- if (mimeType === 'application/pdf')
30
- return 'file-pdf';
31
- if (mimeType.startsWith('text/'))
32
- return 'file-text';
33
- return 'file';
24
+ const getExtension = (name, mimeType) => {
25
+ const dot = name.lastIndexOf('.');
26
+ if (dot >= 0 && dot < name.length - 1) {
27
+ return name.slice(dot + 1).toUpperCase().slice(0, 4);
28
+ }
29
+ if (mimeType) {
30
+ const slash = mimeType.indexOf('/');
31
+ if (slash >= 0)
32
+ return mimeType.slice(slash + 1).toUpperCase().slice(0, 4);
33
+ }
34
+ return 'FILE';
34
35
  };
35
36
  const isImage = (mimeType) => mimeType.startsWith('image/');
36
37
  return html `
37
38
  <div class="context-tags-row" part="context-tags">
38
39
  ${repeat(files, f => f.id, f => html `
39
- <nr-dropdown
40
- trigger="hover"
40
+ <nr-dropdown
41
+ trigger="hover"
41
42
  placement="top"
42
43
  size="small"
43
44
  class="file-preview-dropdown"
44
45
  >
45
- <nr-tag
46
+ <div
46
47
  slot="trigger"
47
- class="context-tag"
48
- size="small"
49
- closable
48
+ class="file-thumb ${f.isUploading ? 'file-thumb--uploading' : ''}"
49
+ role="button"
50
+ tabindex="0"
51
+ title="${f.name}"
50
52
  @click=${() => onFileClick === null || onFileClick === void 0 ? void 0 : onFileClick(f)}
51
- @nr-tag-close=${() => onRemove(f.id)}
52
- style="cursor: pointer;"
53
- >${f.name}</nr-tag>
54
-
53
+ >
54
+ ${isImage(f.mimeType) && (f.previewUrl || f.url) ? html `
55
+ <img
56
+ class="file-thumb__image"
57
+ src="${f.previewUrl || f.url}"
58
+ alt="${f.name}"
59
+ />
60
+ ` : html `
61
+ <div class="file-thumb__ext" data-ext="${getExtension(f.name, f.mimeType)}">
62
+ <span class="file-thumb__ext-label">${getExtension(f.name, f.mimeType)}</span>
63
+ </div>
64
+ `}
65
+ ${f.isUploading ? html `
66
+ <div class="file-thumb__spinner" aria-label="${msg('Uploading')}">
67
+ <span class="file-thumb__spinner-ring"></span>
68
+ </div>
69
+ ` : ''}
70
+ <button
71
+ type="button"
72
+ class="file-thumb__remove"
73
+ aria-label="${msg('Remove file')}"
74
+ title="${msg('Remove file')}"
75
+ @click=${(e) => { e.stopPropagation(); onRemove(f.id); }}
76
+ >
77
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round">
78
+ <line x1="6" y1="6" x2="18" y2="18"/>
79
+ <line x1="6" y1="18" x2="18" y2="6"/>
80
+ </svg>
81
+ </button>
82
+ </div>
83
+
55
84
  <div slot="content" class="file-preview-content">
56
85
  ${isImage(f.mimeType) && (f.url || f.previewUrl) ? html `
57
- <img
58
- src="${f.previewUrl || f.url}"
86
+ <img
87
+ src="${f.previewUrl || f.url}"
59
88
  alt="${f.name}"
60
89
  class="file-preview-image"
61
90
  />
62
91
  ` : html `
63
- <nr-icon
64
- .name=${getFileIcon(f.mimeType)}
65
- size="large"
66
- class="file-preview-icon"
67
- ></nr-icon>
92
+ <div class="file-preview-ext" data-ext="${getExtension(f.name, f.mimeType)}">
93
+ ${getExtension(f.name, f.mimeType)}
94
+ </div>
68
95
  `}
69
96
  <div class="file-preview-info">
70
97
  <div class="file-preview-name" title="${f.name}">${f.name}</div>
71
98
  <div class="file-preview-details">
72
99
  <span>${formatFileSize(f.size)}</span>
100
+ ${f.isUploading ? html `<span> · ${msg('Uploading…')}</span>` : ''}
73
101
  </div>
74
102
  </div>
75
103
  </div>