@okjavis/nodebb-theme-javis 1.7.0 → 2.0.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": "1.7.0",
3
+ "version": "2.0.0",
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
@@ -7,7 +7,8 @@
7
7
  "baseTheme": "nodebb-theme-harmony",
8
8
  "hooks": [
9
9
  { "hook": "filter:widgets.getAreas", "method": "defineWidgetAreas" },
10
- { "hook": "filter:admin.header.build", "method": "addAdminNavigation" }
10
+ { "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
11
+ { "hook": "filter:config.get", "method": "getThemeConfig" }
11
12
  ],
12
13
  "staticDirs": {
13
14
  "static": "./static"
package/scss/_base.scss CHANGED
@@ -11,12 +11,6 @@ nav[component="sidebar/right"],
11
11
 
12
12
  // No custom layout overrides - use Harmony defaults
13
13
 
14
- // Search bar width override
15
- .search-widget .input-group {
16
- max-width: 650px;
17
- margin: 0 auto;
18
- }
19
-
20
14
  // Left sidebar layout - navigation at top, profile/collapse at bottom
21
15
  nav[component="sidebar/left"] {
22
16
  justify-content: flex-start !important; // Remove space-between
@@ -3,16 +3,6 @@
3
3
  // Modern, clean buttons with smooth interactions
4
4
  // ===========================================================
5
5
 
6
- // Semantic colors
7
- $jv-success: #10b981;
8
- $jv-success-hover: #059669;
9
- $jv-danger: #ef4444;
10
- $jv-danger-hover: #dc2626;
11
- $jv-warning: #f59e0b;
12
- $jv-warning-hover: #d97706;
13
- $jv-info: #3b82f6;
14
- $jv-info-hover: #2563eb;
15
-
16
6
  // ===========================================================
17
7
  // BASE BUTTON
18
8
  // ===========================================================
package/scss/_forms.scss CHANGED
@@ -37,23 +37,6 @@ textarea {
37
37
  min-height: 100px;
38
38
  }
39
39
 
40
- // ===========================================================
41
- // SEARCH BAR
42
- // ===========================================================
43
-
44
- .search-widget .input-group {
45
- .form-control {
46
- border-top-right-radius: 0;
47
- border-bottom-right-radius: 0;
48
- }
49
-
50
- .btn-primary {
51
- border-top-left-radius: 0;
52
- border-bottom-left-radius: 0;
53
- padding: 0 18px;
54
- }
55
- }
56
-
57
40
  // ===========================================================
58
41
  // COMPOSER TRIGGER (Write a Post…)
59
42
  // ===========================================================
@@ -0,0 +1,281 @@
1
+ // ============================================
2
+ // HEADER / TOPBAR - Sticky Navigation Bar
3
+ // ============================================
4
+ // Industry-standard sticky header with centered search
5
+ // Inspired by: Slack, Linear, Notion, GitHub
6
+
7
+ // ===========================================================
8
+ // BRAND CONTAINER (Top Bar)
9
+ // ===========================================================
10
+ .brand-container {
11
+ position: sticky;
12
+ top: 0;
13
+ z-index: 1020; // Below modals (1050) but above content
14
+ background: $jv-surface;
15
+ border-bottom: 1px solid $jv-border-subtle;
16
+ margin: 0 !important; // No margins - ever
17
+ padding: $jv-space-3 $jv-space-4 !important; // 12px vertical, 16px horizontal
18
+ min-height: 64px; // 12px top + 40px search + 12px bottom
19
+ max-width: 100% !important; // Override container-lg max-width
20
+ width: 100% !important; // Full width always
21
+ display: flex;
22
+ align-items: center;
23
+ transition: box-shadow $jv-transition-base;
24
+ box-sizing: border-box;
25
+
26
+ // Override Bootstrap container classes
27
+ &.container-lg,
28
+ &.container-md,
29
+ &.container {
30
+ max-width: 100% !important;
31
+ padding-left: $jv-space-4 !important;
32
+ padding-right: $jv-space-4 !important;
33
+ }
34
+
35
+ // Subtle shadow on scroll (added via JS)
36
+ &.scrolled {
37
+ box-shadow: $jv-shadow-sm;
38
+ }
39
+
40
+ // Inner flex container
41
+ > .col-12 {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: space-between;
45
+ gap: $jv-space-4;
46
+ border-bottom: none !important; // Remove Harmony's border
47
+ padding: 0 $jv-space-4 !important; // Horizontal padding
48
+ margin: 0 !important;
49
+ width: 100% !important; // Ensure full width - no right margin
50
+ }
51
+
52
+ // Brand/Logo wrapper - compact padding
53
+ [component="brand/wrapper"] {
54
+ flex-shrink: 0;
55
+ padding: $jv-space-1 !important; // Minimal padding
56
+ border-radius: $jv-radius-sm;
57
+ transition: background $jv-transition-fast;
58
+ gap: $jv-space-2 !important; // Tighter gap
59
+
60
+ &:hover {
61
+ background: $jv-hover-bg;
62
+ }
63
+ }
64
+
65
+ // Logo sizing - compact
66
+ [component="brand/logo"] {
67
+ max-height: 28px; // Compact logo height - matches sidebar logo
68
+ width: auto;
69
+ }
70
+
71
+ // Site title (if shown)
72
+ [component="siteTitle"] {
73
+ h1 {
74
+ font-size: $jv-font-size-base !important; // Smaller title
75
+ font-weight: 600 !important;
76
+ margin: 0 !important;
77
+ }
78
+ }
79
+ }
80
+
81
+ // ===========================================================
82
+ // SEARCH BAR - Centered, Prominent
83
+ // ===========================================================
84
+ [data-widget-area="brand-header"] {
85
+ flex: 1;
86
+ display: flex;
87
+ justify-content: center;
88
+ align-items: center;
89
+ padding: 0 $jv-space-4 !important;
90
+ }
91
+
92
+ // Search widget in header
93
+ .brand-container .search-widget,
94
+ [data-widget-area="brand-header"] .search-widget {
95
+ width: 100%;
96
+ max-width: 560px;
97
+
98
+ .input-group {
99
+ display: flex;
100
+ align-items: center;
101
+ background: $jv-bg; // Light gray background (matches app bg)
102
+ border-radius: $jv-radius-pill;
103
+ border: 1px solid transparent;
104
+ transition: all $jv-transition-fast;
105
+ padding: 0;
106
+ overflow: hidden;
107
+
108
+ // Hover state
109
+ &:hover {
110
+ background: darken($jv-bg, 2%);
111
+ }
112
+
113
+ // Focus-within state
114
+ &:focus-within {
115
+ background: $jv-surface;
116
+ border-color: $jv-primary;
117
+ box-shadow: $jv-focus-ring;
118
+ }
119
+
120
+ // Search icon button
121
+ .btn-outline-secondary,
122
+ .input-group-text {
123
+ background: transparent !important;
124
+ border: none !important;
125
+ padding: $jv-space-2 $jv-space-3;
126
+ padding-right: 0;
127
+ color: $jv-text-soft !important;
128
+
129
+ i {
130
+ font-size: 14px;
131
+ }
132
+
133
+ &:hover {
134
+ background: transparent !important;
135
+ color: $jv-text-muted !important;
136
+ }
137
+ }
138
+
139
+ // Search input field
140
+ .form-control {
141
+ background: transparent !important;
142
+ border: none !important;
143
+ box-shadow: none !important;
144
+ padding: $jv-space-2 $jv-space-3;
145
+ padding-left: $jv-space-2;
146
+ font-size: $jv-font-size-base;
147
+ color: $jv-text-main;
148
+ height: 40px; // Consistent height
149
+
150
+ // Placeholder styling
151
+ &::placeholder {
152
+ color: $jv-text-soft;
153
+ opacity: 1;
154
+ font-weight: 400;
155
+ }
156
+
157
+ &:focus {
158
+ outline: none;
159
+ box-shadow: none !important;
160
+
161
+ &::placeholder {
162
+ color: $jv-text-muted;
163
+ }
164
+ }
165
+ }
166
+
167
+ // Search submit button (blue button)
168
+ .btn-primary,
169
+ .btn[type="submit"] {
170
+ height: 40px;
171
+ min-width: 40px;
172
+ padding: 0 $jv-space-4;
173
+ border-radius: 0 $jv-radius-pill $jv-radius-pill 0 !important;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ }
178
+
179
+ // Keyboard shortcut hint (optional enhancement)
180
+ &::after {
181
+ content: "⌘K";
182
+ display: none; // Enable when JS shortcut is implemented
183
+ padding: $jv-space-1 $jv-space-2;
184
+ margin-right: $jv-space-2;
185
+ background: rgba(0, 0, 0, 0.06);
186
+ border-radius: $jv-radius-xs;
187
+ font-size: $jv-font-size-xs;
188
+ color: $jv-text-soft;
189
+ font-family: $jv-font-sans;
190
+ }
191
+ }
192
+ }
193
+
194
+ // ===========================================================
195
+ // SEARCH DROPDOWN RESULTS
196
+ // ===========================================================
197
+ .brand-container .search-dropdown,
198
+ .search-widget .dropdown-menu {
199
+ width: 100%;
200
+ min-width: 400px;
201
+ max-width: 560px;
202
+ margin-top: $jv-space-2 !important;
203
+ padding: $jv-space-2;
204
+ border-radius: $jv-radius-md;
205
+ border: 1px solid $jv-border-subtle;
206
+ box-shadow: $jv-shadow-lg;
207
+ background: $jv-surface;
208
+
209
+ // Quick search results
210
+ .quick-search-results-container {
211
+ max-height: 400px;
212
+ overflow-y: auto;
213
+
214
+ // Individual result items
215
+ .search-result {
216
+ padding: $jv-space-2 $jv-space-3;
217
+ border-radius: $jv-radius-sm;
218
+ transition: background $jv-transition-fast;
219
+
220
+ &:hover {
221
+ background: $jv-hover-bg;
222
+ }
223
+ }
224
+ }
225
+
226
+ // Advanced search link
227
+ .advanced-search-link {
228
+ color: $jv-text-muted;
229
+ transition: color $jv-transition-fast;
230
+
231
+ &:hover {
232
+ color: $jv-primary;
233
+ }
234
+ }
235
+ }
236
+
237
+ // ===========================================================
238
+ // RESPONSIVE ADJUSTMENTS
239
+ // ===========================================================
240
+ @media (max-width: 991px) {
241
+ .brand-container {
242
+ // On tablet/mobile, maintain proper spacing
243
+ min-height: 56px !important;
244
+ margin: 0 !important;
245
+ padding: $jv-space-2 0 !important;
246
+
247
+ > .col-12 {
248
+ padding: 0 $jv-space-2 !important;
249
+ width: 100% !important;
250
+ }
251
+
252
+ [data-widget-area="brand-header"] {
253
+ padding: 0 $jv-space-2 !important;
254
+ }
255
+
256
+ .search-widget {
257
+ max-width: 100%;
258
+ }
259
+ }
260
+ }
261
+
262
+ @media (max-width: 767px) {
263
+ .brand-container {
264
+ // Hide search in header on mobile (use mobile header instead)
265
+ [data-widget-area="brand-header"] {
266
+ display: none;
267
+ }
268
+
269
+ > .col-12 {
270
+ justify-content: center;
271
+ }
272
+ }
273
+ }
274
+
275
+ // ===========================================================
276
+ // MAIN PANEL - Remove top margin for cohesive layout
277
+ // ===========================================================
278
+ #panel {
279
+ margin-top: 0 !important; // Remove mt-3 class margin
280
+ }
281
+
@@ -6,14 +6,18 @@
6
6
  // ===========================================================
7
7
  // JAVIS SIDEBAR LOGO
8
8
  // ===========================================================
9
+ // Height should match the sticky header (64px) for visual alignment
9
10
  .javis-sidebar-logo {
10
11
  display: flex;
11
12
  align-items: center;
12
13
  justify-content: center;
13
- padding: $jv-space-6 $jv-space-4; // 12px top/bottom, 8px left/right
14
- margin-bottom: $jv-space-4; // 8px gap before nav - industry standard
14
+ height: 65px; // Match header height (12px + 40px + 12px)
15
+ min-height: 65px;
16
+ padding: 0 $jv-space-4; // Only horizontal padding
17
+ margin-bottom: $jv-space-3; // 12px space below border before nav items
15
18
  border-bottom: 1px solid $jv-border-subtle;
16
19
  flex-shrink: 0;
20
+ box-sizing: border-box;
17
21
 
18
22
  .javis-logo-link {
19
23
  display: flex;
@@ -22,32 +26,39 @@
22
26
  text-decoration: none;
23
27
  }
24
28
 
25
- // Icon logo (collapsed state) - 32x32px
29
+ // Icon logo (collapsed state) - 28px to match header logo
26
30
  .javis-logo-icon {
27
- width: 32px;
28
- height: 32px;
31
+ width: 28px;
32
+ height: 28px;
29
33
  object-fit: contain;
30
34
  }
31
35
 
32
- // Full logo (expanded state) - max 140px width
36
+ // Full logo (expanded state) - max 140px width, 28px height
33
37
  .javis-logo-full {
34
38
  max-width: 140px;
35
- height: 32px;
39
+ height: 28px;
36
40
  object-fit: contain;
37
41
  }
38
42
  }
39
43
 
40
- // Adjust when sidebar is open
44
+ // Adjust when sidebar is open - same height, just different horizontal padding
41
45
  .sidebar-left.open .javis-sidebar-logo {
42
- padding: $jv-space-6 $jv-space-10; // 12px vertical, 20px horizontal when expanded
46
+ padding: 0 $jv-space-6; // Only horizontal padding changes
43
47
  justify-content: flex-start;
44
48
  }
45
49
 
46
50
  // ===========================================================
47
51
  // LEFT SIDEBAR (Main Navigation)
48
52
  // ===========================================================
53
+
54
+ // Override Bootstrap's bg-light class on sidebar
55
+ .sidebar.bg-light,
56
+ nav.sidebar.bg-light {
57
+ background-color: $jv-surface !important; // Pure white - cohesive with header
58
+ }
59
+
49
60
  .sidebar {
50
- background: $jv-surface;
61
+ background: $jv-surface !important; // Pure white - cohesive with header
51
62
  border-right: 1px solid $jv-border-subtle;
52
63
  padding-top: 0 !important; // Remove top padding so logo sits at top edge
53
64
 
@@ -172,28 +183,6 @@ div[data-widget-area] {
172
183
  }
173
184
  }
174
185
 
175
- // ===========================================================
176
- // SEARCH WIDGET
177
- // ===========================================================
178
- .search-widget {
179
- .input-group {
180
- display: flex;
181
-
182
- .form-control {
183
- flex: 1;
184
- border-top-right-radius: 0;
185
- border-bottom-right-radius: 0;
186
- border-right: none;
187
- }
188
-
189
- .btn {
190
- border-top-left-radius: 0;
191
- border-bottom-left-radius: 0;
192
- padding: 8px 14px;
193
- }
194
- }
195
- }
196
-
197
186
  // ===========================================================
198
187
  // ONLINE USERS WIDGET
199
188
  // ===========================================================
@@ -515,7 +504,7 @@ div[data-widget-area="right"],
515
504
 
516
505
  // Sticky positioning - stays visible when scrolling
517
506
  position: sticky;
518
- top: 36px; // Adjust based on your header height
507
+ top: 48px; // Adjust based on your header height
519
508
  align-self: flex-start;
520
509
  max-height: calc(100vh - 100px); // Account for header + padding
521
510
  overflow-y: auto; // Allow scrolling if content is too tall
@@ -667,18 +656,7 @@ div[data-widget-area="right"] {
667
656
  }
668
657
 
669
658
  .tag-topic-count {
670
- font-size: $jv-font-size-xs !important; // 12px
671
- font-weight: 400 !important;
672
- color: $jv-text-soft !important;
673
- line-height: 1 !important;
674
-
675
- // Show count in parentheses style
676
- &::before {
677
- content: "(";
678
- }
679
- &::after {
680
- content: ")";
681
- }
659
+ display: none !important;
682
660
  }
683
661
 
684
662
  // [Enhancement 3] Color-code tags by popularity
@@ -851,5 +829,4 @@ div[data-widget-area="right"] {
851
829
  }
852
830
  }
853
831
  }
854
- }
855
-
832
+ }
@@ -0,0 +1,547 @@
1
+ // ===========================================================
2
+ // TOPIC PAGE – Individual Topic/Thread View
3
+ // Clean, cohesive design matching platform theme
4
+ // ===========================================================
5
+
6
+ // ===========================================================
7
+ // BREADCRUMB – Softer, More Subtle
8
+ // ===========================================================
9
+ body.template-topic {
10
+ .breadcrumb {
11
+ background: transparent;
12
+ padding: $jv-space-2 0;
13
+ margin-bottom: $jv-space-4;
14
+ font-size: $jv-font-size-sm;
15
+
16
+ .breadcrumb-item {
17
+ color: $jv-text-soft;
18
+
19
+ a {
20
+ color: $jv-text-muted;
21
+ text-decoration: none;
22
+ transition: color $jv-transition-fast;
23
+
24
+ &:hover {
25
+ color: $jv-primary;
26
+ }
27
+ }
28
+
29
+ // Separator styling
30
+ + .breadcrumb-item::before {
31
+ color: $jv-text-soft;
32
+ content: "/";
33
+ padding: 0 $jv-space-2;
34
+ }
35
+
36
+ // Active/current item
37
+ &.active {
38
+ color: $jv-text-muted;
39
+ font-weight: 500;
40
+ }
41
+ }
42
+ }
43
+
44
+ // ===========================================================
45
+ // TOPIC HEADER – Title and Meta
46
+ // ===========================================================
47
+ .topic-header,
48
+ [component="topic/header"] {
49
+ margin-bottom: $jv-space-6;
50
+
51
+ // Topic title
52
+ h1,
53
+ [component="topic/title"] {
54
+ font-size: 28px;
55
+ font-weight: 700;
56
+ line-height: $jv-line-height-tight;
57
+ letter-spacing: -0.02em;
58
+ color: $jv-text-main;
59
+ margin-bottom: $jv-space-3;
60
+ }
61
+ }
62
+
63
+ // ===========================================================
64
+ // META PILLS – Category, Tags, Stats
65
+ // ===========================================================
66
+ .topic-tags,
67
+ [component="topic/tags"] {
68
+ display: flex;
69
+ flex-wrap: wrap;
70
+ gap: $jv-space-2;
71
+ margin-bottom: $jv-space-4;
72
+
73
+ .badge,
74
+ .tag {
75
+ font-size: $jv-font-size-xs;
76
+ font-weight: 500;
77
+ padding: $jv-space-1 $jv-space-3;
78
+ border-radius: $jv-radius-pill;
79
+ background: rgba(0, 0, 0, 0.05);
80
+ color: $jv-text-muted;
81
+ border: none;
82
+ transition: background-color $jv-transition-fast, color $jv-transition-fast;
83
+
84
+ &:hover {
85
+ background: $jv-primary-soft;
86
+ color: $jv-primary;
87
+ }
88
+ }
89
+ }
90
+
91
+ // Category badge in topic
92
+ .category-badge,
93
+ [component="category/link"] .badge {
94
+ background: rgba(0, 0, 0, 0.06);
95
+ color: $jv-text-muted;
96
+ font-weight: 500;
97
+ }
98
+
99
+ // ===========================================================
100
+ // POST CONTAINER – Clean, Borderless Design
101
+ // ===========================================================
102
+ .topic {
103
+ .posts-container {
104
+ max-width: 100%;
105
+ width: 100%;
106
+ }
107
+
108
+ // Individual post
109
+ [component="post"] {
110
+ background: $jv-surface;
111
+ border-radius: $jv-radius-md;
112
+ margin-bottom: $jv-space-4;
113
+ border: none; // Borderless as per theme
114
+ box-shadow: none; // No shadow for cleaner look
115
+
116
+ // Post container inner
117
+ .post-container {
118
+ padding: $jv-space-6;
119
+ border: none;
120
+ background: transparent;
121
+ }
122
+
123
+ // Selected state (when linked directly)
124
+ &.selected .post-container {
125
+ background: $jv-selected-bg;
126
+ border-radius: $jv-radius-md;
127
+ }
128
+ }
129
+ }
130
+
131
+ // ===========================================================
132
+ // POST HEADER – Avatar, Username, Time
133
+ // ===========================================================
134
+ .post-header {
135
+ display: flex;
136
+ align-items: center;
137
+ gap: $jv-space-3;
138
+ margin-bottom: $jv-space-4;
139
+ font-size: $jv-font-size-sm;
140
+
141
+ // Avatar
142
+ .avatar {
143
+ width: 40px;
144
+ height: 40px;
145
+ border-radius: 50%;
146
+ object-fit: cover;
147
+ flex-shrink: 0;
148
+ }
149
+
150
+ // User info container
151
+ .user-info {
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 2px;
155
+ }
156
+
157
+ // Username
158
+ .username,
159
+ [component="post/header/username"] {
160
+ font-weight: 600;
161
+ color: $jv-text-main;
162
+ text-decoration: none;
163
+
164
+ &:hover {
165
+ color: $jv-primary;
166
+ }
167
+ }
168
+
169
+ // Timestamp
170
+ .timeago,
171
+ [component="post/timestamp"] {
172
+ color: $jv-text-soft;
173
+ font-size: $jv-font-size-xs;
174
+ }
175
+
176
+ // Post index/number - Subtle
177
+ .post-index,
178
+ [component="post/header/index"] {
179
+ color: $jv-text-soft;
180
+ font-size: $jv-font-size-xs;
181
+ font-weight: 400;
182
+ opacity: 0.6;
183
+
184
+ &::before {
185
+ content: "#";
186
+ }
187
+ }
188
+
189
+ // Bookmarked indicator
190
+ .bookmarked {
191
+ color: $jv-warning;
192
+ }
193
+ }
194
+
195
+ // ===========================================================
196
+ // POST CONTENT – Typography
197
+ // ===========================================================
198
+ [component="post/content"] {
199
+ font-size: $jv-font-size-base;
200
+ line-height: $jv-line-height-relaxed;
201
+ color: $jv-text-main;
202
+
203
+ p {
204
+ margin-bottom: $jv-space-4;
205
+
206
+ &:last-child {
207
+ margin-bottom: 0;
208
+ }
209
+ }
210
+
211
+ // Code blocks
212
+ pre, code {
213
+ background: rgba(0, 0, 0, 0.04);
214
+ border-radius: $jv-radius-sm;
215
+ font-size: $jv-font-size-sm;
216
+ }
217
+
218
+ code {
219
+ padding: 2px 6px;
220
+ }
221
+
222
+ pre {
223
+ padding: $jv-space-4;
224
+ overflow-x: auto;
225
+
226
+ code {
227
+ padding: 0;
228
+ background: transparent;
229
+ }
230
+ }
231
+
232
+ // Blockquotes
233
+ blockquote {
234
+ border-left: 3px solid $jv-border-strong;
235
+ padding-left: $jv-space-4;
236
+ margin: $jv-space-4 0;
237
+ color: $jv-text-muted;
238
+ font-style: italic;
239
+ }
240
+
241
+ // Images
242
+ img {
243
+ max-width: 100%;
244
+ border-radius: $jv-radius-sm;
245
+ }
246
+
247
+ // Links
248
+ a {
249
+ color: $jv-primary;
250
+
251
+ &:hover {
252
+ text-decoration: underline;
253
+ }
254
+ }
255
+ }
256
+
257
+ // ===========================================================
258
+ // POST FOOTER – Action Bar
259
+ // ===========================================================
260
+ [component="post/footer"],
261
+ .post-footer {
262
+ padding-top: $jv-space-4;
263
+ margin-top: $jv-space-4;
264
+ border-top: 1px solid $jv-border-subtle;
265
+ }
266
+
267
+ // ===========================================================
268
+ // ACTION ICONS – Ghost Button Style
269
+ // ===========================================================
270
+ [component="post/actions"] {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: $jv-space-2;
274
+ flex-wrap: wrap;
275
+
276
+ // All action buttons - Ghost style
277
+ .btn,
278
+ [component="post/reply"],
279
+ [component="post/quote"],
280
+ [component="post/bookmark"],
281
+ [component="post/upvote"],
282
+ [component="post/downvote"],
283
+ [component="post/tools"] {
284
+ background: transparent !important;
285
+ border: none !important;
286
+ color: $jv-text-muted;
287
+ padding: $jv-space-2 $jv-space-3;
288
+ border-radius: $jv-radius-sm;
289
+ font-size: $jv-font-size-sm;
290
+ font-weight: 500;
291
+ transition: background-color $jv-transition-fast, color $jv-transition-fast;
292
+ display: inline-flex;
293
+ align-items: center;
294
+ gap: $jv-space-2;
295
+
296
+ i {
297
+ font-size: 16px;
298
+ }
299
+
300
+ &:hover {
301
+ background: $jv-hover-bg !important;
302
+ color: $jv-text-main;
303
+ }
304
+
305
+ &:focus-visible {
306
+ outline: none;
307
+ box-shadow: $jv-focus-ring;
308
+ }
309
+
310
+ // Active states (voted, bookmarked)
311
+ &.active,
312
+ &.upvoted,
313
+ &.downvoted,
314
+ &.bookmarked {
315
+ background: $jv-primary-soft !important;
316
+ color: $jv-primary;
317
+ }
318
+ }
319
+
320
+ // Vote counts
321
+ [component="post/vote-count"] {
322
+ font-weight: 600;
323
+ color: $jv-text-muted;
324
+ min-width: 20px;
325
+ text-align: center;
326
+ }
327
+ }
328
+
329
+ // ===========================================================
330
+ // RIGHT SIDEBAR ACTIONS – Ghost Buttons
331
+ // ===========================================================
332
+ .topic-main-buttons,
333
+ [component="topic/actions"],
334
+ .sidebar [data-widget-area] {
335
+ .btn {
336
+ background: transparent !important;
337
+ border: 1px solid transparent !important;
338
+ color: $jv-text-muted;
339
+ padding: $jv-space-2 $jv-space-4;
340
+ border-radius: $jv-radius-sm;
341
+ font-size: $jv-font-size-sm;
342
+ font-weight: 500;
343
+ transition: background-color $jv-transition-fast, color $jv-transition-fast;
344
+ display: inline-flex;
345
+ align-items: center;
346
+ gap: $jv-space-2;
347
+ width: 100%;
348
+ justify-content: flex-start;
349
+ text-align: left;
350
+
351
+ i {
352
+ font-size: 16px;
353
+ width: 20px;
354
+ text-align: center;
355
+ }
356
+
357
+ &:hover {
358
+ background: $jv-hover-bg !important;
359
+ color: $jv-text-main;
360
+ }
361
+
362
+ &:focus-visible {
363
+ outline: none;
364
+ box-shadow: $jv-focus-ring;
365
+ }
366
+
367
+ // Primary action (Reply)
368
+ &.btn-primary {
369
+ background: $jv-primary !important;
370
+ color: #fff;
371
+
372
+ &:hover {
373
+ background: $jv-primary-hover !important;
374
+ color: #fff;
375
+ }
376
+ }
377
+ }
378
+ }
379
+
380
+ // Sidebar action list
381
+ .topic-actions-list,
382
+ .topic-tools {
383
+ display: flex;
384
+ flex-direction: column;
385
+ gap: $jv-space-1;
386
+
387
+ .dropdown-item,
388
+ .action-item {
389
+ padding: $jv-space-2 $jv-space-4;
390
+ border-radius: $jv-radius-sm;
391
+ color: $jv-text-muted;
392
+ font-size: $jv-font-size-sm;
393
+ transition: background-color $jv-transition-fast, color $jv-transition-fast;
394
+
395
+ &:hover {
396
+ background: $jv-hover-bg;
397
+ color: $jv-text-main;
398
+ }
399
+ }
400
+ }
401
+
402
+ // ===========================================================
403
+ // QUICK REPLY BOX
404
+ // ===========================================================
405
+ .quick-reply,
406
+ [component="topic/quickreply"] {
407
+ background: $jv-surface;
408
+ border-radius: $jv-radius-md;
409
+ padding: $jv-space-6;
410
+ margin-top: $jv-space-6;
411
+ border: 1px solid $jv-border-subtle;
412
+
413
+ // Quick reply header
414
+ .quick-reply-header {
415
+ margin-bottom: $jv-space-4;
416
+ font-weight: 600;
417
+ color: $jv-text-main;
418
+ }
419
+
420
+ // Textarea
421
+ textarea,
422
+ .form-control {
423
+ border: 1px solid $jv-border-subtle;
424
+ border-radius: $jv-radius-sm;
425
+ padding: $jv-space-4;
426
+ font-size: $jv-font-size-base;
427
+ line-height: $jv-line-height-base;
428
+ resize: vertical;
429
+ min-height: 120px;
430
+ transition: border-color $jv-transition-fast, box-shadow $jv-transition-fast;
431
+
432
+ &:focus {
433
+ outline: none;
434
+ border-color: $jv-primary;
435
+ box-shadow: $jv-focus-ring;
436
+ }
437
+
438
+ &::placeholder {
439
+ color: $jv-text-soft;
440
+ }
441
+ }
442
+
443
+ // Quick reply footer with buttons
444
+ .quick-reply-footer,
445
+ .composer-actions {
446
+ display: flex;
447
+ justify-content: flex-end;
448
+ gap: $jv-space-3;
449
+ margin-top: $jv-space-4;
450
+ }
451
+ }
452
+
453
+ // ===========================================================
454
+ // REPLY CONTAINER (Nested replies)
455
+ // ===========================================================
456
+ [component="post/replies/container"] {
457
+ margin-top: $jv-space-4;
458
+ padding-left: $jv-space-6;
459
+ border-left: 2px solid $jv-border-subtle;
460
+
461
+ [component="post"] {
462
+ margin-bottom: $jv-space-3;
463
+ padding: $jv-space-4;
464
+ background: rgba(0, 0, 0, 0.02);
465
+ border-radius: $jv-radius-sm;
466
+
467
+ .post-container {
468
+ padding: 0;
469
+ }
470
+ }
471
+ }
472
+
473
+ // ===========================================================
474
+ // TOPIC NAVIGATOR (Floating scroll helper)
475
+ // ===========================================================
476
+ [component="topic/navigator"] {
477
+ background: $jv-surface;
478
+ border-radius: $jv-radius-md;
479
+ box-shadow: $jv-shadow-lg;
480
+ border: 1px solid $jv-border-subtle;
481
+ padding: $jv-space-3;
482
+
483
+ .btn {
484
+ background: transparent;
485
+ border: none;
486
+ color: $jv-text-muted;
487
+ padding: $jv-space-2;
488
+ border-radius: $jv-radius-sm;
489
+
490
+ &:hover {
491
+ background: $jv-hover-bg;
492
+ color: $jv-text-main;
493
+ }
494
+ }
495
+ }
496
+ }
497
+
498
+ // ===========================================================
499
+ // RESPONSIVE ADJUSTMENTS
500
+ // ===========================================================
501
+ @media (max-width: 991px) {
502
+ body.template-topic {
503
+ .topic [component="post"] .post-container {
504
+ padding: $jv-space-4;
505
+ }
506
+
507
+ .post-header {
508
+ flex-wrap: wrap;
509
+ }
510
+
511
+ [component="post/actions"] {
512
+ gap: $jv-space-1;
513
+
514
+ .btn,
515
+ [component="post/reply"],
516
+ [component="post/quote"],
517
+ [component="post/bookmark"],
518
+ [component="post/upvote"],
519
+ [component="post/downvote"] {
520
+ padding: $jv-space-2;
521
+
522
+ // Hide text on smaller screens
523
+ span:not(.fa):not(.fas):not(.far):not(.fab) {
524
+ display: none;
525
+ }
526
+ }
527
+ }
528
+ }
529
+ }
530
+
531
+ @media (max-width: 767px) {
532
+ body.template-topic {
533
+ .topic-header h1,
534
+ .topic-header [component="topic/title"] {
535
+ font-size: 22px;
536
+ }
537
+
538
+ .quick-reply,
539
+ [component="topic/quickreply"] {
540
+ padding: $jv-space-4;
541
+ }
542
+
543
+ [component="post/replies/container"] {
544
+ padding-left: $jv-space-4;
545
+ }
546
+ }
547
+ }
@@ -87,3 +87,13 @@ $jv-focus-ring-offset: 0 0 0 2px $jv-surface, 0 0 0 4px $jv-primary;
87
87
  $jv-hover-bg: rgba(0, 0, 0, 0.04);
88
88
  $jv-active-bg: rgba(0, 0, 0, 0.08);
89
89
  $jv-selected-bg: rgba(0, 81, 255, 0.08);
90
+
91
+ // Semantic Colors
92
+ $jv-success: #10b981;
93
+ $jv-success-hover: #059669;
94
+ $jv-danger: #ef4444;
95
+ $jv-danger-hover: #dc2626;
96
+ $jv-warning: #f59e0b;
97
+ $jv-warning-hover: #d97706;
98
+ $jv-info: #3b82f6;
99
+ $jv-info-hover: #2563eb;
package/theme.js CHANGED
@@ -5,8 +5,64 @@
5
5
 
6
6
  'use strict';
7
7
 
8
+ const meta = require.main.require('./src/meta');
9
+ const user = require.main.require('./src/user');
10
+ const _ = require.main.require('lodash');
11
+
8
12
  const theme = {};
9
13
 
14
+ // Harmony's defaults - we override openSidebars to 'on'
15
+ const defaults = {
16
+ enableQuickReply: 'on',
17
+ enableBreadcrumbs: 'on',
18
+ centerHeaderElements: 'off',
19
+ mobileTopicTeasers: 'off',
20
+ stickyToolbar: 'on',
21
+ topicSidebarTools: 'on',
22
+ topMobilebar: 'off',
23
+ autohideBottombar: 'on',
24
+ openSidebars: 'on', // JAVIS override: sidebar open by default
25
+ chatModals: 'off',
26
+ };
27
+
28
+ /**
29
+ * Load theme config - replaces Harmony's loadThemeConfig
30
+ * Uses JAVIS defaults (with openSidebars: 'on')
31
+ */
32
+ async function loadThemeConfig(uid) {
33
+ const [themeConfig, userConfig] = await Promise.all([
34
+ meta.settings.get('harmony'),
35
+ user.getSettings(uid),
36
+ ]);
37
+
38
+ // 3-tier cascade: JAVIS defaults -> theme settings -> user settings
39
+ const config = { ...defaults, ...themeConfig, ...(_.pick(userConfig, Object.keys(defaults))) };
40
+
41
+ // Convert 'on'/'off' strings to boolean
42
+ config.enableQuickReply = config.enableQuickReply === 'on';
43
+ config.enableBreadcrumbs = config.enableBreadcrumbs === 'on';
44
+ config.centerHeaderElements = config.centerHeaderElements === 'on';
45
+ config.mobileTopicTeasers = config.mobileTopicTeasers === 'on';
46
+ config.stickyToolbar = config.stickyToolbar === 'on';
47
+ config.topicSidebarTools = config.topicSidebarTools === 'on';
48
+ config.autohideBottombar = config.autohideBottombar === 'on';
49
+ config.topMobilebar = config.topMobilebar === 'on';
50
+ config.openSidebars = config.openSidebars === 'on';
51
+ config.chatModals = config.chatModals === 'on';
52
+
53
+ return config;
54
+ }
55
+
56
+ /**
57
+ * Hook: filter:config.get
58
+ * Sets config.theme with JAVIS-specific defaults
59
+ */
60
+ theme.getThemeConfig = async function (config) {
61
+ config.theme = await loadThemeConfig(config.uid);
62
+ config.openDraftsOnPageLoad = false;
63
+ return config;
64
+ };
65
+
10
66
  theme.defineWidgetAreas = async (areas) => {
11
67
  // Define widget areas like Harmony does
12
68
  const locations = ['header', 'sidebar', 'footer'];
package/theme.scss CHANGED
@@ -12,6 +12,7 @@
12
12
  // JAVIS Custom Styles
13
13
  @import "./scss/variables";
14
14
  @import "./scss/base";
15
+ @import "./scss/header";
15
16
  @import "./scss/buttons";
16
17
  @import "./scss/forms";
17
18
  @import "./scss/cards";
@@ -19,3 +20,4 @@
19
20
  @import "./scss/sidebar-user";
20
21
  @import "./scss/categories";
21
22
  @import "./scss/feed";
23
+ @import "./scss/topic";