@okjavis/nodebb-theme-javis 2.3.0 → 2.5.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 +1 -1
- package/plugin.json +6 -1
- package/scss/_base.scss +7 -0
- package/scss/_composer.scss +724 -0
- package/scss/_feed.scss +292 -23
- package/scss/_sidebar.scss +1 -1
- package/scss/_topic.scss +6 -4
- package/static/lib/theme.js +80 -0
- package/templates/admin/plugins/javis.tpl +76 -0
- package/theme.js +35 -4
- package/theme.scss +1 -0
package/scss/_feed.scss
CHANGED
|
@@ -4,22 +4,206 @@
|
|
|
4
4
|
// ===========================================================
|
|
5
5
|
|
|
6
6
|
// ===========================================================
|
|
7
|
-
// HIDE
|
|
7
|
+
// HIDE CATEGORIES WIDGET ON FEED PAGE
|
|
8
|
+
// The categories list widget is not relevant on the feed page
|
|
8
9
|
// ===========================================================
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
//
|
|
10
|
+
.feed {
|
|
11
|
+
// Hide categories list directly - simple and reliable
|
|
12
|
+
ul.categories-list {
|
|
13
|
+
display: none !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Also hide any widget title that precedes a categories list
|
|
17
|
+
// This targets the "All Categories" header
|
|
18
|
+
[data-widget-area="left"],
|
|
19
|
+
[data-widget-area="right"] {
|
|
20
|
+
// Hide the entire widget container that has categories
|
|
21
|
+
// Using adjacent sibling and general sibling selectors
|
|
22
|
+
.categories-list,
|
|
23
|
+
[component="categories/category"] {
|
|
24
|
+
display: none !important;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
16
27
|
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
// Hide the original "New Topic" button via CSS to prevent flicker
|
|
29
|
+
// The composer prompt card will replace it via JS
|
|
30
|
+
#new_topic {
|
|
31
|
+
display: none !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Hide the controls row (category dropdown, options)
|
|
35
|
+
// The composer prompt card replaces this functionality
|
|
36
|
+
> .row > div > .d-flex.justify-content-between.py-2.mb-2 {
|
|
37
|
+
display: none !important;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ===========================================================
|
|
42
|
+
// LINKEDIN-STYLE COMPOSER PROMPT CARD
|
|
43
|
+
// ===========================================================
|
|
44
|
+
.composer-prompt-card {
|
|
45
|
+
background: $jv-surface;
|
|
46
|
+
border: 1px solid $jv-border-subtle;
|
|
47
|
+
border-radius: $jv-radius-lg;
|
|
48
|
+
padding: $jv-space-4 $jv-space-6; // 16px top/bottom, 24px left/right
|
|
49
|
+
margin-top: $jv-space-4; // Align with trending tags padding-top
|
|
50
|
+
margin-bottom: $jv-space-4;
|
|
51
|
+
box-shadow: $jv-shadow-sm;
|
|
52
|
+
transition: box-shadow $jv-transition-fast, border-color $jv-transition-fast;
|
|
53
|
+
|
|
54
|
+
&:hover {
|
|
55
|
+
box-shadow: $jv-shadow-md;
|
|
56
|
+
border-color: $jv-border-strong;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Guest variant
|
|
60
|
+
&.composer-prompt-guest {
|
|
61
|
+
.composer-prompt-input {
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.composer-prompt-inner {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: $jv-space-3;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.composer-prompt-avatar {
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
|
|
76
|
+
.avatar {
|
|
77
|
+
width: 48px !important;
|
|
78
|
+
height: 48px !important;
|
|
79
|
+
border-radius: 50% !important;
|
|
80
|
+
object-fit: cover;
|
|
81
|
+
border: 2px solid rgba(0, 0, 0, 0.05);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.avatar-placeholder {
|
|
85
|
+
width: 48px;
|
|
86
|
+
height: 48px;
|
|
87
|
+
border-radius: 50%;
|
|
88
|
+
background: linear-gradient(135deg, $jv-primary-soft 0%, rgba(0, 81, 255, 0.2) 100%);
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: center;
|
|
92
|
+
color: $jv-primary;
|
|
93
|
+
font-size: 18px;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.composer-prompt-input {
|
|
98
|
+
flex: 1;
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
padding: $jv-space-3 $jv-space-4;
|
|
102
|
+
background: $jv-bg;
|
|
103
|
+
border: 1px solid $jv-border-subtle;
|
|
104
|
+
border-radius: $jv-radius-pill;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
transition: all $jv-transition-fast;
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
min-height: 48px;
|
|
109
|
+
|
|
110
|
+
&:hover {
|
|
111
|
+
background: rgba(0, 0, 0, 0.04);
|
|
112
|
+
border-color: $jv-border-strong;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&:focus {
|
|
116
|
+
outline: none;
|
|
117
|
+
border-color: $jv-primary;
|
|
118
|
+
box-shadow: $jv-focus-ring;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Reset button styles
|
|
122
|
+
&.composer-prompt-input {
|
|
123
|
+
text-align: left;
|
|
124
|
+
font-family: inherit;
|
|
125
|
+
font-size: inherit;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.composer-prompt-placeholder {
|
|
130
|
+
color: $jv-text-muted;
|
|
131
|
+
font-size: $jv-font-size-base;
|
|
132
|
+
font-weight: 500;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.composer-prompt-actions {
|
|
136
|
+
display: flex;
|
|
137
|
+
gap: $jv-space-1;
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.composer-prompt-action {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
width: 40px;
|
|
146
|
+
height: 40px;
|
|
147
|
+
border: none;
|
|
148
|
+
background: transparent;
|
|
149
|
+
border-radius: 50%;
|
|
150
|
+
color: $jv-text-muted;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
transition: all $jv-transition-fast;
|
|
153
|
+
|
|
154
|
+
&:hover {
|
|
155
|
+
background: $jv-hover-bg;
|
|
156
|
+
color: $jv-primary;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
&:focus {
|
|
160
|
+
outline: none;
|
|
161
|
+
box-shadow: $jv-focus-ring;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
i {
|
|
165
|
+
font-size: 18px;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Feed options row (settings dropdown only)
|
|
170
|
+
.feed-options-row {
|
|
171
|
+
margin-bottom: $jv-space-2 !important;
|
|
172
|
+
padding: $jv-space-2 0 !important;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Mobile responsive
|
|
176
|
+
@media (max-width: 576px) {
|
|
177
|
+
.composer-prompt-card {
|
|
178
|
+
padding: $jv-space-3;
|
|
179
|
+
border-radius: $jv-radius-md;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.composer-prompt-avatar {
|
|
183
|
+
.avatar,
|
|
184
|
+
.avatar-placeholder {
|
|
185
|
+
width: 40px !important;
|
|
186
|
+
height: 40px !important;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.avatar-placeholder {
|
|
190
|
+
font-size: 16px;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.composer-prompt-input {
|
|
195
|
+
padding: $jv-space-2 $jv-space-3;
|
|
196
|
+
min-height: 40px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.composer-prompt-placeholder {
|
|
200
|
+
font-size: $jv-font-size-sm;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.composer-prompt-actions {
|
|
204
|
+
display: none; // Hide action buttons on mobile for cleaner UI
|
|
205
|
+
}
|
|
206
|
+
}
|
|
23
207
|
|
|
24
208
|
// ===========================================================
|
|
25
209
|
// FEED CONTAINER
|
|
@@ -131,22 +315,18 @@
|
|
|
131
315
|
.overflow-hidden {
|
|
132
316
|
border-radius: $jv-radius-md $jv-radius-md 0 0 !important; // Match card radius
|
|
133
317
|
max-height: 280px !important; // Reduced from 350px - more balanced ratio
|
|
318
|
+
background: rgba(0, 0, 0, 0.02); // Subtle background for letterboxing
|
|
134
319
|
|
|
135
320
|
img {
|
|
136
321
|
width: 100%;
|
|
137
322
|
height: 280px; // Fixed height for consistency
|
|
138
|
-
object-fit:
|
|
323
|
+
object-fit: contain; // Prevent stretching, maintain aspect ratio
|
|
139
324
|
}
|
|
140
325
|
}
|
|
141
326
|
|
|
142
|
-
//
|
|
327
|
+
// Hide additional thumbnails - only show main image in feed
|
|
143
328
|
.position-absolute {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
img {
|
|
147
|
-
border: 2px solid $jv-surface;
|
|
148
|
-
box-shadow: $jv-shadow-soft;
|
|
149
|
-
}
|
|
329
|
+
display: none !important;
|
|
150
330
|
}
|
|
151
331
|
}
|
|
152
332
|
}
|
|
@@ -191,7 +371,7 @@
|
|
|
191
371
|
font-weight: 700 !important; // Bolder for better hierarchy
|
|
192
372
|
line-height: 1.4 !important; // Better readability
|
|
193
373
|
color: $jv-text-main !important;
|
|
194
|
-
margin-bottom:
|
|
374
|
+
margin-bottom: 0;
|
|
195
375
|
letter-spacing: -0.02em; // Tighter for modern feel
|
|
196
376
|
|
|
197
377
|
&:hover {
|
|
@@ -314,6 +494,95 @@
|
|
|
314
494
|
border-color: $jv-primary !important;
|
|
315
495
|
}
|
|
316
496
|
}
|
|
497
|
+
|
|
498
|
+
// ===========================================================
|
|
499
|
+
// FEED IMAGE CAROUSEL (matching topic page design)
|
|
500
|
+
// ===========================================================
|
|
501
|
+
.post-image-carousel {
|
|
502
|
+
border-radius: $jv-radius-md;
|
|
503
|
+
overflow: hidden;
|
|
504
|
+
margin: $jv-space-4 0;
|
|
505
|
+
background: rgba(0, 0, 0, 0.03);
|
|
506
|
+
|
|
507
|
+
.carousel-inner {
|
|
508
|
+
border-radius: $jv-radius-md;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.carousel-item {
|
|
512
|
+
// Fixed height container to prevent jumping
|
|
513
|
+
height: 400px;
|
|
514
|
+
|
|
515
|
+
img {
|
|
516
|
+
width: 100%;
|
|
517
|
+
height: 100%;
|
|
518
|
+
object-fit: contain;
|
|
519
|
+
background: rgba(0, 0, 0, 0.02);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Navigation arrows - matching topic page
|
|
524
|
+
.carousel-control-prev,
|
|
525
|
+
.carousel-control-next {
|
|
526
|
+
width: 48px;
|
|
527
|
+
height: 48px;
|
|
528
|
+
top: 50%;
|
|
529
|
+
transform: translateY(-50%);
|
|
530
|
+
background: rgba(0, 0, 0, 0.5);
|
|
531
|
+
border-radius: 50%;
|
|
532
|
+
opacity: 0;
|
|
533
|
+
transition: opacity $jv-transition-fast;
|
|
534
|
+
|
|
535
|
+
&:hover {
|
|
536
|
+
background: rgba(0, 0, 0, 0.7);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.carousel-control-prev-icon,
|
|
540
|
+
.carousel-control-next-icon {
|
|
541
|
+
width: 20px;
|
|
542
|
+
height: 20px;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.carousel-control-prev {
|
|
547
|
+
left: $jv-space-3;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.carousel-control-next {
|
|
551
|
+
right: $jv-space-3;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
&:hover {
|
|
555
|
+
.carousel-control-prev,
|
|
556
|
+
.carousel-control-next {
|
|
557
|
+
opacity: 1;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Indicator dots - matching topic page
|
|
562
|
+
.carousel-indicators {
|
|
563
|
+
margin-bottom: $jv-space-3;
|
|
564
|
+
gap: $jv-space-2;
|
|
565
|
+
|
|
566
|
+
button {
|
|
567
|
+
width: 8px;
|
|
568
|
+
height: 8px;
|
|
569
|
+
border-radius: 50%;
|
|
570
|
+
background: rgba(255, 255, 255, 0.5);
|
|
571
|
+
border: none;
|
|
572
|
+
opacity: 1;
|
|
573
|
+
transition: background-color $jv-transition-fast, transform $jv-transition-fast;
|
|
574
|
+
|
|
575
|
+
&.active {
|
|
576
|
+
background: #fff;
|
|
577
|
+
transform: scale(1.2);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
&:hover {
|
|
581
|
+
background: rgba(255, 255, 255, 0.8);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
317
586
|
}
|
|
318
587
|
|
|
319
588
|
// ===========================================================
|
package/scss/_sidebar.scss
CHANGED
|
@@ -504,7 +504,7 @@ div[data-widget-area="right"],
|
|
|
504
504
|
|
|
505
505
|
// Sticky positioning - stays visible when scrolling
|
|
506
506
|
position: sticky;
|
|
507
|
-
top:
|
|
507
|
+
top: 72px; // Adjust based on your header height
|
|
508
508
|
align-self: flex-start;
|
|
509
509
|
max-height: calc(100vh - 100px); // Account for header + padding
|
|
510
510
|
overflow-y: auto; // Allow scrolling if content is too tall
|
package/scss/_topic.scss
CHANGED
|
@@ -598,9 +598,7 @@ body.template-topic {
|
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
[component="topic/reply/container"] {
|
|
601
|
-
|
|
602
|
-
flex-direction: column;
|
|
603
|
-
gap: $jv-space-2;
|
|
601
|
+
width: 100%;
|
|
604
602
|
|
|
605
603
|
.btn-primary {
|
|
606
604
|
flex: 1;
|
|
@@ -609,7 +607,11 @@ body.template-topic {
|
|
|
609
607
|
padding: $jv-space-3 $jv-space-4;
|
|
610
608
|
}
|
|
611
609
|
|
|
612
|
-
|
|
610
|
+
// Hide the "Reply as topic" dropdown toggle and menu
|
|
611
|
+
.dropdown-toggle,
|
|
612
|
+
.dropdown-menu {
|
|
613
|
+
display: none !important;
|
|
614
|
+
}
|
|
613
615
|
}
|
|
614
616
|
|
|
615
617
|
// Sidebar action buttons - full-width, left-aligned (extends base .btn-ghost)
|
package/static/lib/theme.js
CHANGED
|
@@ -27,12 +27,16 @@
|
|
|
27
27
|
// Initialize post hover actions (show actions only on directly hovered post)
|
|
28
28
|
initPostHoverActions();
|
|
29
29
|
|
|
30
|
+
// Initialize click handler for composer prompt card (rendered server-side in feed.tpl)
|
|
31
|
+
initFeedComposerPromptHandler();
|
|
32
|
+
|
|
30
33
|
// Re-initialize carousels when new posts are loaded (infinite scroll, etc.)
|
|
31
34
|
// Also handle post edits by clearing the processed flag
|
|
32
35
|
$(window).on('action:posts.loaded action:topic.loaded action:ajaxify.end', function() {
|
|
33
36
|
initPostImageCarousels();
|
|
34
37
|
initParentPostNavigation();
|
|
35
38
|
initPostHoverActions();
|
|
39
|
+
initFeedComposerPromptHandler();
|
|
36
40
|
});
|
|
37
41
|
|
|
38
42
|
// Handle post edits - need to re-process the edited post
|
|
@@ -370,4 +374,80 @@
|
|
|
370
374
|
});
|
|
371
375
|
}
|
|
372
376
|
|
|
377
|
+
/**
|
|
378
|
+
* Initialize LinkedIn-style composer prompt card on feed page
|
|
379
|
+
* Injects the card HTML and sets up click handlers
|
|
380
|
+
* The original "New Topic" button is hidden via CSS to prevent flicker
|
|
381
|
+
*/
|
|
382
|
+
function initFeedComposerPromptHandler() {
|
|
383
|
+
// Only run on feed page
|
|
384
|
+
if (!$('.feed').length) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Don't re-inject if already present
|
|
389
|
+
if ($('.composer-prompt-card').length) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Find the existing controls row (contains New Topic button)
|
|
394
|
+
var $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-2').first();
|
|
395
|
+
if (!$controlsRow.length) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Get user info for avatar - clone from header avatar which is already correctly rendered
|
|
400
|
+
var isLoggedIn = false;
|
|
401
|
+
var avatarHtml = '';
|
|
402
|
+
|
|
403
|
+
if (typeof app !== 'undefined' && app.user && app.user.uid) {
|
|
404
|
+
isLoggedIn = true;
|
|
405
|
+
// Clone the avatar from the header - it's already correctly rendered by NodeBB
|
|
406
|
+
var $headerAvatar = $('[component="header/avatar"] img, [component="header/avatar"] [component="avatar/picture"]').first();
|
|
407
|
+
if ($headerAvatar.length) {
|
|
408
|
+
avatarHtml = '<img src="' + $headerAvatar.attr('src') + '" alt="Avatar" class="avatar avatar-rounded" />';
|
|
409
|
+
} else {
|
|
410
|
+
// Fallback to icon-based avatar from header
|
|
411
|
+
var $headerIcon = $('[component="header/avatar"] [component="avatar/icon"]').first();
|
|
412
|
+
if ($headerIcon.length) {
|
|
413
|
+
avatarHtml = $headerIcon.clone().addClass('avatar avatar-rounded').get(0).outerHTML;
|
|
414
|
+
} else {
|
|
415
|
+
// Last resort: use placeholder
|
|
416
|
+
avatarHtml = '<div class="avatar avatar-rounded avatar-placeholder"><i class="fa fa-user"></i></div>';
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
avatarHtml = '<div class="avatar avatar-rounded avatar-placeholder"><i class="fa fa-user"></i></div>';
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
var cardHtml = '<div class="composer-prompt-card' + (isLoggedIn ? '' : ' composer-prompt-guest') + '">' +
|
|
424
|
+
'<div class="composer-prompt-inner">' +
|
|
425
|
+
'<div class="composer-prompt-avatar">' + avatarHtml + '</div>' +
|
|
426
|
+
'<button class="composer-prompt-input" type="button" id="composer-prompt-btn">' +
|
|
427
|
+
'<span class="composer-prompt-placeholder">' + (isLoggedIn ? 'Start a discussion...' : 'Sign in to start a discussion...') + '</span>' +
|
|
428
|
+
'</button>' +
|
|
429
|
+
'</div>' +
|
|
430
|
+
'</div>';
|
|
431
|
+
|
|
432
|
+
// Insert the card before the controls row
|
|
433
|
+
$controlsRow.before(cardHtml);
|
|
434
|
+
|
|
435
|
+
// Bind click handler to open composer
|
|
436
|
+
$('.composer-prompt-card').on('click', '.composer-prompt-input, .composer-prompt-action', function(e) {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
|
|
439
|
+
if (!isLoggedIn) {
|
|
440
|
+
// Redirect to login
|
|
441
|
+
window.location.href = config.relative_path + '/login';
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Trigger the original New Topic button click
|
|
446
|
+
var $newTopicBtn = $('#new_topic');
|
|
447
|
+
if ($newTopicBtn.length) {
|
|
448
|
+
$newTopicBtn.trigger('click');
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
373
453
|
})();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<!-- IMPORT admin/partials/settings/header.tpl -->
|
|
3
|
+
|
|
4
|
+
<div class="row m-0">
|
|
5
|
+
<div id="spy-container" class="col-12 col-md-8 px-0 mb-4" tabindex="0">
|
|
6
|
+
<form role="form" class="javis-settings">
|
|
7
|
+
<div class="mb-4">
|
|
8
|
+
<h5 class="fw-bold">JAVIS Theme Settings</h5>
|
|
9
|
+
<p class="text-muted">Configure the default settings for the JAVIS Community theme.</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="form-check form-switch mb-3">
|
|
13
|
+
<input type="checkbox" class="form-check-input" id="enableQuickReply" name="enableQuickReply" />
|
|
14
|
+
<label for="enableQuickReply" class="form-check-label">Enable Quick Reply</label>
|
|
15
|
+
<p class="form-text">Allow users to quickly reply to topics without opening the full composer.</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="form-check form-switch mb-3">
|
|
19
|
+
<input type="checkbox" class="form-check-input" id="enableBreadcrumbs" name="enableBreadcrumbs" />
|
|
20
|
+
<label for="enableBreadcrumbs" class="form-check-label">Enable Breadcrumbs</label>
|
|
21
|
+
<p class="form-text">Show navigation breadcrumbs at the top of pages.</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="form-check form-switch mb-3">
|
|
25
|
+
<input type="checkbox" class="form-check-input" id="centerHeaderElements" name="centerHeaderElements" />
|
|
26
|
+
<label for="centerHeaderElements" class="form-check-label">Center Header Elements</label>
|
|
27
|
+
<p class="form-text">Center-align the header navigation elements.</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="form-check form-switch mb-3">
|
|
31
|
+
<input type="checkbox" class="form-check-input" id="mobileTopicTeasers" name="mobileTopicTeasers" />
|
|
32
|
+
<label for="mobileTopicTeasers" class="form-check-label">Mobile Topic Teasers</label>
|
|
33
|
+
<p class="form-text">Show topic teasers on mobile devices.</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="form-check form-switch mb-3">
|
|
37
|
+
<input type="checkbox" class="form-check-input" id="stickyToolbar" name="stickyToolbar" />
|
|
38
|
+
<label for="stickyToolbar" class="form-check-label">Sticky Toolbar</label>
|
|
39
|
+
<p class="form-text">Keep the topic toolbar visible when scrolling.</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="form-check form-switch mb-3">
|
|
43
|
+
<input type="checkbox" class="form-check-input" id="topicSidebarTools" name="topicSidebarTools" />
|
|
44
|
+
<label for="topicSidebarTools" class="form-check-label">Topic Sidebar Tools</label>
|
|
45
|
+
<p class="form-text">Show topic tools in the sidebar instead of inline.</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="form-check form-switch mb-3">
|
|
49
|
+
<input type="checkbox" class="form-check-input" id="autohideBottombar" name="autohideBottombar" />
|
|
50
|
+
<label for="autohideBottombar" class="form-check-label">Auto-hide Bottom Bar</label>
|
|
51
|
+
<p class="form-text">Automatically hide the bottom navigation bar when scrolling down.</p>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="form-check form-switch mb-3">
|
|
55
|
+
<input type="checkbox" class="form-check-input" id="topMobilebar" name="topMobilebar" />
|
|
56
|
+
<label for="topMobilebar" class="form-check-label">Top Mobile Bar</label>
|
|
57
|
+
<p class="form-text">Show mobile navigation at the top instead of bottom.</p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="form-check form-switch mb-3">
|
|
61
|
+
<input type="checkbox" class="form-check-input" id="openSidebars" name="openSidebars" />
|
|
62
|
+
<label for="openSidebars" class="form-check-label">Open Sidebars by Default</label>
|
|
63
|
+
<p class="form-text">Keep sidebars expanded by default (JAVIS default: enabled).</p>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="form-check form-switch mb-3">
|
|
67
|
+
<input type="checkbox" class="form-check-input" id="chatModals" name="chatModals" />
|
|
68
|
+
<label for="chatModals" class="form-check-label">Chat Modals</label>
|
|
69
|
+
<p class="form-text">Open chats in modal windows instead of navigating to the chat page.</p>
|
|
70
|
+
</div>
|
|
71
|
+
</form>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- IMPORT admin/partials/settings/toc.tpl -->
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
package/theme.js
CHANGED
|
@@ -9,9 +9,11 @@ const meta = require.main.require('./src/meta');
|
|
|
9
9
|
const user = require.main.require('./src/user');
|
|
10
10
|
const _ = require.main.require('lodash');
|
|
11
11
|
|
|
12
|
+
const controllers = require('./lib/controllers');
|
|
13
|
+
|
|
12
14
|
const theme = {};
|
|
13
15
|
|
|
14
|
-
//
|
|
16
|
+
// JAVIS defaults - openSidebars is 'on' by default
|
|
15
17
|
const defaults = {
|
|
16
18
|
enableQuickReply: 'on',
|
|
17
19
|
enableBreadcrumbs: 'on',
|
|
@@ -26,12 +28,24 @@ const defaults = {
|
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
31
|
+
* Hook: static:app.load
|
|
32
|
+
* Initialize routes for admin panel
|
|
33
|
+
*/
|
|
34
|
+
theme.init = async function (params) {
|
|
35
|
+
const { router } = params;
|
|
36
|
+
const routeHelpers = require.main.require('./src/routes/helpers');
|
|
37
|
+
|
|
38
|
+
// Admin panel route
|
|
39
|
+
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/javis', [], controllers.renderAdminPage);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load theme config
|
|
30
44
|
* Uses JAVIS defaults (with openSidebars: 'on')
|
|
31
45
|
*/
|
|
32
46
|
async function loadThemeConfig(uid) {
|
|
33
47
|
const [themeConfig, userConfig] = await Promise.all([
|
|
34
|
-
meta.settings.get('
|
|
48
|
+
meta.settings.get('javis'),
|
|
35
49
|
user.getSettings(uid),
|
|
36
50
|
]);
|
|
37
51
|
|
|
@@ -53,6 +67,9 @@ async function loadThemeConfig(uid) {
|
|
|
53
67
|
return config;
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
// Export loadThemeConfig for potential use by other modules
|
|
71
|
+
theme.loadThemeConfig = loadThemeConfig;
|
|
72
|
+
|
|
56
73
|
/**
|
|
57
74
|
* Hook: filter:config.get
|
|
58
75
|
* Sets config.theme with JAVIS-specific defaults
|
|
@@ -63,6 +80,20 @@ theme.getThemeConfig = async function (config) {
|
|
|
63
80
|
return config;
|
|
64
81
|
};
|
|
65
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Hook: filter:settings.get
|
|
85
|
+
* Provide default values for admin settings
|
|
86
|
+
*/
|
|
87
|
+
theme.getAdminSettings = async function (hookData) {
|
|
88
|
+
if (hookData.plugin === 'javis') {
|
|
89
|
+
hookData.values = {
|
|
90
|
+
...defaults,
|
|
91
|
+
...hookData.values,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return hookData;
|
|
95
|
+
};
|
|
96
|
+
|
|
66
97
|
theme.defineWidgetAreas = async (areas) => {
|
|
67
98
|
// Define widget areas like Harmony does
|
|
68
99
|
const locations = ['header', 'sidebar', 'footer'];
|
|
@@ -125,7 +156,7 @@ theme.defineWidgetAreas = async (areas) => {
|
|
|
125
156
|
|
|
126
157
|
theme.addAdminNavigation = (header) => {
|
|
127
158
|
header.plugins.push({
|
|
128
|
-
route: '/plugins/javis
|
|
159
|
+
route: '/plugins/javis',
|
|
129
160
|
icon: 'fa-paint-brush',
|
|
130
161
|
name: 'JAVIS Theme',
|
|
131
162
|
});
|
package/theme.scss
CHANGED