@okjavis/nodebb-theme-javis 2.2.0 → 2.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okjavis/nodebb-theme-javis",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling",
5
5
  "main": "theme.js",
6
6
  "scripts": {
@@ -0,0 +1,716 @@
1
+ // ===========================================================
2
+ // COMPOSER MODAL – JAVIS Design System
3
+ // Transforms the default NodeBB composer into a centered modal
4
+ // ===========================================================
5
+
6
+ // ===========================================================
7
+ // BACKDROP
8
+ // ===========================================================
9
+ html.composing::before {
10
+ content: '';
11
+ position: fixed;
12
+ top: 0;
13
+ left: 0;
14
+ right: 0;
15
+ bottom: 0;
16
+ background: rgba(0, 0, 0, 0.5);
17
+ backdrop-filter: blur(4px);
18
+ -webkit-backdrop-filter: blur(4px);
19
+ z-index: 1040;
20
+ }
21
+
22
+ // ===========================================================
23
+ // MODAL CONTAINER
24
+ // ===========================================================
25
+ html.composing .composer {
26
+ position: fixed !important;
27
+ top: 50% !important;
28
+ left: 50% !important;
29
+ right: auto !important;
30
+ bottom: auto !important;
31
+ transform: translate(-50%, -50%) !important;
32
+ width: 720px !important;
33
+ max-width: calc(100vw - 32px) !important;
34
+ height: 600px !important;
35
+ max-height: 85vh !important;
36
+ background: $jv-surface !important;
37
+ border-radius: $jv-radius-lg !important;
38
+ border: 1px solid $jv-border-subtle !important;
39
+ box-shadow: $jv-shadow-xl !important;
40
+ z-index: 1050 !important;
41
+ overflow: visible !important; // Allow absolute children to overflow
42
+ margin: 0 !important;
43
+ }
44
+
45
+ html.composing body {
46
+ overflow: hidden !important;
47
+ }
48
+
49
+ // ===========================================================
50
+ // COMPOSER CONTAINER
51
+ // ===========================================================
52
+ html.composing .composer .composer-container {
53
+ display: flex !important;
54
+ flex-direction: column !important;
55
+ height: 100% !important;
56
+ padding: 0 !important;
57
+ gap: 0 !important;
58
+ overflow: hidden !important;
59
+ border-radius: $jv-radius-lg !important;
60
+ }
61
+
62
+ html.composing .composer .composer-container > .p-2 {
63
+ display: flex !important;
64
+ flex-direction: column !important;
65
+ height: 100% !important;
66
+ padding: 0 !important;
67
+ gap: 0 !important;
68
+ }
69
+
70
+ // ===========================================================
71
+ // HIDE MOBILE NAVBAR
72
+ // ===========================================================
73
+ html.composing .composer .mobile-navbar {
74
+ display: none !important;
75
+ }
76
+
77
+ // ===========================================================
78
+ // HEADER
79
+ // ===========================================================
80
+ html.composing .composer .title-container {
81
+ display: flex !important;
82
+ align-items: center !important;
83
+ gap: $jv-space-3 !important;
84
+ padding: $jv-space-4 $jv-space-5 !important;
85
+ border-bottom: 1px solid $jv-border-subtle !important;
86
+ background: $jv-surface !important;
87
+ flex-shrink: 0 !important;
88
+ min-height: 56px !important;
89
+ border-radius: $jv-radius-lg $jv-radius-lg 0 0 !important;
90
+ }
91
+
92
+ // Category selector
93
+ html.composing .composer .category-list-container {
94
+ flex-shrink: 0 !important;
95
+ display: block !important;
96
+
97
+ [component="category-selector"] {
98
+ .btn {
99
+ background: $jv-bg !important;
100
+ border: 1px solid $jv-border-subtle !important;
101
+ border-radius: $jv-radius-sm !important;
102
+ padding: $jv-space-2 $jv-space-3 !important;
103
+ font-size: $jv-font-size-sm !important;
104
+ color: $jv-text-main !important;
105
+
106
+ &:hover {
107
+ border-color: $jv-border-strong !important;
108
+ background: $jv-hover-bg !important;
109
+ }
110
+ }
111
+
112
+ .dropdown-menu {
113
+ min-width: 280px !important;
114
+ max-height: 320px !important;
115
+ overflow-y: auto !important;
116
+ padding: $jv-space-2 !important;
117
+
118
+ // Fix category list spacing
119
+ .category-dropdown-menu {
120
+ margin: 0 !important;
121
+ padding: 0 !important;
122
+ }
123
+
124
+ // Category items inside dropdown
125
+ li.category {
126
+ margin: 0 !important;
127
+ padding: 0 !important;
128
+
129
+ a.dropdown-item {
130
+ display: flex !important;
131
+ align-items: center !important;
132
+ gap: $jv-space-2 !important;
133
+ padding: $jv-space-2 $jv-space-3 !important;
134
+ margin: 2px 0 !important;
135
+ font-size: $jv-font-size-sm !important;
136
+ color: $jv-text-main !important;
137
+ border-radius: $jv-radius-xs !important;
138
+ white-space: nowrap !important;
139
+ overflow: hidden !important;
140
+ text-overflow: ellipsis !important;
141
+
142
+ &:hover {
143
+ background: $jv-hover-bg !important;
144
+ }
145
+
146
+ // Category icon
147
+ .category-item {
148
+ display: inline-flex !important;
149
+ align-items: center !important;
150
+ gap: $jv-space-2 !important;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ // Title input
159
+ html.composing .composer [data-component="composer/title"] {
160
+ flex: 1 !important;
161
+ min-width: 0 !important;
162
+
163
+ .title {
164
+ width: 100% !important;
165
+ font-size: $jv-font-size-lg !important;
166
+ font-weight: 600 !important;
167
+ border: none !important;
168
+ background: transparent !important;
169
+ padding: $jv-space-2 0 !important;
170
+ color: $jv-text-main !important;
171
+ box-shadow: none !important;
172
+
173
+ &::placeholder {
174
+ color: $jv-text-soft !important;
175
+ font-weight: 400 !important;
176
+ }
177
+
178
+ &:focus {
179
+ outline: none !important;
180
+ box-shadow: none !important;
181
+ }
182
+ }
183
+ }
184
+
185
+ // ===========================================================
186
+ // ACTION BAR - X close button in header, Submit at bottom
187
+ // ===========================================================
188
+ html.composing .composer .action-bar {
189
+ flex-shrink: 0 !important;
190
+ display: flex !important;
191
+ align-items: center !important;
192
+ gap: $jv-space-2 !important;
193
+
194
+ // HIDE minimize button
195
+ .composer-minimize {
196
+ display: none !important;
197
+ }
198
+
199
+ // Style discard as X close button - stays in header
200
+ .composer-discard {
201
+ display: flex !important;
202
+ align-items: center !important;
203
+ justify-content: center !important;
204
+ width: 36px !important;
205
+ height: 36px !important;
206
+ padding: 0 !important;
207
+ margin: 0 !important;
208
+ border-radius: $jv-radius-sm !important;
209
+ background: transparent !important;
210
+ border: none !important;
211
+ color: $jv-text-muted !important;
212
+ cursor: pointer !important;
213
+
214
+ &:hover {
215
+ background: $jv-hover-bg !important;
216
+ color: $jv-text-main !important;
217
+ }
218
+
219
+ // Hide text
220
+ span {
221
+ display: none !important;
222
+ }
223
+
224
+ // Style icon as X
225
+ i.fa-trash {
226
+ font-size: 18px !important;
227
+ margin: 0 !important;
228
+
229
+ &::before {
230
+ content: '\f00d' !important; // fa-times
231
+ }
232
+ }
233
+ }
234
+
235
+ }
236
+
237
+ // ===========================================================
238
+ // SUBMIT BUTTON - Positioned at bottom right of modal (footer)
239
+ // Vertically centered with the footer (56px height, button ~40px)
240
+ // ===========================================================
241
+ html.composing .composer .action-bar [component="composer/submit/container"] {
242
+ position: absolute !important;
243
+ bottom: 8px !important; // Centered: (56px footer - 40px button) / 2 = 8px
244
+ right: $jv-space-5 !important;
245
+ top: auto !important;
246
+ left: auto !important;
247
+ z-index: 1060 !important;
248
+ display: flex !important;
249
+ align-items: center !important;
250
+
251
+ .btn-group {
252
+ display: flex !important;
253
+ }
254
+
255
+ .composer-submit {
256
+ background: $jv-primary !important;
257
+ border-color: $jv-primary !important;
258
+ color: white !important;
259
+ font-weight: 600 !important;
260
+ padding: $jv-space-2 $jv-space-5 !important;
261
+ border-radius: $jv-radius-sm !important;
262
+ font-size: $jv-font-size-sm !important;
263
+ display: inline-flex !important;
264
+ align-items: center !important;
265
+ gap: $jv-space-1 !important;
266
+ height: 40px !important;
267
+
268
+ &:hover {
269
+ background: $jv-primary-hover !important;
270
+ border-color: $jv-primary-hover !important;
271
+ }
272
+
273
+ i {
274
+ margin-right: $jv-space-1 !important;
275
+ }
276
+
277
+ span {
278
+ display: inline !important;
279
+ }
280
+ }
281
+
282
+ // HIDE schedule/options dropdown - not needed
283
+ [component="composer/submit/options/container"],
284
+ .dropdown-toggle {
285
+ display: none !important;
286
+ }
287
+ }
288
+
289
+ // ===========================================================
290
+ // FORMATTING BAR
291
+ // ===========================================================
292
+ html.composing .composer .formatting-bar {
293
+ display: flex !important;
294
+ align-items: center !important;
295
+ justify-content: space-between !important;
296
+ padding: $jv-space-2 $jv-space-4 !important;
297
+ background: $jv-bg !important;
298
+ border-bottom: 1px solid $jv-border-subtle !important;
299
+ margin: 0 !important;
300
+ flex-shrink: 0 !important;
301
+ gap: $jv-space-3 !important;
302
+ }
303
+
304
+ html.composing .composer .formatting-group {
305
+ display: flex !important;
306
+ align-items: center !important;
307
+ gap: 2px !important;
308
+ flex-wrap: nowrap !important;
309
+ overflow-x: auto !important;
310
+
311
+ li {
312
+ flex-shrink: 0 !important;
313
+ list-style: none !important;
314
+
315
+ .btn {
316
+ color: $jv-text-muted !important;
317
+ padding: $jv-space-2 !important;
318
+ border-radius: $jv-radius-sm !important;
319
+ min-width: 32px !important;
320
+ height: 32px !important;
321
+ display: flex !important;
322
+ align-items: center !important;
323
+ justify-content: center !important;
324
+ background: transparent !important;
325
+ border: none !important;
326
+
327
+ &:hover {
328
+ background: $jv-hover-bg !important;
329
+ color: $jv-text-main !important;
330
+ }
331
+ }
332
+ }
333
+
334
+ .spacer {
335
+ width: 1px !important;
336
+ height: 20px !important;
337
+ background: $jv-border-subtle !important;
338
+ margin: 0 $jv-space-1 !important;
339
+ }
340
+ }
341
+
342
+ // Preview/Help buttons
343
+ html.composing .composer .formatting-bar > .d-flex {
344
+ flex-shrink: 0 !important;
345
+ gap: $jv-space-2 !important;
346
+ align-items: center !important;
347
+
348
+ .draft-icon {
349
+ color: $jv-success !important;
350
+ }
351
+
352
+ [data-action="preview"],
353
+ [data-action="help"] {
354
+ color: $jv-text-muted !important;
355
+ font-size: $jv-font-size-sm !important;
356
+ padding: $jv-space-2 $jv-space-3 !important;
357
+ border-radius: $jv-radius-sm !important;
358
+ background: transparent !important;
359
+ border: none !important;
360
+
361
+ &:hover {
362
+ background: $jv-hover-bg !important;
363
+ color: $jv-text-main !important;
364
+ }
365
+ }
366
+ }
367
+
368
+ // ===========================================================
369
+ // WRITE/PREVIEW CONTAINER
370
+ // ===========================================================
371
+ html.composing .composer .write-preview-container {
372
+ flex: 1 !important;
373
+ display: flex !important;
374
+ flex-direction: row !important;
375
+ min-height: 0 !important;
376
+ overflow: hidden !important;
377
+ gap: 0 !important;
378
+ }
379
+
380
+ // Write container - OVERRIDE w-50
381
+ html.composing .composer .write-container {
382
+ flex: 1 !important;
383
+ display: flex !important;
384
+ flex-direction: column !important;
385
+ width: 100% !important;
386
+ min-width: 0 !important;
387
+ position: relative !important;
388
+
389
+ &.w-50 {
390
+ width: 100% !important;
391
+ }
392
+
393
+ .write {
394
+ flex: 1 !important;
395
+ width: 100% !important;
396
+ height: 100% !important;
397
+ min-height: 150px !important;
398
+ padding: $jv-space-4 !important;
399
+ border: none !important;
400
+ border-radius: 0 !important;
401
+ resize: none !important;
402
+ font-size: $jv-font-size-base !important;
403
+ line-height: $jv-line-height-relaxed !important;
404
+ color: $jv-text-main !important;
405
+ background: $jv-surface !important;
406
+
407
+ &::placeholder {
408
+ color: $jv-text-soft !important;
409
+ }
410
+
411
+ &:focus {
412
+ outline: none !important;
413
+ box-shadow: none !important;
414
+ }
415
+ }
416
+ }
417
+
418
+ // Preview container - toggles via d-none/d-flex classes from NodeBB JS
419
+ html.composing .composer .preview-container {
420
+ width: 100% !important;
421
+ overflow-y: auto !important;
422
+ padding: $jv-space-4 !important;
423
+ background: $jv-surface !important;
424
+ flex: 1 !important;
425
+ flex-direction: column !important;
426
+
427
+ &.w-50 {
428
+ width: 100% !important;
429
+ }
430
+
431
+ // Allow NodeBB's d-none to work (hide by default unless d-flex is added)
432
+ &.d-none {
433
+ display: none !important;
434
+ }
435
+
436
+ &:not(.d-none) {
437
+ display: flex !important;
438
+ }
439
+
440
+ .preview {
441
+ width: 100% !important;
442
+ }
443
+ }
444
+
445
+ // Write container visibility - controlled by NodeBB JS
446
+ html.composing .composer .write-container.d-none {
447
+ display: none !important;
448
+ }
449
+
450
+ html.composing .composer .write-container:not(.d-none) {
451
+ display: flex !important;
452
+ }
453
+
454
+ // ===========================================================
455
+ // FOOTER - Tags row
456
+ // ===========================================================
457
+ html.composing .composer .tag-row {
458
+ display: flex !important;
459
+ align-items: center !important;
460
+ padding: 8px 20px !important;
461
+ border-top: 1px solid $jv-border-subtle !important;
462
+ background: $jv-surface !important;
463
+ flex-shrink: 0 !important;
464
+ min-height: 56px !important;
465
+ gap: $jv-space-4 !important;
466
+ border-radius: 0 0 $jv-radius-lg $jv-radius-lg !important;
467
+ }
468
+
469
+ html.composing .composer .tags-container {
470
+ display: flex !important;
471
+ align-items: center !important;
472
+ gap: $jv-space-2 !important;
473
+ flex: 1 !important;
474
+ min-width: 0 !important;
475
+ max-width: calc(100% - 140px) !important; // Leave space for submit button
476
+ background: $jv-bg !important;
477
+ border: 1px solid $jv-border-subtle !important;
478
+ border-radius: $jv-radius-sm !important;
479
+ padding: $jv-space-1 $jv-space-3 !important;
480
+ min-height: 40px !important;
481
+ cursor: text !important;
482
+ transition: border-color 0.15s ease !important;
483
+
484
+ &:hover {
485
+ border-color: $jv-border-strong !important;
486
+ }
487
+
488
+ &:focus-within {
489
+ border-color: $jv-primary !important;
490
+ box-shadow: 0 0 0 2px rgba($jv-primary, 0.1) !important;
491
+ }
492
+
493
+ [component="composer/tag/dropdown"] {
494
+ flex-shrink: 0 !important;
495
+
496
+ .btn {
497
+ color: $jv-text-muted !important;
498
+ font-size: $jv-font-size-sm !important;
499
+ padding: $jv-space-1 $jv-space-2 !important;
500
+ border-radius: $jv-radius-xs !important;
501
+ background: transparent !important;
502
+ border: none !important;
503
+
504
+ i {
505
+ color: $jv-primary !important;
506
+ }
507
+
508
+ &:hover {
509
+ background: $jv-hover-bg !important;
510
+ }
511
+ }
512
+ }
513
+
514
+ // Wrapper that appears when tags are present
515
+ .bootstrap-tagsinput {
516
+ flex: 1 !important;
517
+ display: flex !important;
518
+ flex-wrap: wrap !important;
519
+ align-items: center !important;
520
+ gap: $jv-space-2 !important;
521
+ background: transparent !important;
522
+ border: none !important;
523
+ padding: 0 !important;
524
+ box-shadow: none !important;
525
+ min-height: auto !important;
526
+
527
+ // Individual tags - beautiful pills
528
+ .tag {
529
+ display: inline-flex !important;
530
+ align-items: center !important;
531
+ gap: 4px !important;
532
+ background: $jv-primary-soft !important;
533
+ color: $jv-primary !important;
534
+ border-radius: $jv-radius-pill !important;
535
+ padding: 4px 10px !important;
536
+ font-size: $jv-font-size-xs !important;
537
+ font-weight: 500 !important;
538
+ border: none !important;
539
+ line-height: 1.4 !important;
540
+
541
+ // Remove button inside tag
542
+ [data-role="remove"] {
543
+ margin-left: 2px !important;
544
+ cursor: pointer !important;
545
+ opacity: 0.7 !important;
546
+ font-size: 12px !important;
547
+
548
+ &:hover {
549
+ opacity: 1 !important;
550
+ }
551
+ }
552
+ }
553
+
554
+ // The input inside bootstrap-tagsinput
555
+ input {
556
+ flex: 1 !important;
557
+ min-width: 80px !important;
558
+ border: none !important;
559
+ background: transparent !important;
560
+ font-size: $jv-font-size-sm !important;
561
+ color: $jv-text-main !important;
562
+ padding: $jv-space-1 0 !important;
563
+ outline: none !important;
564
+
565
+ &::placeholder {
566
+ color: $jv-text-soft !important;
567
+ }
568
+ }
569
+ }
570
+
571
+ // Plain input when no bootstrap-tagsinput
572
+ > input.tags {
573
+ flex: 1 !important;
574
+ border: none !important;
575
+ background: transparent !important;
576
+ font-size: $jv-font-size-sm !important;
577
+ color: $jv-text-main !important;
578
+ padding: $jv-space-1 0 !important;
579
+
580
+ &::placeholder {
581
+ color: $jv-text-soft !important;
582
+ }
583
+
584
+ &:focus {
585
+ outline: none !important;
586
+ }
587
+ }
588
+ }
589
+
590
+ // ===========================================================
591
+ // IMAGE DROP ZONE
592
+ // ===========================================================
593
+ html.composing .composer .imagedrop {
594
+ position: absolute !important;
595
+ inset: 0 !important;
596
+ background: rgba($jv-primary, 0.1) !important;
597
+ border: 2px dashed $jv-primary !important;
598
+ border-radius: $jv-radius-md !important;
599
+ display: none !important;
600
+ align-items: center !important;
601
+ justify-content: center !important;
602
+ z-index: 100 !important;
603
+
604
+ > div {
605
+ background: $jv-surface !important;
606
+ padding: $jv-space-4 $jv-space-8 !important;
607
+ border-radius: $jv-radius-sm !important;
608
+ font-weight: 500 !important;
609
+ color: $jv-primary !important;
610
+ }
611
+ }
612
+
613
+ // ===========================================================
614
+ // RESIZER - HIDE
615
+ // ===========================================================
616
+ html.composing .composer .resizer {
617
+ display: none !important;
618
+ }
619
+
620
+ // ===========================================================
621
+ // DROPDOWN MENUS
622
+ // ===========================================================
623
+ html.composing .composer .dropdown-menu {
624
+ border: 1px solid $jv-border-subtle !important;
625
+ border-radius: $jv-radius-sm !important;
626
+ box-shadow: $jv-shadow-lg !important;
627
+ background: $jv-surface !important;
628
+ padding: $jv-space-2 !important;
629
+
630
+ .dropdown-item {
631
+ font-size: $jv-font-size-sm !important;
632
+ padding: $jv-space-2 $jv-space-3 !important;
633
+ border-radius: $jv-radius-xs !important;
634
+
635
+ &:hover {
636
+ background: $jv-hover-bg !important;
637
+ }
638
+ }
639
+ }
640
+
641
+ // ===========================================================
642
+ // FILE FORM - HIDE
643
+ // ===========================================================
644
+ html.composing .composer #fileForm {
645
+ display: none !important;
646
+ }
647
+
648
+ // ===========================================================
649
+ // QUICK SEARCH
650
+ // ===========================================================
651
+ html.composing .composer .quick-search-container {
652
+ position: absolute !important;
653
+ top: 100% !important;
654
+ left: 0 !important;
655
+ right: 0 !important;
656
+ background: $jv-surface !important;
657
+ border: 1px solid $jv-border-subtle !important;
658
+ border-radius: $jv-radius-sm !important;
659
+ box-shadow: $jv-shadow-lg !important;
660
+ z-index: 50 !important;
661
+ }
662
+
663
+ // ===========================================================
664
+ // REPLY MODE
665
+ // ===========================================================
666
+ html.composing .composer.reply {
667
+ height: 450px !important;
668
+
669
+ .tags-container {
670
+ display: none !important;
671
+ }
672
+ }
673
+
674
+ // ===========================================================
675
+ // MOBILE RESPONSIVE
676
+ // ===========================================================
677
+ @media (max-width: 767px) {
678
+ html.composing .composer {
679
+ top: 0 !important;
680
+ left: 0 !important;
681
+ transform: none !important;
682
+ width: 100vw !important;
683
+ max-width: 100vw !important;
684
+ height: 100vh !important;
685
+ max-height: 100vh !important;
686
+ border-radius: 0 !important;
687
+ border: none !important;
688
+ }
689
+
690
+ html.composing .composer .composer-container {
691
+ height: 100vh !important;
692
+ border-radius: 0 !important;
693
+ }
694
+
695
+ html.composing .composer .title-container {
696
+ padding: $jv-space-3 $jv-space-4 !important;
697
+ padding-top: max($jv-space-3, env(safe-area-inset-top)) !important;
698
+ border-radius: 0 !important;
699
+ }
700
+
701
+ html.composing .composer .formatting-bar {
702
+ padding: $jv-space-2 $jv-space-3 !important;
703
+ overflow-x: auto !important;
704
+
705
+ [data-action="preview"] span,
706
+ [data-action="help"] span {
707
+ display: none !important;
708
+ }
709
+ }
710
+
711
+ html.composing .composer .tag-row {
712
+ padding: $jv-space-3 $jv-space-4 !important;
713
+ padding-bottom: max($jv-space-3, env(safe-area-inset-bottom)) !important;
714
+ border-radius: 0 !important;
715
+ }
716
+ }