@okjavis/nodebb-theme-javis 3.0.2 → 3.0.5

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": "3.0.2",
3
+ "version": "3.0.5",
4
4
  "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling",
5
5
  "main": "theme.js",
6
6
  "scripts": {
package/plugin.json CHANGED
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "templates": "templates",
24
24
  "screenshot": "screenshot.png",
25
- "version": "1.5.0",
25
+ "version": "3.0.3",
26
26
  "nbbpm": {
27
27
  "compatibility": "^4.0.0"
28
28
  }
package/scss/_base.scss CHANGED
@@ -30,8 +30,13 @@ nav[component="sidebar/left"] {
30
30
 
31
31
  // Sidebar toggle link padding
32
32
  .sidebar-toggle {
33
+ position: relative;
34
+ z-index: 10;
35
+
33
36
  [component="sidebar/toggle"] {
34
37
  padding: $jv-space-4 $jv-space-5 !important; // 16px vertical, 20px horizontal
38
+ pointer-events: auto !important;
39
+ cursor: pointer !important;
35
40
  }
36
41
  }
37
42
 
package/scss/_feed.scss CHANGED
@@ -58,11 +58,8 @@
58
58
  display: none !important;
59
59
  }
60
60
 
61
- // Hide the controls row (category dropdown, options)
62
- // The composer prompt card replaces this functionality
63
- > .row > div > .d-flex.justify-content-between.py-2.mb-2 {
64
- display: none !important;
65
- }
61
+ // The controls row is now visible but the new_topic button is hidden
62
+ // JS injects the composer prompt card before this row
66
63
  }
67
64
 
68
65
  // ===========================================================
@@ -74,7 +71,7 @@
74
71
  border-radius: $jv-radius-lg;
75
72
  padding: $jv-space-4 $jv-space-6; // 16px top/bottom, 24px left/right
76
73
  margin-top: $jv-space-4; // Align with trending tags padding-top
77
- margin-bottom: $jv-space-4;
74
+ margin-bottom: $jv-space-2; // Reduced bottom margin for tighter spacing
78
75
  box-shadow: $jv-shadow-sm;
79
76
  transition: box-shadow $jv-transition-fast, border-color $jv-transition-fast;
80
77
 
@@ -486,7 +483,7 @@
486
483
  .content[component="post/content"] {
487
484
  font-size: 15px !important; // Slightly larger for better readability
488
485
  line-height: 1.6 !important;
489
- color: $jv-text-muted;
486
+ color: #000000 !important; // Black text for feed body copy
490
487
  margin-top: $jv-space-3; // Better spacing from meta
491
488
 
492
489
  // Truncation styles (from plugin)
@@ -1001,3 +998,140 @@ ul.categories-list {
1001
998
  }
1002
999
  }
1003
1000
  }
1001
+
1002
+ // ===========================================================
1003
+ // FEED CATEGORY FILTER - LinkedIn Style
1004
+ // Minimal text-based dropdown like "Sort by: Top"
1005
+ // ===========================================================
1006
+
1007
+ // Ultra-specific override to remove pill button styling
1008
+ .feed .feed-category-filter .btn-group .btn.btn-ghost.btn-sm.dropdown-toggle,
1009
+ .feed .feed-category-filter .category-dropdown-container .btn.btn-ghost.btn-sm,
1010
+ .feed .feed-category-filter button.btn.btn-ghost.btn-sm {
1011
+ border: none !important;
1012
+ border-width: 0 !important;
1013
+ border-color: transparent !important;
1014
+ border-radius: 4px !important;
1015
+ background: transparent !important;
1016
+ box-shadow: none !important;
1017
+ }
1018
+
1019
+ .feed-category-filter {
1020
+ .category-dropdown-container,
1021
+ [component="category/dropdown"] {
1022
+ // Override the btn-group styling
1023
+ &.btn-group {
1024
+ border: none !important;
1025
+ box-shadow: none !important;
1026
+ }
1027
+
1028
+ // Target the specific button with all its classes
1029
+ .dropdown-toggle,
1030
+ .btn.btn-ghost.btn-sm.dropdown-toggle,
1031
+ button.dropdown-toggle {
1032
+ // LinkedIn style - minimal, text-based, NO border
1033
+ padding: 4px 8px !important;
1034
+ font-size: 12px !important;
1035
+ font-weight: 400 !important;
1036
+ color: $jv-text-muted !important;
1037
+ background: transparent !important;
1038
+ background-color: transparent !important;
1039
+ border: none !important;
1040
+ border-width: 0 !important;
1041
+ border-color: transparent !important;
1042
+ box-shadow: none !important;
1043
+ border-radius: 4px !important;
1044
+ min-width: auto !important;
1045
+ gap: 4px !important;
1046
+
1047
+ // Hide the category icon, show only name
1048
+ .category-item {
1049
+ gap: 4px !important;
1050
+
1051
+ // Hide category icon on compact view
1052
+ .rounded-circle,
1053
+ [component="category/icon"],
1054
+ span[style*="background"] {
1055
+ display: none !important;
1056
+ }
1057
+
1058
+ // Show category name - smaller text
1059
+ span.d-none.d-md-inline,
1060
+ .fw-semibold {
1061
+ display: inline !important;
1062
+ font-size: 12px !important;
1063
+ font-weight: 600 !important;
1064
+ color: $jv-text-main !important;
1065
+ }
1066
+ }
1067
+
1068
+ // "All Categories" state
1069
+ > i.fa-list {
1070
+ font-size: 12px !important;
1071
+ color: $jv-text-muted !important;
1072
+ }
1073
+
1074
+ // Show "All Categories" text
1075
+ > span.d-none.d-md-inline,
1076
+ > .fw-semibold {
1077
+ display: inline !important;
1078
+ font-size: 12px !important;
1079
+ font-weight: 600 !important;
1080
+ color: $jv-text-main !important;
1081
+ }
1082
+
1083
+ // Dropdown arrow - smaller
1084
+ &::after {
1085
+ font-size: 8px !important;
1086
+ margin-left: 4px !important;
1087
+ color: $jv-text-muted !important;
1088
+ border-top-width: 4px !important;
1089
+ border-right-width: 4px !important;
1090
+ border-left-width: 4px !important;
1091
+ }
1092
+
1093
+ &:hover {
1094
+ background: rgba(0, 0, 0, 0.04) !important;
1095
+ background-color: rgba(0, 0, 0, 0.04) !important;
1096
+ color: $jv-text-main !important;
1097
+ border: none !important;
1098
+ box-shadow: none !important;
1099
+ }
1100
+
1101
+ &:focus,
1102
+ &:active {
1103
+ background: transparent !important;
1104
+ border: none !important;
1105
+ box-shadow: none !important;
1106
+ outline: none !important;
1107
+ }
1108
+ }
1109
+
1110
+ // Dropdown menu styling
1111
+ .dropdown-menu {
1112
+ min-width: 180px;
1113
+ max-width: 240px;
1114
+ border-radius: $jv-radius-md !important;
1115
+ border: 1px solid $jv-border-subtle !important;
1116
+ box-shadow: $jv-shadow-card !important;
1117
+ padding: $jv-space-1 !important;
1118
+ margin-top: 4px !important;
1119
+
1120
+ .category-dropdown-menu {
1121
+ max-height: 280px;
1122
+ overflow-y: auto;
1123
+ }
1124
+
1125
+ .dropdown-item {
1126
+ font-size: 13px !important;
1127
+ padding: 6px 12px !important;
1128
+ border-radius: $jv-radius-xs !important;
1129
+ color: $jv-text-main !important;
1130
+
1131
+ &:hover {
1132
+ background: $jv-hover-bg !important;
1133
+ }
1134
+ }
1135
+ }
1136
+ }
1137
+ }
@@ -137,6 +137,11 @@
137
137
  margin-top: auto;
138
138
  }
139
139
 
140
+ // Ensure the dropup container has proper positioning context
141
+ .sidebar-user-section .dropup {
142
+ position: relative;
143
+ }
144
+
140
145
  // User dropdown menu styling
141
146
  .sidebar-user-section .dropdown-menu {
142
147
  min-width: 220px;
@@ -145,6 +150,18 @@
145
150
  border: 1px solid $jv-border-subtle;
146
151
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
147
152
 
153
+ // Fix the shifting issue - disable Popper transforms and use fixed position
154
+ // Position dropdown to open upward from the bottom of the trigger
155
+ transform: none !important;
156
+ inset: auto auto 100% 0 !important; // bottom: 100%, left: 0
157
+ margin-bottom: $jv-space-2;
158
+ top: auto !important;
159
+ bottom: 100% !important;
160
+ left: 0 !important;
161
+
162
+ // Remove animation that could cause visual shifting
163
+ animation: none;
164
+
148
165
  .dropdown-item {
149
166
  padding: $jv-space-2 $jv-space-4;
150
167
  font-size: $jv-font-size-sm;
package/scss/_topic.scss CHANGED
@@ -32,9 +32,32 @@ body.template-topic {
32
32
  [component="post/header"].fs-3 {
33
33
  margin-bottom: 0;
34
34
  display: flex;
35
- align-items: flex-start;
35
+ align-items: center; // Center align items vertically
36
36
  gap: $jv-space-3;
37
37
 
38
+ // Back button styling
39
+ .topic-back-btn {
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ width: 36px;
44
+ height: 36px;
45
+ border-radius: $jv-radius-sm;
46
+ color: $jv-text-muted;
47
+ text-decoration: none;
48
+ transition: background-color $jv-transition-fast, color $jv-transition-fast;
49
+ flex-shrink: 0;
50
+
51
+ &:hover {
52
+ background: $jv-hover-bg;
53
+ color: $jv-text-main;
54
+ }
55
+
56
+ i {
57
+ font-size: 18px;
58
+ }
59
+ }
60
+
38
61
  h1,
39
62
  [component="topic/title"],
40
63
  .topic-title {
@@ -142,33 +165,71 @@ body.template-topic {
142
165
  display: none !important;
143
166
  }
144
167
 
145
- // Card styling on the parent container that includes avatar
168
+ // Card styling - vertical stack layout (like feed page)
146
169
  .post-container-parent {
147
170
  background: $jv-surface;
148
171
  border-radius: $jv-radius-md;
149
172
  border: 1px solid $jv-border-subtle;
150
- padding: $jv-space-3;
151
- gap: $jv-space-2 !important; // Tighter gap
152
-
153
- // Reduce avatar size from 48px to 36px
154
- > .bg-body {
155
- // Align avatar container with the post header (username/timestamp line)
156
- align-self: flex-start;
157
- margin-top: 2px; // Fine-tune to vertically center with text
158
-
159
- [component="user/picture"],
160
- .avatar {
161
- width: 36px !important;
162
- height: 36px !important;
163
- font-size: $jv-font-size-sm !important;
164
- line-height: 36px !important;
173
+ padding: $jv-space-4;
174
+ gap: $jv-space-3 !important;
175
+ }
176
+
177
+ // Author row - avatar + username + timestamp inline
178
+ .post-author-row {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: $jv-space-2;
182
+
183
+ // Avatar size 32px
184
+ [component="user/picture"],
185
+ .avatar {
186
+ width: 32px !important;
187
+ height: 32px !important;
188
+ min-width: 32px !important;
189
+ min-height: 32px !important;
190
+ font-size: $jv-font-size-sm !important;
191
+ line-height: 32px !important;
192
+ border-radius: 50% !important;
193
+ }
194
+
195
+ // Author name
196
+ .post-author-name {
197
+ font-weight: 600;
198
+ font-size: $jv-font-size-sm;
199
+ color: $jv-text-main;
200
+ text-decoration: none;
201
+
202
+ &:hover {
203
+ color: $jv-primary;
165
204
  }
166
205
  }
167
- }
168
206
 
169
- // Align the post header row with avatar (extends base .post-header)
170
- .post-header {
171
- min-height: 36px; // Match avatar height for alignment
207
+ // Timestamp
208
+ .post-timestamp {
209
+ font-size: $jv-font-size-xs;
210
+ color: $jv-text-soft;
211
+
212
+ &::before {
213
+ content: "• ";
214
+ margin-right: 2px;
215
+ }
216
+ }
217
+
218
+ // Edit indicator
219
+ .edit-icon {
220
+ font-size: $jv-font-size-xs;
221
+ }
222
+
223
+ // Post index
224
+ .post-index {
225
+ font-size: $jv-font-size-xs;
226
+ color: $jv-text-soft;
227
+ opacity: 0.6;
228
+ }
229
+
230
+ .bookmarked {
231
+ color: $jv-warning;
232
+ }
172
233
  }
173
234
 
174
235
  .post-container {
@@ -185,7 +246,7 @@ body.template-topic {
185
246
  }
186
247
 
187
248
  // ===========================================================
188
- // POST HEADER Avatar, Username, Time
249
+ // POST HEADER (legacy support for any remaining old structure)
189
250
  // ===========================================================
190
251
  .post-header {
191
252
  display: flex;
@@ -382,10 +443,11 @@ body.template-topic {
382
443
  // ===========================================================
383
444
  [component="post/footer"],
384
445
  .post-footer {
385
- padding-top: 0 !important;
446
+ padding-top: $jv-space-3 !important;
386
447
  padding-bottom: 0 !important;
387
- margin-top: $jv-space-4;
448
+ margin-top: $jv-space-3;
388
449
  border-top: 1px solid $jv-border-subtle;
450
+ border-bottom: none !important; // Remove duplicate line from HTML class
389
451
  }
390
452
 
391
453
  [component="post/actions"] {
@@ -569,6 +631,9 @@ body.template-topic {
569
631
  align-self: flex-start;
570
632
  z-index: 100;
571
633
  pointer-events: auto;
634
+ // Match the top padding of posts list (py-3 = 1rem) + first post pt-4 (1.5rem)
635
+ // to align sidebar with post card
636
+ margin-top: calc(1rem + 1.5rem);
572
637
  }
573
638
 
574
639
  .topic-sidebar {
@@ -648,17 +713,60 @@ body.template-topic {
648
713
  }
649
714
  }
650
715
 
651
- .bottom-sheet {
716
+ // Each dropdown container gets stacking context
717
+ .bottom-sheet,
718
+ .thread-tools {
652
719
  width: 100%;
653
- .dropdown-menu { width: 100%; }
720
+ position: relative;
721
+ z-index: 1;
722
+
723
+ // When dropdown is open, raise z-index
724
+ &.show,
725
+ &:focus-within {
726
+ z-index: 100;
727
+ }
728
+
729
+ .dropdown-menu {
730
+ position: absolute;
731
+ top: 100%;
732
+ left: 0;
733
+ width: 100%;
734
+ min-width: 200px;
735
+ max-width: 280px;
736
+ z-index: 1050;
737
+ margin-top: $jv-space-1;
738
+ padding: $jv-space-2;
739
+ background: $jv-surface;
740
+ border: 1px solid $jv-border-subtle;
741
+ border-radius: $jv-radius-md;
742
+ box-shadow: $jv-shadow-lg;
743
+
744
+ // Dropdown items
745
+ .dropdown-item {
746
+ padding: $jv-space-2 $jv-space-3;
747
+ border-radius: $jv-radius-sm;
748
+ font-size: $jv-font-size-sm;
749
+ white-space: normal;
750
+
751
+ .help-text {
752
+ font-size: $jv-font-size-xs;
753
+ line-height: 1.3;
754
+ margin-top: 2px;
755
+ }
756
+ }
757
+ }
654
758
  }
655
759
 
656
- .thread-tools {
657
- .dropdown-menu { min-width: 100%; }
760
+ // Ensure pagination block stays below dropdowns
761
+ & + .pagination-block {
762
+ position: relative;
763
+ z-index: 0;
658
764
  }
659
765
  }
660
766
 
661
767
  .pagination-block {
768
+ position: relative;
769
+ z-index: 0;
662
770
  margin-top: $jv-space-4;
663
771
  padding-top: $jv-space-4;
664
772
  border-top: 1px solid $jv-border-subtle;
@@ -730,6 +838,22 @@ body.template-topic {
730
838
  margin-top: $jv-space-6;
731
839
  border: 1px solid $jv-border-subtle;
732
840
 
841
+ // Reduce avatar size to 32px (matching post author avatars)
842
+ [component="user/picture"],
843
+ .avatar {
844
+ width: 32px !important;
845
+ height: 32px !important;
846
+ min-width: 32px !important;
847
+ min-height: 32px !important;
848
+ font-size: $jv-font-size-sm !important;
849
+ line-height: 32px !important;
850
+ }
851
+
852
+ // Hide user status indicator (green dot)
853
+ [component="user/status"] {
854
+ display: none !important;
855
+ }
856
+
733
857
  .quick-reply-header {
734
858
  margin-bottom: $jv-space-4;
735
859
  font-weight: 600;
@@ -203,7 +203,7 @@
203
203
 
204
204
  /**
205
205
  * Initialize sidebar toggle click handler
206
- * Ensures the sidebar can be toggled and saves user preference
206
+ * Custom handler needed because Harmony's handler targets .sidebar but JAVIS uses .sidebar-left
207
207
  */
208
208
  function initSidebarToggle() {
209
209
  var $toggle = $('[component="sidebar/toggle"]');
@@ -431,7 +431,14 @@
431
431
  }
432
432
 
433
433
  // Find the existing controls row (contains New Topic button)
434
- var $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-2').first();
434
+ // Try mb-0, mb-1 and mb-2 for compatibility
435
+ var $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-0').first();
436
+ if (!$controlsRow.length) {
437
+ $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-1').first();
438
+ }
439
+ if (!$controlsRow.length) {
440
+ $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-2').first();
441
+ }
435
442
  if (!$controlsRow.length) {
436
443
  return;
437
444
  }
@@ -26,39 +26,16 @@
26
26
  <div class="col-lg-6 col-sm-12 ms-auto">
27
27
  {{{ end }}}
28
28
 
29
- <div class="d-flex justify-content-between py-2 mb-2 gap-1">
29
+ <!-- Controls row for composer prompt injection -->
30
+ <div class="d-flex justify-content-between py-2 mb-0 gap-1">
30
31
  {{{ if canPost }}}
31
- <button id="new_topic" class="btn btn-primary btn-sm">[[category:new-topic-button]]</button>
32
+ <button id="new_topic" class="btn btn-primary btn-sm d-none">[[category:new-topic-button]]</button>
32
33
  {{{ end }}}
33
- {{{ if (!loggedIn && !canPost) }}}
34
- <a href="{config.relative_path}/login" class="btn btn-primary btn-sm">[[category:guest-login-post]]</a>
35
- {{{ end }}}
36
-
37
- <div class="d-flex justify-content-end gap-1">
38
- <!-- IMPORT partials/category/filter-dropdown-right.tpl -->
34
+ </div>
39
35
 
40
- <div id="options-dropdown" class="btn-group dropdown dropdown-right bottom-sheet">
41
- <button type="button" class="btn btn-ghost btn-sm d-flex align-items-center gap-2 ff-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
42
- <i class="fa fa-fw fa-gear text-primary"></i>
43
- </button>
44
- <ul class="dropdown-menu p-1 text-sm" role="menu">
45
- <li class="py-1 px-3">
46
- <div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
47
- <label class="form-check-label text-nowrap" for="showAllPosts">[[feed:show-all-posts]]</label>
48
- <input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showAllPosts" {{{ if showAllPosts }}}checked{{{ end }}}>
49
- </div>
50
- </li>
51
- {{{ if loggedIn }}}
52
- <li class="py-1 px-3">
53
- <div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
54
- <label class="form-check-label text-nowrap" for="showFollowedUsers">[[feed:followed-users-only]]</label>
55
- <input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showFollowedUsers" {{{ if showFollowed }}}checked{{{ end }}}>
56
- </div>
57
- </li>
58
- {{{ end }}}
59
- </ul>
60
- </div>
61
- </div>
36
+ <!-- Category filter - LinkedIn style, right aligned, tight spacing -->
37
+ <div class="d-flex justify-content-end py-1 feed-category-filter">
38
+ <!-- IMPORT partials/category/filter-dropdown-right.tpl -->
62
39
  </div>
63
40
 
64
41
  {{{ if !posts.length }}}
@@ -70,25 +47,23 @@
70
47
  <li component="post" class="shadow-sm mb-3 rounded-2 border posts-list-item {{{ if ./deleted }}} deleted{{{ else }}}{{{ if ./topic.deleted }}} deleted{{{ end }}}{{{ end }}}{{{ if ./topic.scheduled }}} scheduled{{{ end }}}" data-pid="{./pid}" data-uid="{./uid}">
71
48
 
72
49
  <!-- 1. CONTENT (first) -->
73
- <div class="d-flex gap-2 p-3">
74
- <div class="d-none d-lg-block">
75
- <a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "40px", true, "not-responsive")}</a>
76
- </div>
77
- <div class="post-body d-flex flex-column gap-2 flex-grow-1 hover-parent" style="min-width: 0px;">
50
+ <div class="p-3 feed-content-section">
51
+ <div class="post-body d-flex flex-column gap-2 flex-grow-1" style="min-width: 0px;">
52
+ <!-- Topic title (first) -->
78
53
  {{{ if ./isMainPost }}}
79
54
  <a class="lh-1 topic-title fw-semibold fs-5 text-reset text-break d-block" href="{config.relative_path}/topic/{./topic.slug}">
80
55
  {./topic.title}
81
56
  </a>
82
57
  {{{ end }}}
83
58
 
84
- <div class="d-flex gap-1 post-info text-sm align-items-center">
85
- <div class="post-author d-flex align-items-center gap-1">
86
- <a class="d-inline d-lg-none lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "16px", true, "not-responsive")}</a>
87
- <a class="lh-normal fw-semibold text-nowrap" href="{config.relative_path}/user/{./user.userslug}">{./user.displayname}</a>
88
- </div>
59
+ <!-- Author info row (second) -->
60
+ <div class="d-flex gap-2 post-info text-sm align-items-center">
61
+ <a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "24px", true, "not-responsive")}</a>
62
+ <a class="lh-normal fw-semibold text-nowrap" href="{config.relative_path}/user/{./user.userslug}">{./user.displayname}</a>
89
63
  {{{ if !./isMainPost}}}{./repliedString}{{{ else }}}<span class="timeago text-muted lh-normal" title="{./timestampISO}"></span>{{{ end}}}
90
64
  </div>
91
65
 
66
+ <!-- Post content text (third) -->
92
67
  <div component="post/content" class="content text-sm text-break position-relative truncate-post-content">
93
68
  <a href="{config.relative_path}/post/{./pid}" class="stretched-link"></a>
94
69
  {./content}
@@ -101,7 +76,7 @@
101
76
 
102
77
  <!-- 2. IMAGE (second) -->
103
78
  {{{ if (showThumbs && ./topic.thumbs.length)}}}
104
- <div class="p-1 position-relative">
79
+ <div class="p-1 position-relative feed-image-section">
105
80
  <div class="overflow-hidden rounded-1" style="max-height: 300px;">
106
81
  <a href="{config.relative_path}/topic/{./topic.slug}">
107
82
  <img class="w-100" src="{./topic.thumbs.0.url}">
@@ -3,9 +3,9 @@
3
3
  <div class="javis-sidebar-logo">
4
4
  <a href="{relative_path}/" class="javis-logo-link" title="JAVIS Community">
5
5
  <!-- Icon logo (shown when collapsed) -->
6
- <img src="{relative_path}/plugins/@okjavis/nodebb-theme-javis/static/images/logo-icon.png" alt="JAVIS" class="javis-logo-icon visible-closed" />
6
+ <img src="{relative_path}/plugins/nodebb-theme-javis/static/images/logo-icon.png" alt="JAVIS" class="javis-logo-icon visible-closed" />
7
7
  <!-- Full logo (shown when expanded) -->
8
- <img src="{relative_path}/plugins/@okjavis/nodebb-theme-javis/static/images/logo-full.png" alt="JAVIS Community" class="javis-logo-full visible-open" />
8
+ <img src="{relative_path}/plugins/nodebb-theme-javis/static/images/logo-full.png" alt="JAVIS Community" class="javis-logo-full visible-open" />
9
9
  </a>
10
10
  </div>
11
11
 
@@ -38,7 +38,7 @@
38
38
  <!-- User Profile Section (only for logged-in users) -->
39
39
  {{{ if config.loggedIn }}}
40
40
  <div class="sidebar-user-section mx-2 mb-2">
41
- <div class="nav-item dropend usermenu">
41
+ <div class="nav-item dropup usermenu">
42
42
  <a component="header/avatar" id="sidebar_user_dropdown" href="#" role="button"
43
43
  class="nav-link d-flex gap-2 align-items-center text-truncate sidebar-user-trigger"
44
44
  data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
@@ -0,0 +1,102 @@
1
+ {{{ if (!./index && widgets.mainpost-header.length) }}}
2
+ <div data-widget-area="mainpost-header">
3
+ {{{ each widgets.mainpost-header }}}
4
+ {widgets.mainpost-header.html}
5
+ {{{ end }}}
6
+ </div>
7
+ {{{ end }}}
8
+ {{{ if (./parent && !hideParent) }}}
9
+ <!-- IMPORT partials/topic/post-parent.tpl -->
10
+ {{{ end }}}
11
+ <div class="d-flex flex-column gap-2 post-container-parent">
12
+ <!-- Author row: Avatar + Username + Timestamp -->
13
+ <div class="d-flex align-items-center gap-2 post-author-row">
14
+ <a class="d-inline-block position-relative text-decoration-none flex-shrink-0" href="{{{ if ./user.userslug }}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}" aria-label="[[aria:profile-page-for, {./user.displayname}]]">
15
+ {buildAvatar(posts.user, "32px", true, "", "user/picture")}
16
+ </a>
17
+ <div class="d-flex flex-wrap align-items-center gap-1" itemprop="author" itemscope itemtype="https://schema.org/Person">
18
+ <meta itemprop="name" content="{./user.displayname}">
19
+ {{{ if ./user.userslug }}}<meta itemprop="url" content="{config.relative_path}/user/{./user.userslug}">{{{ end }}}
20
+ <a class="fw-semibold text-nowrap post-author-name" href="{{{ if ./user.userslug }}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}" data-username="{posts.user.username}" data-uid="{posts.user.uid}">{posts.user.displayname}</a>
21
+ <span class="text-muted post-timestamp">{generateWrote(@value, config.timeagoCutoff)}</span>
22
+ <i component="post/edit-indicator" class="fa fa-edit text-muted{{{ if privileges.posts:history }}} pointer{{{ end }}} edit-icon {{{ if !posts.editor.username }}}hidden{{{ end }}}" title="[[global:edited-timestamp, {isoTimeToLocaleString(./editedISO, config.userLang)}]]"></i>
23
+ <span data-editor="{posts.editor.userslug}" component="post/editor" class="visually-hidden">[[global:last-edited-by, {posts.editor.username}]] <span class="timeago" title="{isoTimeToLocaleString(posts.editedISO, config.userLang)}"></span></span>
24
+ </div>
25
+ <div class="d-flex align-items-center gap-1 ms-auto">
26
+ <span class="bookmarked opacity-0 text-primary"><i class="fa fa-bookmark-o"></i></span>
27
+ <a href="{config.relative_path}/post/{encodeURIComponent(./pid)}" class="post-index text-muted d-none d-md-inline">#{increment(./index, "1")}</a>
28
+ </div>
29
+ </div>
30
+
31
+ <!-- Post content - full width below author row -->
32
+ <div class="post-container d-flex flex-column w-100" style="min-width:0;">
33
+ <div class="content text-break" component="post/content" itemprop="text">
34
+ {posts.content}
35
+ </div>
36
+
37
+ <div component="post/footer" class="post-footer border-bottom pb-2">
38
+ {{{ if posts.user.signature }}}
39
+ <div component="post/signature" data-uid="{posts.user.uid}" class="text-xs text-muted mt-2">{posts.user.signature}</div>
40
+ {{{ end }}}
41
+
42
+ <div class="d-flex flex-wrap-reverse gap-2 {{{ if (hideReplies || !posts.replies.count) }}}justify-content-end{{{ else }}}justify-content-between{{{ end }}}">
43
+ {{{ if !hideReplies }}}
44
+ <a component="post/reply-count" data-target-component="post/replies/container" href="#" class="d-flex gap-2 align-items-center btn btn-ghost ff-secondary border rounded-1 p-1 text-muted text-decoration-none text-xs {{{ if (!./replies || shouldHideReplyContainer(@value)) }}}hidden{{{ end }}}">
45
+ <span component="post/reply-count/avatars" class="d-flex gap-1 {{{ if posts.replies.hasMore }}}hasMore{{{ end }}}">
46
+ {{{each posts.replies.users}}}
47
+ <span>{buildAvatar(posts.replies.users, "20px", true, "avatar-tooltip")}</span>
48
+ {{{end}}}
49
+ {{{ if posts.replies.hasMore}}}
50
+ <span style="height: 20px; line-height: 20px;"><i class="fa fa-ellipsis"></i></span>
51
+ {{{ end }}}
52
+ </span>
53
+
54
+ <span class="ms-2 replies-count fw-semibold text-nowrap" component="post/reply-count/text" data-replies="{posts.replies.count}">{posts.replies.text}</span>
55
+ <span class="ms-2 replies-last hidden-xs fw-semibold">[[topic:last-reply-time]] <span class="timeago" title="{posts.replies.timestampISO}"></span></span>
56
+
57
+ <i class="fa fa-fw fa-chevron-down" component="post/replies/open"></i>
58
+ </a>
59
+ {{{ end }}}
60
+ <div component="post/actions" class="d-flex flex-grow-1 align-items-center justify-content-end gap-1 post-tools">
61
+ <!-- IMPORT partials/topic/reactions.tpl -->
62
+ <a component="post/reply" href="#" class="btn btn-ghost btn-sm {{{ if !privileges.topics:reply }}}hidden{{{ end }}}" title="[[topic:reply]]"><i class="fa fa-fw fa-reply text-primary"></i></a>
63
+ <a component="post/quote" href="#" class="btn btn-ghost btn-sm {{{ if !privileges.topics:reply }}}hidden{{{ end }}}" title="[[topic:quote]]"><i class="fa fa-fw fa-quote-right text-primary"></i></a>
64
+
65
+ {{{ if ./announces }}}
66
+ <a component="post/announce-count" href="#" class="btn btn-ghost btn-sm d-flex gap-2 align-items-center" title="[[topic:announcers]]"><i class="fa fa-share-alt text-primary"></i> {./announces}</a>
67
+ {{{ end }}}
68
+
69
+ {{{ if !reputation:disabled }}}
70
+ <div class="d-flex votes align-items-center">
71
+ <a component="post/upvote" href="#" class="btn btn-ghost btn-sm{{{ if posts.upvoted }}} upvoted{{{ end }}}" title="[[topic:upvote-post]]">
72
+ <i class="fa fa-fw fa-chevron-up text-primary"></i>
73
+ </a>
74
+
75
+ <meta itemprop="upvoteCount" content="{posts.upvotes}">
76
+ <meta itemprop="downvoteCount" content="{posts.downvotes}">
77
+ <a href="#" class="px-2 mx-1 btn btn-ghost btn-sm" component="post/vote-count" data-votes="{posts.votes}" title="[[global:voters]]">{posts.votes}</a>
78
+
79
+ {{{ if !downvote:disabled }}}
80
+ <a component="post/downvote" href="#" class="btn btn-ghost btn-sm{{{ if posts.downvoted }}} downvoted{{{ end }}}" title="[[topic:downvote-post]]">
81
+ <i class="fa fa-fw fa-chevron-down text-primary"></i>
82
+ </a>
83
+ {{{ end }}}
84
+ </div>
85
+ {{{ end }}}
86
+
87
+ <!-- IMPORT partials/topic/post-menu.tpl -->
88
+ </div>
89
+ </div>
90
+
91
+ <div component="post/replies/container" class="my-2 col-11 border rounded-1 p-3 hidden-empty"></div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ {{{ if (!./index && widgets.mainpost-footer.length) }}}
97
+ <div data-widget-area="mainpost-footer">
98
+ {{{ each widgets.mainpost-footer }}}
99
+ {widgets.mainpost-footer.html}
100
+ {{{ end }}}
101
+ </div>
102
+ {{{ end }}}
@@ -0,0 +1,20 @@
1
+ <div component="topic/reply/container" class="btn-group {{{ if !privileges.topics:reply }}}hidden{{{ end }}}">
2
+ <a href="{config.relative_path}/compose?tid={tid}" class="d-flex gap-2 align-items-center btn btn-sm btn-primary fw-semibold w-100 justify-content-center" component="topic/reply" data-ajaxify="false" role="button">
3
+ <i class="fa fa-fw fa-reply"></i>
4
+ <span>[[topic:reply]]</span>
5
+ </a>
6
+ <button type="button" class="btn btn-sm btn-primary dropdown-toggle flex-0" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="[[topic:reply-options]]">
7
+ <span class="caret"></span>
8
+ </button>
9
+ <ul class="dropdown-menu dropdown-menu-end p-1 text-sm" role="menu">
10
+ <li><a class="dropdown-item rounded-1" href="#" component="topic/reply-as-topic" role="menuitem">[[topic:reply-as-topic]]</a></li>
11
+ </ul>
12
+ </div>
13
+
14
+ {{{ if loggedIn }}}
15
+ <a href="#" component="topic/reply/locked" class="d-flex gap-2 align-items-center fw-semibold btn btn-sm btn-primary disabled hidden w-100 justify-content-center {{{ if (privileges.topics:reply || !locked) }}}hidden{{{ end }}}" disabled><i class="fa fa-fw fa-lock"></i> [[topic:locked]]</a>
16
+ {{{ else }}}
17
+ {{{ if !privileges.topics:reply }}}
18
+ <a component="topic/reply/guest" href="{config.relative_path}/login" class="d-flex gap-2 align-items-center fw-semibold btn btn-sm btn-primary w-100 justify-content-center"><i class="fa fa-fw fa-sign-in"></i><span>[[topic:guest-login-reply]]</span></a>
19
+ {{{ end }}}
20
+ {{{ end }}}
@@ -5,7 +5,7 @@
5
5
  <!-- IMPORT partials/topic/reply-button.tpl -->
6
6
 
7
7
  <!-- Mark Unread Button -->
8
- {{{ if loggedIn }}}
8
+ {{{ if config.loggedIn }}}
9
9
  <button component="topic/mark-unread" class="btn btn-ghost btn-sm d-flex gap-2 align-items-center w-100 justify-content-start">
10
10
  <i class="fa fa-fw fa-inbox"></i>
11
11
  <span class="fw-semibold text-nowrap">[[topic:mark-unread]]</span>
@@ -0,0 +1,27 @@
1
+ <div class="btn-group bottom-sheet w-100" component="thread/sort">
2
+ <button class="btn btn-ghost btn-sm d-flex gap-2 align-items-center dropdown-toggle w-100 justify-content-start" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="[[aria:post-sort-option, {sortOptionLabel}]]">
3
+ <i class="fa fa-fw fa-arrow-down-wide-short"></i>
4
+ <span class="fw-semibold text-nowrap">{sortOptionLabel}</span>
5
+ </button>
6
+
7
+ <ul class="dropdown-menu p-1 text-sm" role="menu">
8
+ <li>
9
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" class="oldest_to_newest" data-sort="oldest_to_newest" role="menuitem">
10
+ <span class="flex-grow-1">[[topic:oldest-to-newest]]</span>
11
+ <i class="flex-shrink-0 fa fa-fw text-secondary"></i>
12
+ </a>
13
+ </li>
14
+ <li>
15
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" class="newest_to_oldest" data-sort="newest_to_oldest" role="menuitem">
16
+ <span class="flex-grow-1">[[topic:newest-to-oldest]]</span>
17
+ <i class="flex-shrink-0 fa fa-fw text-secondary"></i>
18
+ </a>
19
+ </li>
20
+ <li>
21
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" class="most_votes" data-sort="most_votes" role="menuitem">
22
+ <span class="flex-grow-1">[[topic:most-votes]]</span>
23
+ <i class="flex-shrink-0 fa fa-fw text-secondary"></i>
24
+ </a>
25
+ </li>
26
+ </ul>
27
+ </div>
@@ -0,0 +1,9 @@
1
+ {{{ if privileges.view_thread_tools }}}
2
+ <div class="btn-group thread-tools bottom-sheet w-100">
3
+ <button class="btn btn-ghost btn-sm d-flex align-items-center gap-2 dropdown-toggle w-100 justify-content-start" data-bs-toggle="dropdown" type="button" aria-haspopup="true" aria-expanded="false">
4
+ <i class="fa fa-fw fa-gear"></i>
5
+ <span class="fw-semibold text-nowrap">[[topic:thread-tools.title]]</span>
6
+ </button>
7
+ <ul class="dropdown-menu p-1 text-sm" role="menu"></ul>
8
+ </div>
9
+ {{{ end }}}
@@ -0,0 +1,60 @@
1
+ {{{ if config.loggedIn }}}
2
+ <div class="btn-group bottom-sheet w-100" component="topic/watch">
3
+ <button class="btn btn-ghost btn-sm d-flex gap-2 align-items-center dropdown-toggle w-100 justify-content-start" data-bs-toggle="dropdown" type="button" aria-haspopup="true" aria-expanded="false">
4
+ <span component="topic/following/menu" class="d-flex gap-2 align-items-center{{{ if !isFollowing }}} hidden{{{ end }}}">
5
+ <i class="fa fa-fw fa-bell-o"></i>
6
+ <span class="fw-semibold text-nowrap">[[topic:watching]]</span>
7
+ </span>
8
+
9
+ <span component="topic/not-following/menu" class="d-flex gap-2 align-items-center{{{ if !isNotFollowing}}} hidden{{{ end }}}">
10
+ <i class="fa fa-fw fa-bell-slash-o"></i>
11
+ <span class="fw-semibold text-nowrap">[[topic:not-watching]]</span>
12
+ </span>
13
+
14
+ <span component="topic/ignoring/menu" class="d-flex gap-2 align-items-center{{{ if !isIgnoring }}} hidden{{{ end }}}">
15
+ <i class="fa fa-fw fa-eye-slash"></i>
16
+ <span class="fw-semibold text-nowrap">[[topic:ignoring]]</span>
17
+ </span>
18
+ </button>
19
+ <ul class="dropdown-menu p-1 text-sm" role="menu">
20
+ <li>
21
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="topic/following" role="menuitem">
22
+ <div class="flex-grow-1 d-flex flex-column">
23
+ <span class="d-flex align-items-center gap-2">
24
+ <i class="fa fa-fw fa-bell-o text-secondary"></i>
25
+ <span class="flex-grow-1 fw-semibold">[[topic:watching]]</span>
26
+ </span>
27
+ <div class="help-text text-secondary text-xs">[[topic:watching.description]]</div>
28
+ </div>
29
+ <span class="flex-shrink-0"><i component="topic/following/check" class="fa fa-fw {{{ if isFollowing }}}fa-check{{{ end }}}"></i></span>
30
+ </a>
31
+ </li>
32
+
33
+ <li>
34
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="topic/not-following" role="menuitem">
35
+ <div class="flex-grow-1 d-flex flex-column">
36
+ <span class="d-flex align-items-center gap-2">
37
+ <i class="fa fa-fw fa-bell-slash-o text-secondary"></i>
38
+ <span class="flex-grow-1 fw-semibold">[[topic:not-watching]]</span>
39
+ </span>
40
+ <div class="help-text text-secondary text-xs">[[topic:not-watching.description]]</div>
41
+ </div>
42
+ <span class="flex-shrink-0"><i component="topic/not-following/check" class="fa fa-fw {{{ if isNotFollowing }}}fa-check{{{ end }}}"></i></span>
43
+ </a>
44
+ </li>
45
+
46
+ <li>
47
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="topic/ignoring" role="menuitem">
48
+ <div class="flex-grow-1 d-flex flex-column">
49
+ <span class="d-flex align-items-center gap-2">
50
+ <i class="fa fa-fw fa-eye-slash text-secondary"></i>
51
+ <span class="flex-grow-1 fw-semibold">[[topic:ignoring]]</span>
52
+ </span>
53
+ <div class="help-text text-secondary text-xs">[[topic:ignoring.description]]</div>
54
+ </div>
55
+ <span class="flex-shrink-0"><i component="topic/ignoring/check" class="fa fa-fw {{{ if isIgnoring }}}fa-check{{{ end }}}"></i></span>
56
+ </a>
57
+ </li>
58
+ </ul>
59
+ </div>
60
+ {{{ end }}}
@@ -57,9 +57,9 @@
57
57
  <div class="d-flex gap-2 align-items-center mt-2 hidden-empty" component="topic/thumb/list"><!-- IMPORT partials/topic/thumbs.tpl --></div>
58
58
  </div>
59
59
 
60
- <div class="topic-layout d-flex gap-4 mb-4 mb-lg-0">
60
+ <div class="topic topic-layout d-flex gap-4 mb-4 mb-lg-0">
61
61
  <!-- Main Content Area -->
62
- <div class="topic flex-grow-1" style="min-width: 0;">
62
+ <div class="topic-main flex-grow-1" style="min-width: 0;">
63
63
  {{{ if merger }}}
64
64
  <!-- IMPORT partials/topic/merged-message.tpl -->
65
65
  {{{ end }}}
package/theme.json CHANGED
@@ -4,5 +4,6 @@
4
4
  "description": "Modern, premium NodeBB theme for JAVIS Community",
5
5
  "url": "https://github.com/javis-admin/nodebb-community-theme",
6
6
  "screenshot": "screenshot.png",
7
- "baseTheme": "nodebb-theme-harmony"
7
+ "baseTheme": "nodebb-theme-harmony",
8
+ "templates": "templates"
8
9
  }