bible270 1.8.5 → 1.9.1
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.
- checksums.yaml +4 -4
- data/README.md +18 -0
- data/app/controllers/bible270/profiles_controller.rb +16 -3
- data/app/models/bible270/reader.rb +14 -0
- data/app/views/bible270/profiles/edit.html.erb +14 -0
- data/app/views/bible270/shared/_header.html.erb +5 -3
- data/app/views/bible270/shared/_styles.html.erb +81 -38
- data/app/views/layouts/bible270/application.html.erb +1 -1
- data/db/migrate/20260101000023_add_display_mode_to_bible270_readers.rb +7 -0
- data/lib/bible270/migration_reconciler.rb +11 -0
- data/lib/bible270/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e8a5fd512d3d6da6e59a4ecbbb112717b13eeb00b4b6fa0c4033c7c61ace730
|
|
4
|
+
data.tar.gz: 190694fc67180501f5e991aaf7640c7577b90f636fdea9dfca742941f9577841
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78a6b2c66b69dc0f6fb268ff1669b968567a4fdedbcf749ba34d31daec6853119a30da545c129c0f77d0571ef488a43a24b0c6f10dc7b5b7738e2c6d2e60e366
|
|
7
|
+
data.tar.gz: a132a769bef838b993cac33603fd2d5ef6b66796a3f9d86f40d257436040732d73ebe154d92054ca5d724b88899abf6970d41a122438286afe60dedf52a128b4
|
data/README.md
CHANGED
|
@@ -368,6 +368,24 @@ Psalm 35: []
|
|
|
368
368
|
Psalm 78: [20, 39, 55]
|
|
369
369
|
```
|
|
370
370
|
|
|
371
|
+
## Display preference
|
|
372
|
+
|
|
373
|
+
Readers can choose **Light**, **Dark**, or **System** under **Profile → Display**, then save.
|
|
374
|
+
The preference is stored with their account and applies throughout Bible270 on every device.
|
|
375
|
+
System is the default for existing and new readers, follows the device's current appearance,
|
|
376
|
+
and responds to changes without reloading. Signed-out visitors also use System.
|
|
377
|
+
The completion dove retains its bright colors in either theme.
|
|
378
|
+
|
|
379
|
+
Upgrading hosts must install and run migration `20260101000023`:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
bin/rails bible270:install:migrations
|
|
383
|
+
bin/rails db:migrate
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Custom engine layouts must render the shared styles and set `data-display-mode` on the
|
|
387
|
+
`.b270` element to `current_reader&.effective_display_mode || 'system'` to honor the preference.
|
|
388
|
+
|
|
371
389
|
## Start dates
|
|
372
390
|
|
|
373
391
|
The plan runs **undated** (day numbers only) or **dated** (days mapped onto a calendar):
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Bible270
|
|
4
|
-
# A reader editing their own
|
|
5
|
-
# change. Everything else about a reader comes from how they signed in.
|
|
4
|
+
# A reader editing their own details and preferences.
|
|
6
5
|
class ProfilesController < ApplicationController
|
|
7
6
|
before_action :require_signed_in_reader
|
|
8
7
|
|
|
9
8
|
def edit
|
|
10
9
|
@names = current_reader.suggested_names
|
|
10
|
+
@display_mode = current_reader.effective_display_mode
|
|
11
11
|
load_comment_notification_state
|
|
12
12
|
load_daily_reminder_state
|
|
13
13
|
end
|
|
@@ -16,7 +16,8 @@ module Bible270
|
|
|
16
16
|
@comment_notifications_available = comment_notifications_available?
|
|
17
17
|
@comment_notification_level = requested_comment_notification_level
|
|
18
18
|
@daily_reminders_available = daily_reminders_available?
|
|
19
|
-
|
|
19
|
+
@display_mode = params.fetch(:display_mode, current_reader.effective_display_mode).to_s
|
|
20
|
+
problems = [comment_notification_problem, display_mode_problem].compact
|
|
20
21
|
problems << 'both a first and last name' unless current_reader.update_names(params[:first_name],
|
|
21
22
|
params[:last_name])
|
|
22
23
|
if params[:bible_version].present? && !current_reader.update_bible_version(params[:bible_version])
|
|
@@ -35,6 +36,7 @@ module Bible270
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
if problems.empty?
|
|
39
|
+
save_display_mode
|
|
38
40
|
current_reader.update_comment_notification_level!(@comment_notification_level) if @comment_notifications_available
|
|
39
41
|
if @daily_reminders_available
|
|
40
42
|
current_reader.update!(daily_reminders: requested_daily_reminders,
|
|
@@ -57,6 +59,17 @@ module Bible270
|
|
|
57
59
|
|
|
58
60
|
private
|
|
59
61
|
|
|
62
|
+
def display_mode_problem
|
|
63
|
+
return unless Reader.display_mode_column?
|
|
64
|
+
return if Reader::DISPLAY_MODES.include?(@display_mode)
|
|
65
|
+
|
|
66
|
+
'a display preference from the list'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def save_display_mode
|
|
70
|
+
current_reader.update!(display_mode: @display_mode) if Reader.display_mode_column?
|
|
71
|
+
end
|
|
72
|
+
|
|
60
73
|
def load_comment_notification_state
|
|
61
74
|
@comment_notifications_available = comment_notifications_available?
|
|
62
75
|
@comment_notification_level = current_reader.comment_notification_level
|
|
@@ -27,6 +27,8 @@ module Bible270
|
|
|
27
27
|
|
|
28
28
|
PASSAGE_SOURCES = %w[bible_com bible_gateway blue_letter_bible].freeze
|
|
29
29
|
DEFAULT_PASSAGE_SOURCE = 'bible_com'
|
|
30
|
+
DISPLAY_MODES = %w[light dark system].freeze
|
|
31
|
+
DEFAULT_DISPLAY_MODE = 'system'
|
|
30
32
|
DAILY_REMINDER_TIME_FORMAT = %r{\A(?:[01]\d|2[0-3]):[0-5]\d\z}
|
|
31
33
|
DAILY_REMINDER_MINUTES = %w[00 15 30 45].freeze
|
|
32
34
|
DAILY_REMINDER_COLUMNS = %w[daily_reminders daily_reminder_time last_daily_reminder_sent_on].freeze
|
|
@@ -39,6 +41,7 @@ module Bible270
|
|
|
39
41
|
validates :display_name, presence: true
|
|
40
42
|
validates :uid, uniqueness: { scope: :provider }, allow_nil: true
|
|
41
43
|
validates :passage_source, inclusion: { in: PASSAGE_SOURCES }
|
|
44
|
+
validates :display_mode, inclusion: { in: DISPLAY_MODES }, if: -> { has_attribute?(:display_mode) }
|
|
42
45
|
validates :daily_reminder_time,
|
|
43
46
|
format: { with: DAILY_REMINDER_TIME_FORMAT, message: 'must use 24-hour HH:MM format' },
|
|
44
47
|
if: -> { self.class.daily_reminder_columns? }
|
|
@@ -55,6 +58,17 @@ module Bible270
|
|
|
55
58
|
false
|
|
56
59
|
end
|
|
57
60
|
|
|
61
|
+
def self.display_mode_column?
|
|
62
|
+
column_names.include?('display_mode')
|
|
63
|
+
rescue ActiveRecord::StatementInvalid
|
|
64
|
+
false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def effective_display_mode
|
|
68
|
+
value = self[:display_mode] if has_attribute?(:display_mode)
|
|
69
|
+
DISPLAY_MODES.include?(value) ? value : DEFAULT_DISPLAY_MODE
|
|
70
|
+
end
|
|
71
|
+
|
|
58
72
|
def self.valid_daily_reminder_time?(value)
|
|
59
73
|
value.to_s.match?(DAILY_REMINDER_TIME_FORMAT)
|
|
60
74
|
end
|
|
@@ -38,6 +38,20 @@
|
|
|
38
38
|
</label>
|
|
39
39
|
<%= render "bible270/shared/passage_source", reader: current_reader %>
|
|
40
40
|
|
|
41
|
+
<% if Bible270::Reader.display_mode_column? %>
|
|
42
|
+
<label class="b270-field">
|
|
43
|
+
<span>Display</span>
|
|
44
|
+
<%= select_tag :display_mode,
|
|
45
|
+
options_for_select([["Light", "light"], ["Dark", "dark"], ["System", "system"]], @display_mode),
|
|
46
|
+
class: "b270-select", aria: { describedby: "b270-display-hint" } %>
|
|
47
|
+
<span id="b270-display-hint" class="b270-hint" style="margin:0">System follows your device's light or dark setting.</span>
|
|
48
|
+
</label>
|
|
49
|
+
<% else %>
|
|
50
|
+
<p class="b270-flash alert" style="margin:0">
|
|
51
|
+
Display settings are temporarily unavailable while this installation finishes a database update.
|
|
52
|
+
</p>
|
|
53
|
+
<% end %>
|
|
54
|
+
|
|
41
55
|
<% if @comment_notifications_available %>
|
|
42
56
|
<%= render "bible270/shared/comment_notification_choices",
|
|
43
57
|
level: @comment_notification_level,
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
<div class="b270-navmenu">
|
|
13
13
|
<button type="button" class="b270-navtoggle" aria-label="Menu"
|
|
14
14
|
aria-expanded="false" aria-controls="b270-mobile-navigation">
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
<svg class="b270-menuicon" viewBox="0 0 18 14" width="18" height="14"
|
|
16
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
17
|
+
aria-hidden="true" focusable="false">
|
|
18
|
+
<path d="M1 1h16M1 7h16M1 13h16" />
|
|
19
|
+
</svg>
|
|
18
20
|
</button>
|
|
19
21
|
<div id="b270-mobile-navigation" class="b270-navlinks" hidden>
|
|
20
22
|
<%= render "bible270/shared/nav_links" %>
|
|
@@ -3,9 +3,52 @@
|
|
|
3
3
|
--paper:#ece6da; --card:#f4efe6; --ink:#231f18; --muted:#5d564c; --faint:#6e665b;
|
|
4
4
|
--line:#d7cfbf; --line-soft:#e2dbcd; --teal:#2f6a67; --oxblood:#8a2f3b; --gold:#80631d;
|
|
5
5
|
--gold-lite:#80631d; --shadow:0 1px 0 rgba(35,31,24,.04),0 10px 30px -18px rgba(35,31,24,.35);
|
|
6
|
+
--control:#fff; --control-line:#d7cfbf; --task-surface:rgba(255,255,255,.35); --button-hover:#3a352b;
|
|
7
|
+
--on-accent:#fff; --complete-ink:#eef3f2; --complete-dot:#f4efe6;
|
|
8
|
+
--button-surface:#231f18; --button-ink:#ece6da;
|
|
9
|
+
--teal-fill:#2f6a67; --oxblood-fill:#8a2f3b; --gold-fill:#80631d;
|
|
10
|
+
--teal-mark:#2f6a67; --oxblood-mark:#8a2f3b; --gold-mark:#80631d;
|
|
11
|
+
--alert-surface:#f6e4e2; --alert-ink:#7a2b28; --alert-line:#e6c3bf;
|
|
12
|
+
--notice-surface:#e7efe6; --notice-ink:#2f5d34; --notice-line:#c6dcc6;
|
|
13
|
+
color-scheme:light;
|
|
6
14
|
margin:0; background:radial-gradient(120% 80% at 100% 0%,rgba(165,129,44,.06),transparent 55%),var(--paper);
|
|
7
15
|
color:var(--ink); font-family:"Inter",system-ui,sans-serif; line-height:1.2; -webkit-font-smoothing:antialiased;
|
|
8
16
|
}
|
|
17
|
+
/* Explicit light keeps native controls light even when the OS is dark.
|
|
18
|
+
Only system mode follows the media query, including live OS changes. */
|
|
19
|
+
.b270[data-display-mode="light"]{color-scheme:light}
|
|
20
|
+
.b270[data-display-mode="dark"]{
|
|
21
|
+
color-scheme:dark;
|
|
22
|
+
background-image:none;
|
|
23
|
+
--control-line:#827c6e;
|
|
24
|
+
--paper:#39311f; --card:#322714; --ink:#ccc9bd; --muted:#b7b09f; --faint:#a9a28f;
|
|
25
|
+
--line:#42484a; --line-soft:#4b422d; --teal:#80b5ad; --oxblood:#d19199; --gold:#d6be79;
|
|
26
|
+
--gold-lite:#695116; --shadow:0 1px 0 rgba(0,0,0,.1);
|
|
27
|
+
--control:#181b1b; --task-surface:rgba(255,255,255,.025); --button-hover:#292316;
|
|
28
|
+
--on-accent:#ded8c5; --complete-ink:#ded8c5; --complete-dot:#ded8c5;
|
|
29
|
+
--button-surface:#201c12; --button-ink:#ded8c5;
|
|
30
|
+
--teal-fill:#2f5955; --oxblood-fill:#702731; --gold-fill:#695116;
|
|
31
|
+
--teal-mark:#409594; --oxblood-mark:#a73d50; --gold-mark:#b18b2b;
|
|
32
|
+
--alert-surface:#42282a; --alert-ink:#ffc0b8; --alert-line:#a66d68;
|
|
33
|
+
--notice-surface:#25382a; --notice-ink:#b6dfb6; --notice-line:#6d956e;
|
|
34
|
+
}
|
|
35
|
+
@media (prefers-color-scheme:dark){
|
|
36
|
+
.b270[data-display-mode="system"]{
|
|
37
|
+
color-scheme:dark;
|
|
38
|
+
background-image:none;
|
|
39
|
+
--control-line:#827c6e;
|
|
40
|
+
--paper:#39311f; --card:#322714; --ink:#ccc9bd; --muted:#b7b09f; --faint:#a9a28f;
|
|
41
|
+
--line:#42484a; --line-soft:#4b422d; --teal:#80b5ad; --oxblood:#d19199; --gold:#d6be79;
|
|
42
|
+
--gold-lite:#695116; --shadow:0 1px 0 rgba(0,0,0,.1);
|
|
43
|
+
--control:#181b1b; --task-surface:rgba(255,255,255,.025); --button-hover:#292316;
|
|
44
|
+
--on-accent:#ded8c5; --complete-ink:#ded8c5; --complete-dot:#ded8c5;
|
|
45
|
+
--button-surface:#201c12; --button-ink:#ded8c5;
|
|
46
|
+
--teal-fill:#2f5955; --oxblood-fill:#702731; --gold-fill:#695116;
|
|
47
|
+
--teal-mark:#409594; --oxblood-mark:#a73d50; --gold-mark:#b18b2b;
|
|
48
|
+
--alert-surface:#42282a; --alert-ink:#ffc0b8; --alert-line:#a66d68;
|
|
49
|
+
--notice-surface:#25382a; --notice-ink:#b6dfb6; --notice-line:#6d956e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
9
52
|
.b270 *{box-sizing:border-box}
|
|
10
53
|
/* Blanket anchor colour. NB: this is (0,1,1), so any single-class rule
|
|
11
54
|
colouring an <a> (.b270-btn, .b270-cell, .b270-arrow ...) must be
|
|
@@ -38,7 +81,7 @@
|
|
|
38
81
|
.b270-avatarrow input[type=file]{font:inherit;font-size:13px;max-width:100%}
|
|
39
82
|
.b270-emailform{display:grid;gap:12px;max-width:340px;margin:12px 0 0}
|
|
40
83
|
.b270-field{display:grid;gap:5px;font-size:12.5px;color:var(--muted)}
|
|
41
|
-
.b270-field input{font:inherit;font-size:15px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background
|
|
84
|
+
.b270-field input{font:inherit;font-size:15px;padding:10px 12px;border:1px solid var(--control-line);border-radius:9px;background:var(--control);color:var(--ink);width:100%}
|
|
42
85
|
.b270-field input:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(165,129,44,.13)}
|
|
43
86
|
.b270-notification-field{margin:0;padding:0;border:0}
|
|
44
87
|
.b270-notification-field legend{padding:0;margin:0 0 3px}
|
|
@@ -63,8 +106,8 @@
|
|
|
63
106
|
.b270-time-control:active .b270-reminder-tooltip{visibility:visible;opacity:1}
|
|
64
107
|
.b270-field .b270-reminder-optin input[type=checkbox]{width:auto;margin:0;flex:none}
|
|
65
108
|
.b270-time-control{grid-template-columns:48px 5px 48px;align-items:center;gap:2px}
|
|
66
|
-
.b270-field .b270-time-select{width:48px;height:30px;margin:0;padding:4px 2px;border:1px solid var(--line);
|
|
67
|
-
border-radius:7px;background
|
|
109
|
+
.b270-field .b270-time-select{width:48px;height:30px;margin:0;padding:4px 2px;border:1px solid var(--control-line);
|
|
110
|
+
border-radius:7px;background:var(--control);color:var(--ink);font:12px/16px system-ui,sans-serif;
|
|
68
111
|
font-variant-numeric:tabular-nums;text-align:center}
|
|
69
112
|
.b270-time-select:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(165,129,44,.13)}
|
|
70
113
|
.b270-time-separator{display:grid;place-items:center;height:30px;font:12px/30px system-ui,sans-serif;color:var(--muted)}
|
|
@@ -72,19 +115,19 @@
|
|
|
72
115
|
.b270-or{display:flex;align-items:center;gap:12px;margin:22px 0 18px;color:var(--faint);font-size:12px;letter-spacing:.1em;text-transform:uppercase}
|
|
73
116
|
.b270-or::before,.b270-or::after{content:"";flex:1;height:1px;background:var(--line-soft)}
|
|
74
117
|
.b270-providers{display:grid;gap:10px;max-width:340px}
|
|
75
|
-
.b270 .b270-provider{display:block;width:100%;text-align:center;border:1px solid var(--line);background
|
|
118
|
+
.b270 .b270-provider{display:block;width:100%;text-align:center;border:1px solid var(--line);background:var(--control);color:var(--ink);border-radius:10px;padding:12px 16px;font:inherit;font-weight:600;font-size:14.5px;cursor:pointer;transition:border-color .15s,transform .05s}
|
|
76
119
|
.b270-provider:hover{border-color:var(--gold);color:var(--gold)}
|
|
77
120
|
.b270-provider:active{transform:translateY(1px)}
|
|
78
121
|
.b270 .b270-signinbtn{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:8px;padding:7px 13px;font:inherit;font-weight:600;font-size:13px;cursor:pointer}
|
|
79
122
|
.b270-signinbtn:hover{border-color:var(--gold);color:var(--gold)}
|
|
80
123
|
.b270-signin-hint a{color:var(--gold);font-weight:600}
|
|
81
124
|
.b270-flash{border-radius:10px;padding:11px 15px;margin-bottom:18px;font-size:14px}
|
|
82
|
-
.b270-flash.alert{background
|
|
83
|
-
.b270-flash.notice{background
|
|
125
|
+
.b270-flash.alert{background:var(--alert-surface);color:var(--alert-ink);border:1px solid var(--alert-line)}
|
|
126
|
+
.b270-flash.notice{background:var(--notice-surface);color:var(--notice-ink);border:1px solid var(--notice-line)}
|
|
84
127
|
.b270-interaction-status{margin:8px 0 12px;padding:8px 11px;border-radius:8px;background:rgba(47,106,103,.08);color:var(--teal);font-size:12.5px}
|
|
85
128
|
.b270-interaction-status:empty{display:none}
|
|
86
129
|
.b270-interaction-status[data-state="pending"]{color:var(--muted)}
|
|
87
|
-
.b270-interaction-status[data-state="error"]{background
|
|
130
|
+
.b270-interaction-status[data-state="error"]{background:var(--alert-surface);color:var(--alert-ink)}
|
|
88
131
|
.b270 form[aria-busy="true"] [data-b270-disabled-by-submit]{cursor:wait;opacity:.55}
|
|
89
132
|
|
|
90
133
|
.b270-eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold);font-weight:600;margin:0 0 10px}
|
|
@@ -103,7 +146,7 @@
|
|
|
103
146
|
|
|
104
147
|
|
|
105
148
|
.b270-task-list{list-style:none;margin:0;padding:0;display:grid;gap:8px}
|
|
106
|
-
.b270-task{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:10px 12px;border:1px solid var(--line-soft);border-radius:9px;background:
|
|
149
|
+
.b270-task{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:10px 12px;border:1px solid var(--line-soft);border-radius:9px;background:var(--task-surface)}
|
|
107
150
|
.b270-task-body{display:grid;gap:3px;min-width:0}
|
|
108
151
|
.b270-task-body strong{font-size:14px}
|
|
109
152
|
.b270-task-body span{color:var(--muted);font-size:12px;line-height:1.4}
|
|
@@ -118,7 +161,7 @@
|
|
|
118
161
|
.b270-startline{margin:0;font-size:13.5px;color:var(--muted)}
|
|
119
162
|
.b270-startline strong{color:var(--ink)}
|
|
120
163
|
.b270-startform{display:flex;align-items:flex-end;gap:10px;margin-top:10px;flex-wrap:wrap}
|
|
121
|
-
.b270-startform input[type=date]{font:inherit;font-size:14px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background
|
|
164
|
+
.b270-startform input[type=date]{font:inherit;font-size:14px;padding:8px 10px;border:1px solid var(--control-line);border-radius:8px;background:var(--control);color:var(--ink)}
|
|
122
165
|
.b270-linkbtn{background:none;border:none;padding:0;margin-top:8px;font:inherit;font-size:12.5px;color:var(--oxblood);cursor:pointer;text-decoration:underline}
|
|
123
166
|
.b270-hint{margin:8px 0 0;font-size:12px;color:var(--faint);max-width:111ch}
|
|
124
167
|
.b270-tracks{margin-top:16px;display:grid;gap:11px}
|
|
@@ -139,7 +182,7 @@
|
|
|
139
182
|
.b270-dayhead .k{display:flex;align-items:center;flex-wrap:wrap;gap:9px;font-family:"Spectral",serif;font-size:24px;font-weight:600;line-height:1}
|
|
140
183
|
.b270-dayhead .date{display:flex;align-items:center;flex-wrap:wrap;gap:9px;color:var(--faint);font-size:13px;margin-top:4px}
|
|
141
184
|
.b270-dayhead .b270-badge{margin-left:0}
|
|
142
|
-
.b270-badge{display:inline-block;vertical-align:middle;line-height:1;font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color
|
|
185
|
+
.b270-badge{display:inline-block;vertical-align:middle;line-height:1;font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--on-accent);background:var(--gold-lite);padding:5px 8px 4px;border-radius:20px;margin-left:9px}
|
|
143
186
|
|
|
144
187
|
.b270-day-complete{display:flex;align-items:center;gap:14px;margin:0 0 14px;padding:11px 13px;
|
|
145
188
|
border:1px solid var(--line);border-radius:10px;background:var(--card);box-shadow:var(--shadow)}
|
|
@@ -168,15 +211,15 @@
|
|
|
168
211
|
.b270-readings{background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);overflow:hidden}
|
|
169
212
|
.b270-reading{display:flex;gap:15px;align-items:center;padding:16px 20px;border-left:4px solid transparent}
|
|
170
213
|
.b270-reading+.b270-reading{border-top:1px solid var(--line-soft)}
|
|
171
|
-
.b270-reading.ot{border-left-color:var(--teal)} .b270-reading.nt{border-left-color:var(--oxblood)} .b270-reading.pp{border-left-color:var(--gold)}
|
|
214
|
+
.b270-reading.ot{border-left-color:var(--teal-mark)} .b270-reading.nt{border-left-color:var(--oxblood-mark)} .b270-reading.pp{border-left-color:var(--gold-mark)}
|
|
172
215
|
.b270-reading.rest{opacity:.65}
|
|
173
216
|
.b270-toggle{border:none;background:none;padding:0;cursor:pointer}
|
|
174
217
|
.b270-toggle form{margin:0}
|
|
175
|
-
.b270-check{width:26px;height:26px;border-radius:8px;border:2px solid var(--line);flex:none;display:grid;place-items:center;background
|
|
176
|
-
.b270-check svg{width:15px;height:15px;stroke
|
|
177
|
-
.b270-reading.done.ot .b270-check{background:var(--teal);border-color:var(--teal)}
|
|
178
|
-
.b270-reading.done.nt .b270-check{background:var(--oxblood);border-color:var(--oxblood)}
|
|
179
|
-
.b270-reading.done.pp .b270-check{background:var(--gold);border-color:var(--gold)}
|
|
218
|
+
.b270-check{width:26px;height:26px;border-radius:8px;border:2px solid var(--control-line);flex:none;display:grid;place-items:center;background:var(--control);transition:background-color .18s,border-color .18s,opacity .18s}
|
|
219
|
+
.b270-check svg{width:15px;height:15px;stroke:var(--on-accent);stroke-width:3;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:0}
|
|
220
|
+
.b270-reading.done.ot .b270-check{background:var(--teal-fill);border-color:var(--teal-mark)}
|
|
221
|
+
.b270-reading.done.nt .b270-check{background:var(--oxblood-fill);border-color:var(--oxblood-mark)}
|
|
222
|
+
.b270-reading.done.pp .b270-check{background:var(--gold-fill);border-color:var(--gold-mark)}
|
|
180
223
|
.b270-reading.done .b270-check svg{opacity:1}
|
|
181
224
|
.b270-rbody{flex:1;min-width:0}
|
|
182
225
|
.b270-cat{font-size:11px;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);font-weight:600}
|
|
@@ -195,16 +238,16 @@
|
|
|
195
238
|
/* A ticked chapter looks exactly like a ticked single reading: the box fills
|
|
196
239
|
in the track's colour and the tick appears. No strikethrough — the other
|
|
197
240
|
tracks don't strike their references either. */
|
|
198
|
-
.b270-reading.ot .b270-part.done .b270-check{background:var(--teal);border-color:var(--teal)}
|
|
199
|
-
.b270-reading.nt .b270-part.done .b270-check{background:var(--oxblood);border-color:var(--oxblood)}
|
|
200
|
-
.b270-reading.pp .b270-part.done .b270-check{background:var(--gold);border-color:var(--gold)}
|
|
241
|
+
.b270-reading.ot .b270-part.done .b270-check{background:var(--teal-fill);border-color:var(--teal-mark)}
|
|
242
|
+
.b270-reading.nt .b270-part.done .b270-check{background:var(--oxblood-fill);border-color:var(--oxblood-mark)}
|
|
243
|
+
.b270-reading.pp .b270-part.done .b270-check{background:var(--gold-fill);border-color:var(--gold-mark)}
|
|
201
244
|
.b270-part.done .b270-check svg{opacity:1}
|
|
202
245
|
.b270-parts-meta{margin-top:10px;font-size:12.5px;color:var(--faint)}
|
|
203
246
|
.b270-version{font-size:11.5px;color:var(--faint);font-weight:400;letter-spacing:.02em;white-space:nowrap}
|
|
204
|
-
.b270-select{font:inherit;font-size:14px;padding:10px 30px 10px 12px;border:1px solid var(--line);border-radius:9px;background
|
|
247
|
+
.b270-select{font:inherit;font-size:14px;padding:10px 30px 10px 12px;border:1px solid var(--control-line);border-radius:9px;background:var(--control);color:var(--ink);width:100%;max-width:100%;text-overflow:ellipsis}
|
|
205
248
|
.b270-source-choices{display:flex;align-items:center;justify-content:space-between;gap:4px;flex-wrap:nowrap;width:100%;margin:8px 0 2px;color:var(--muted);font-size:clamp(10.5px,3.4vw,12.5px)}
|
|
206
249
|
.b270-source-choice{display:inline-flex;align-items:center;gap:4px;cursor:pointer;white-space:nowrap}
|
|
207
|
-
.b270-source-choice input{appearance:none;width:14px;height:14px;margin:0;border:1px solid var(--line);border-radius:3px;background
|
|
250
|
+
.b270-source-choice input{appearance:none;width:14px;height:14px;margin:0;border:1px solid var(--control-line);border-radius:3px;background:var(--control);display:grid;place-content:center;cursor:pointer;flex:none}
|
|
208
251
|
.b270-source-choice input::before{content:"";width:9px;height:9px;transform:scale(0);transition:transform .1s;clip-path:polygon(14% 44%,0 59%,38% 100%,100% 18%,82% 0,37% 65%);background:var(--paper)}
|
|
209
252
|
.b270-source-choice input:checked{background:var(--gold);border-color:var(--gold)}
|
|
210
253
|
.b270-source-choice input:checked::before{transform:scale(1)}
|
|
@@ -219,7 +262,7 @@
|
|
|
219
262
|
|
|
220
263
|
/* completers */
|
|
221
264
|
.b270-completers{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:16px;font-size:13px;color:var(--muted)}
|
|
222
|
-
.b270-avatar{border-radius:50%;object-fit:cover;display:inline-block;text-align:center;background:var(--teal);color
|
|
265
|
+
.b270-avatar{border-radius:50%;object-fit:cover;display:inline-block;text-align:center;background:var(--teal-fill);color:var(--on-accent);font-size:12px;font-weight:600}
|
|
223
266
|
.b270-avatar-fallback{border-radius:50%}
|
|
224
267
|
|
|
225
268
|
/* comments */
|
|
@@ -281,8 +324,8 @@
|
|
|
281
324
|
.b270-comment.b270-editing{background:rgba(165,129,44,.05);border-radius:10px;padding:12px}
|
|
282
325
|
.b270-editing textarea{min-height:64px}
|
|
283
326
|
.b270-cdel{font-size:12px;color:var(--oxblood);background:none;border:none;cursor:pointer;padding:0;margin-left:8px}
|
|
284
|
-
.b270-input{width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:9px;background
|
|
285
|
-
.b270-form textarea{width:100%;font:inherit;font-size:15px;border:1px solid var(--line);border-radius:10px;padding:11px 13px;background
|
|
327
|
+
.b270-input{width:100%;padding:9px 11px;border:1px solid var(--control-line);border-radius:9px;background:var(--control);color:var(--ink);font:inherit;font-size:14px;margin-bottom:8px}
|
|
328
|
+
.b270-form textarea{width:100%;font:inherit;font-size:15px;border:1px solid var(--control-line);border-radius:10px;padding:11px 13px;background:var(--control);color:var(--ink);resize:vertical;min-height:76px}
|
|
286
329
|
.b270-form textarea[data-b270-autosize=false]{min-height:114px}
|
|
287
330
|
.b270-broadcast-audience{margin:8px 0}
|
|
288
331
|
.b270-editor-utilities[hidden]{display:none}
|
|
@@ -302,7 +345,7 @@
|
|
|
302
345
|
.b270-format-bold{font-weight:700}
|
|
303
346
|
.b270-format-italic{font-style:italic;font-weight:600}
|
|
304
347
|
.b270-markdown-editor[data-b270-formatting-open=true] textarea{border-radius:0 0 10px 10px}
|
|
305
|
-
.b270-markdown-preview{min-height:76px;margin:0;padding:11px 13px;border:1px solid var(--line);border-radius:10px;background
|
|
348
|
+
.b270-markdown-preview{min-height:76px;margin:0;padding:11px 13px;border:1px solid var(--control-line);border-radius:10px;background:var(--control);color:var(--ink)}
|
|
306
349
|
.b270-markdown-preview[hidden]{display:none}
|
|
307
350
|
.b270-markdown-preview>.b270-hint{margin:0}
|
|
308
351
|
.b270-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
|
|
@@ -316,15 +359,16 @@
|
|
|
316
359
|
.b270-mention-option:hover,.b270-mention-option[aria-selected=true]{background:var(--paper);color:var(--gold)}
|
|
317
360
|
.b270-draft-status{min-height:16px;margin:4px 0 -4px;font-size:11.5px;color:var(--faint)}
|
|
318
361
|
.b270-form .row{display:flex;align-items:center;gap:10px;margin-top:10px}
|
|
319
|
-
|
|
320
|
-
.b270 .
|
|
321
|
-
.b270-btn:
|
|
362
|
+
/* Override the reflection track selector's inline background and border color. */
|
|
363
|
+
.b270-form .row select{max-width:100%;background:var(--control)!important;border-color:var(--control-line)!important;color:var(--ink)}
|
|
364
|
+
.b270 .b270-btn{border:none;border-radius:10px;padding:8px 16px;margin-top:4px;font:inherit;font-weight:600;font-size:14px;cursor:pointer;background:var(--button-surface);color:var(--button-ink);text-decoration:none;display:inline-block}
|
|
365
|
+
.b270 .b270-btn:hover{background:var(--button-hover)}
|
|
322
366
|
|
|
323
367
|
/* community */
|
|
324
368
|
.b270-admin-reader-tools{display:flex;align-items:flex-end;justify-content:flex-end;gap:8px;margin:0 0 10px;flex-wrap:wrap}
|
|
325
369
|
.b270-admin-reader-sort{display:flex;align-items:flex-end;gap:8px}
|
|
326
370
|
.b270-admin-reader-sort label{display:grid;gap:3px;font-size:11.5px;color:var(--muted)}
|
|
327
|
-
.b270-admin-reader-sort select{min-width:170px;padding:7px 25px 7px 9px;border:1px solid var(--line);border-radius:8px;background
|
|
371
|
+
.b270-admin-reader-sort select{min-width:170px;padding:7px 25px 7px 9px;border:1px solid var(--control-line);border-radius:8px;background:var(--control);color:var(--ink);font:inherit;font-size:12.5px}
|
|
328
372
|
.b270 .b270-admin-reader-sort .b270-btn,.b270 .b270-reader-stats-open{margin:0;padding:8px 13px;font-size:12.5px}
|
|
329
373
|
.b270-reader-stats-dialog{width:min(680px,calc(100vw - 28px));max-width:none;padding:0;border:1px solid var(--line);border-radius:14px;background:var(--card);color:var(--ink);box-shadow:0 18px 60px rgba(35,31,24,.35)}
|
|
330
374
|
.b270-reader-stats-dialog::backdrop{background:rgba(35,31,24,.55)}
|
|
@@ -333,7 +377,7 @@
|
|
|
333
377
|
.b270-reader-stats-head .b270-subhead{margin:0}
|
|
334
378
|
.b270-reader-stats-head form{margin:0}
|
|
335
379
|
.b270-reader-stats-head .b270-linkbtn{margin:0;color:var(--muted)}
|
|
336
|
-
.b270-reader-stats-dialog pre{max-height:65vh;overflow:auto;margin:0;padding:12px;border:1px solid var(--line-soft);border-radius:9px;background
|
|
380
|
+
.b270-reader-stats-dialog pre{max-height:65vh;overflow:auto;margin:0;padding:12px;border:1px solid var(--line-soft);border-radius:9px;background:var(--control);color:var(--ink);font:12.5px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
|
|
337
381
|
.b270-list{background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);overflow:hidden}
|
|
338
382
|
.b270-li{display:flex;align-items:center;gap:14px;padding:14px 18px}
|
|
339
383
|
.b270-li+.b270-li{border-top:1px solid var(--line-soft)}
|
|
@@ -349,13 +393,13 @@
|
|
|
349
393
|
.b270 .b270-cell{aspect-ratio:1;border-radius:6px;border:1px solid var(--line);background:var(--card);font-size:10.5px;color:var(--faint);display:grid;place-items:center;text-decoration:none;font-variant-numeric:tabular-nums}
|
|
350
394
|
.b270-cell:hover{border-color:var(--gold);color:var(--gold)}
|
|
351
395
|
.b270-cell.partial{background:linear-gradient(135deg,var(--card) 50%,rgba(165,129,44,.35) 50%);color:var(--muted)}
|
|
352
|
-
.b270-cell.complete{background:var(--teal);border-color:var(--teal);color
|
|
396
|
+
.b270-cell.complete{background:var(--teal-fill);border-color:var(--teal-mark);color:var(--complete-ink);font-weight:600}
|
|
353
397
|
/* Days that have reflections carry a dot and nothing else: the fill stays
|
|
354
398
|
free to mean reading progress, so the two facts never compete. On a finished
|
|
355
|
-
square the dot
|
|
399
|
+
square the dot contrasts with teal, since oxblood on teal is hard to pick out. */
|
|
356
400
|
.b270-cell.talk{position:relative}
|
|
357
401
|
.b270-cell.talk::after{content:"";position:absolute;top:3px;right:3px;width:7px;height:7px;border-radius:50%;background:var(--oxblood)}
|
|
358
|
-
.b270-cell.talk.complete::after{background
|
|
402
|
+
.b270-cell.talk.complete::after{background:var(--complete-dot)}
|
|
359
403
|
.b270-cell.now{box-shadow:0 0 0 2px var(--gold)}
|
|
360
404
|
|
|
361
405
|
/* On the Reflections page each thread carries the day it belongs to, since
|
|
@@ -365,7 +409,7 @@
|
|
|
365
409
|
.b270-reflection-count{margin:3px 0 0;font-size:12px;color:var(--faint)}
|
|
366
410
|
.b270-reflection-filters{display:flex;align-items:flex-end;justify-content:flex-end;gap:8px;flex-wrap:wrap}
|
|
367
411
|
.b270-reflection-filters label{display:grid;gap:3px;font-size:11.5px;color:var(--muted)}
|
|
368
|
-
.b270-reflection-filters select{min-width:112px;max-width:180px;padding:7px 25px 7px 9px;border:1px solid var(--line);border-radius:8px;background
|
|
412
|
+
.b270-reflection-filters select{min-width:112px;max-width:180px;padding:7px 25px 7px 9px;border:1px solid var(--control-line);border-radius:8px;background:var(--control);color:var(--ink);font:inherit;font-size:12.5px}
|
|
369
413
|
.b270 .b270-reflection-filters .b270-btn{margin:0;padding:8px 13px;font-size:12.5px}
|
|
370
414
|
.b270 .b270-filter-clear{align-self:center;font-size:12px;color:var(--muted)}
|
|
371
415
|
.b270-thread{margin-bottom:8px}
|
|
@@ -399,11 +443,10 @@
|
|
|
399
443
|
.b270-navmenu{display:block}
|
|
400
444
|
.b270-navtoggle{display:flex;align-items:center;justify-content:center;width:40px;height:38px;
|
|
401
445
|
cursor:pointer;padding:0;border:1px solid var(--line);color:var(--ink);
|
|
402
|
-
border-radius:8px;background:var(--card)}
|
|
446
|
+
border-radius:8px;background:var(--card);-webkit-appearance:none;appearance:none}
|
|
403
447
|
.b270-navtoggle:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
|
|
404
|
-
.b270-menuicon{display:
|
|
405
|
-
.b270-menuicon
|
|
406
|
-
.b270-navtoggle[aria-expanded="true"] .b270-menuicon span{background:var(--gold)}
|
|
448
|
+
.b270-menuicon{display:block;flex:none;color:var(--ink)}
|
|
449
|
+
.b270-navtoggle[aria-expanded="true"] .b270-menuicon{color:var(--gold)}
|
|
407
450
|
.b270-navlinks[hidden]{display:none}
|
|
408
451
|
.b270-navlinks{position:absolute;z-index:20;right:0;top:calc(100% + 7px);min-width:180px;
|
|
409
452
|
flex-direction:column;align-items:stretch;gap:0;padding:6px;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<%= render "bible270/shared/styles" %>
|
|
14
14
|
<%= yield :head %>
|
|
15
15
|
</head>
|
|
16
|
-
<body class="b270">
|
|
16
|
+
<body class="b270" data-display-mode="<%= current_reader&.effective_display_mode || Bible270::Reader::DEFAULT_DISPLAY_MODE %>">
|
|
17
17
|
<div class="b270-wrap">
|
|
18
18
|
<%= render "bible270/shared/header" %>
|
|
19
19
|
<% if flash[:alert] %><div class="b270-flash alert"><%= flash[:alert] %></div><% end %>
|
|
@@ -32,6 +32,7 @@ module Bible270
|
|
|
32
32
|
'AddCompletionDoveDisabledToBible270Readers' => :check_completion_dove_disabled,
|
|
33
33
|
'AddBodyFormatToBible270Comments' => :check_comment_body_format,
|
|
34
34
|
'ChangeAllCommentNoticesDefaultToTrue' => :check_all_comment_notices_default,
|
|
35
|
+
'AddDisplayModeToBible270Readers' => :check_display_mode,
|
|
35
36
|
'CreateActiveStorageTables' => :check_active_storage
|
|
36
37
|
}.freeze
|
|
37
38
|
|
|
@@ -222,6 +223,16 @@ module Bible270
|
|
|
222
223
|
missing
|
|
223
224
|
end
|
|
224
225
|
|
|
226
|
+
def check_display_mode
|
|
227
|
+
missing = table_missing(:bible270_readers, columns: %i[display_mode])
|
|
228
|
+
return missing if missing.any?
|
|
229
|
+
|
|
230
|
+
column = connection.columns(:bible270_readers).find { |candidate| candidate.name == 'display_mode' }
|
|
231
|
+
missing << 'column bible270_readers.display_mode default system' unless column&.default == 'system'
|
|
232
|
+
missing << 'column bible270_readers.display_mode null false' unless column && !column.null
|
|
233
|
+
missing
|
|
234
|
+
end
|
|
235
|
+
|
|
225
236
|
def check_all_comment_notices_default
|
|
226
237
|
missing = table_missing(:bible270_readers, columns: %i[notify_on_all_comments])
|
|
227
238
|
return missing if missing.any?
|
data/lib/bible270/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bible270
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew vonderLuft
|
|
@@ -217,6 +217,7 @@ files:
|
|
|
217
217
|
- db/migrate/20260101000020_add_completion_dove_disabled_to_bible270_readers.rb
|
|
218
218
|
- db/migrate/20260101000021_add_body_format_to_bible270_comments.rb
|
|
219
219
|
- db/migrate/20260101000022_change_all_comment_notices_default_to_true.rb
|
|
220
|
+
- db/migrate/20260101000023_add_display_mode_to_bible270_readers.rb
|
|
220
221
|
- lib/bible270.rb
|
|
221
222
|
- lib/bible270/avatars.rb
|
|
222
223
|
- lib/bible270/comment_formatter.rb
|