Compact reader controls and margin layout
This commit is contained in:
+155
-59
@@ -11,6 +11,10 @@
|
||||
filter: blur(1.5px) brightness(0.62) saturate(0.72);
|
||||
}
|
||||
|
||||
.app-shell.notes-pinned .reader {
|
||||
padding-right: min(500px, 30vw);
|
||||
}
|
||||
|
||||
.tool-rail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -155,14 +159,13 @@
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
height: 100vh;
|
||||
padding: 18px 42px 18px 18px;
|
||||
padding: 10px 48px 12px 12px;
|
||||
box-sizing: border-box;
|
||||
transition: filter 160ms ease;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 6px;
|
||||
align-items: stretch;
|
||||
min-width: 0;
|
||||
@@ -170,15 +173,18 @@
|
||||
|
||||
.bible-top-tools {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: minmax(0, 1.42fr) minmax(160px, 1fr);
|
||||
gap: 6px;
|
||||
align-items: end;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.study-top-tools {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-width: 0;
|
||||
padding: 0 12px 3px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #d4cdbd;
|
||||
border-radius: 8px;
|
||||
background: #fbf8ef;
|
||||
@@ -188,6 +194,18 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.study-top-tools button {
|
||||
min-height: 28px;
|
||||
padding: 5px 9px;
|
||||
background: #2f6f5e;
|
||||
color: #fffef8;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.split-spacer {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.study-margin h2,
|
||||
.notes-workspace h2,
|
||||
.strong-focus h4 {
|
||||
@@ -206,7 +224,7 @@
|
||||
.search-box {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
input,
|
||||
@@ -216,11 +234,11 @@ textarea {
|
||||
min-width: 0;
|
||||
border: 1px solid #bdb8aa;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
padding: 5px 8px;
|
||||
background: #fffef9;
|
||||
color: #202124;
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -233,11 +251,11 @@ textarea {
|
||||
button {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
padding: 6px 9px;
|
||||
background: #d6a84f;
|
||||
color: #141510;
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -252,17 +270,17 @@ textarea:focus-visible {
|
||||
|
||||
.page-controls {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(92px, 1fr) minmax(118px, 1.24fr) minmax(72px, 0.74fr) minmax(68px, 0.66fr) minmax(96px, 0.9fr);
|
||||
gap: 8px;
|
||||
grid-template-columns: minmax(62px, 0.8fr) minmax(92px, 1.25fr) minmax(54px, 0.55fr) minmax(50px, 0.5fr) minmax(80px, 0.82fr);
|
||||
gap: 5px;
|
||||
align-items: end;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.page-controls label {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
gap: 2px;
|
||||
color: #657061;
|
||||
font-size: 11px;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
@@ -270,8 +288,7 @@ textarea:focus-visible {
|
||||
|
||||
.study-desk {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 6px;
|
||||
gap: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -282,6 +299,19 @@ textarea:focus-visible {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.split-resizer {
|
||||
width: 6px;
|
||||
min-width: 6px;
|
||||
cursor: col-resize;
|
||||
background: linear-gradient(90deg, transparent, #cfc6b4 45%, #cfc6b4 55%, transparent);
|
||||
}
|
||||
|
||||
.split-resizer:hover,
|
||||
.split-resizer:focus-visible {
|
||||
background: #d6a84f;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.page-system,
|
||||
.notes-workspace,
|
||||
.status-panel {
|
||||
@@ -310,7 +340,7 @@ textarea:focus-visible {
|
||||
grid-column: 1;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
min-height: 0;
|
||||
padding: 26px 30px;
|
||||
padding: 22px 26px;
|
||||
}
|
||||
|
||||
.bible-page header,
|
||||
@@ -327,7 +357,9 @@ textarea:focus-visible {
|
||||
|
||||
.page-turner {
|
||||
grid-column: 1;
|
||||
padding: 14px 30px;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
padding: 8px 26px 6px;
|
||||
border-top: 1px solid #e8e2d4;
|
||||
background: #fffef8;
|
||||
text-transform: none;
|
||||
@@ -335,10 +367,50 @@ textarea:focus-visible {
|
||||
|
||||
.page-turner span {
|
||||
color: #6b675d;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-turner button {
|
||||
align-self: start;
|
||||
min-height: 26px;
|
||||
padding: 4px 9px;
|
||||
}
|
||||
|
||||
.page-position {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
justify-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chapter-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
max-height: 38px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chapter-tabs button {
|
||||
min-width: 22px;
|
||||
min-height: 20px;
|
||||
padding: 2px 5px;
|
||||
border-color: #cfc4ad;
|
||||
background: #f2ead8;
|
||||
color: #4c422f;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chapter-tabs button.active,
|
||||
.chapter-tabs button:hover,
|
||||
.chapter-tabs button:focus-visible {
|
||||
background: #d6a84f;
|
||||
color: #151510;
|
||||
}
|
||||
|
||||
.page-columns {
|
||||
column-count: 2;
|
||||
column-gap: 34px;
|
||||
@@ -419,7 +491,7 @@ textarea:focus-visible {
|
||||
.notes-workspace {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.study-margin {
|
||||
@@ -435,13 +507,13 @@ textarea:focus-visible {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e4e0d6;
|
||||
}
|
||||
|
||||
.panel-header h2,
|
||||
.notes-workspace h2 {
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.panel-header span,
|
||||
@@ -453,16 +525,23 @@ textarea:focus-visible {
|
||||
}
|
||||
|
||||
.margin-reference {
|
||||
margin: 12px 0;
|
||||
margin: 8px 0;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.margin-resource-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.original-language-verse {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
margin: 0 0 14px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #dfd6c2;
|
||||
border-radius: 6px;
|
||||
@@ -471,11 +550,11 @@ textarea:focus-visible {
|
||||
|
||||
.margin-section-header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: -6px -6px 2px;
|
||||
padding: 7px 8px;
|
||||
margin: -5px -5px 1px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 5px;
|
||||
background: #f5efe1;
|
||||
cursor: pointer;
|
||||
@@ -492,7 +571,7 @@ textarea:focus-visible {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #657061;
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
@@ -505,16 +584,10 @@ textarea:focus-visible {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapse-button::before {
|
||||
margin-right: 5px;
|
||||
color: #735c0f;
|
||||
content: 'v';
|
||||
}
|
||||
|
||||
.mini-settings {
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 3px 6px;
|
||||
padding: 2px 5px;
|
||||
border: 1px solid #d8cfbf;
|
||||
border-radius: 4px;
|
||||
background: #fbf8ef;
|
||||
@@ -541,7 +614,7 @@ textarea:focus-visible {
|
||||
color: #1f211e;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-size: 20px;
|
||||
line-height: 1.7;
|
||||
line-height: 1.45;
|
||||
white-space: normal;
|
||||
word-break: normal;
|
||||
}
|
||||
@@ -573,9 +646,9 @@ textarea:focus-visible {
|
||||
|
||||
.english-strong-line {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin: 0 0 14px;
|
||||
padding: 12px;
|
||||
gap: 5px;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
border: 1px solid #dfd6c2;
|
||||
border-radius: 6px;
|
||||
background: #fffdf6;
|
||||
@@ -584,7 +657,7 @@ textarea:focus-visible {
|
||||
.linked-line-text {
|
||||
margin: 0;
|
||||
color: #202124;
|
||||
line-height: 1.65;
|
||||
line-height: 1.45;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@@ -609,8 +682,8 @@ textarea:focus-visible {
|
||||
|
||||
.strong-focus {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
padding: 16px;
|
||||
gap: 6px;
|
||||
padding: 8px;
|
||||
border: 1px solid #b9cabf;
|
||||
border-radius: 8px;
|
||||
background: #eef6f1;
|
||||
@@ -632,14 +705,14 @@ textarea:focus-visible {
|
||||
|
||||
.strong-focus h4 {
|
||||
color: #1f211e;
|
||||
font-size: 22px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.strong-focus p {
|
||||
margin: 0;
|
||||
color: #3f443d;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
font-size: 12px;
|
||||
line-height: 1.38;
|
||||
}
|
||||
|
||||
.strong-focus button {
|
||||
@@ -651,31 +724,36 @@ textarea:focus-visible {
|
||||
|
||||
.resource-card {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
margin-top: 14px;
|
||||
padding: 12px;
|
||||
gap: 6px;
|
||||
margin-top: 0;
|
||||
padding: 8px;
|
||||
border: 1px solid #dfd6c2;
|
||||
border-radius: 6px;
|
||||
background: #fffdf6;
|
||||
}
|
||||
|
||||
.commentary-card,
|
||||
.dictionary-card {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.commentary-list,
|
||||
.dictionary-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.commentary-list section,
|
||||
.dictionary-list section {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.commentary-list h3,
|
||||
.dictionary-list h3 {
|
||||
margin: 0;
|
||||
color: #252821;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
@@ -683,8 +761,8 @@ textarea:focus-visible {
|
||||
.dictionary-list p {
|
||||
margin: 0;
|
||||
color: #3f443d;
|
||||
font-size: 13px;
|
||||
line-height: 1.48;
|
||||
font-size: 12px;
|
||||
line-height: 1.38;
|
||||
}
|
||||
|
||||
.commentary-list small {
|
||||
@@ -713,15 +791,15 @@ textarea:focus-visible {
|
||||
right: 0;
|
||||
z-index: 28;
|
||||
display: grid;
|
||||
grid-template-columns: 30px minmax(360px, 30vw);
|
||||
width: min(540px, calc(100vw - 92px));
|
||||
grid-template-columns: 42px minmax(380px, 30vw);
|
||||
width: min(560px, calc(100vw - 92px));
|
||||
height: 100vh;
|
||||
transform: translateX(calc(100% - 30px));
|
||||
transform: translateX(calc(100% - 42px));
|
||||
transition: transform 190ms ease, box-shadow 190ms ease;
|
||||
}
|
||||
|
||||
.notes-drawer.open,
|
||||
.notes-drawer:hover,
|
||||
.notes-drawer.pinned,
|
||||
.notes-drawer:focus-within {
|
||||
transform: translateX(0);
|
||||
box-shadow: -18px 0 42px rgba(17, 20, 17, 0.24);
|
||||
@@ -740,6 +818,24 @@ textarea:focus-visible {
|
||||
font-weight: 900;
|
||||
letter-spacing: 0;
|
||||
cursor: pointer;
|
||||
width: 42px;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.notes-workspace > header {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pin-notes {
|
||||
background: #2f6f5e;
|
||||
color: #fffef8;
|
||||
}
|
||||
|
||||
.notes-drawer .notes-workspace {
|
||||
|
||||
+264
-35
@@ -6,7 +6,7 @@ import {
|
||||
getLibraryState,
|
||||
searchVerses,
|
||||
} from './studyClient'
|
||||
import type { DragEvent, ReactNode } from 'react'
|
||||
import type { DragEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react'
|
||||
import type { CommentaryEntry, DictionaryEntry, Verse } from './types'
|
||||
import './App.css'
|
||||
|
||||
@@ -219,6 +219,7 @@ function App() {
|
||||
const [timelineModalOpen, setTimelineModalOpen] = useState(false)
|
||||
const [settingsModalOpen, setSettingsModalOpen] = useState(false)
|
||||
const [notesDrawerOpen, setNotesDrawerOpen] = useState(false)
|
||||
const [notesPinned, setNotesPinned] = useState(false)
|
||||
const [pageMotion, setPageMotion] = useState<PageMotion>('idle')
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('notes')
|
||||
const [query, setQuery] = useState('')
|
||||
@@ -245,6 +246,12 @@ function App() {
|
||||
const [definitionCollapsed, setDefinitionCollapsed] = useState(false)
|
||||
const [commentaryCollapsed, setCommentaryCollapsed] = useState(false)
|
||||
const [dictionaryCollapsed, setDictionaryCollapsed] = useState(false)
|
||||
const [showOriginalLine, setShowOriginalLine] = useState(true)
|
||||
const [showLinkedLine, setShowLinkedLine] = useState(true)
|
||||
const [showDefinitionPanel, setShowDefinitionPanel] = useState(true)
|
||||
const [showCommentaryPanel, setShowCommentaryPanel] = useState(true)
|
||||
const [showDictionaryPanel, setShowDictionaryPanel] = useState(true)
|
||||
const [splitPercent, setSplitPercent] = useState(50)
|
||||
const [commentaryEntries, setCommentaryEntries] = useState<CommentaryEntry[]>([])
|
||||
const [dictionaryEntries, setDictionaryEntries] = useState<DictionaryEntry[]>([])
|
||||
const [marginResourcesLoading, setMarginResourcesLoading] = useState(false)
|
||||
@@ -252,6 +259,8 @@ function App() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const restoredSessionId = useRef('')
|
||||
const pageMotionTimeout = useRef<number | null>(null)
|
||||
const notesDragOpenTimeout = useRef<number | null>(null)
|
||||
const readerRef = useRef<HTMLElement | null>(null)
|
||||
|
||||
async function loadLibrary() {
|
||||
setError(null)
|
||||
@@ -389,12 +398,30 @@ function App() {
|
||||
originalCollapsed: boolean
|
||||
linkedCollapsed: boolean
|
||||
definitionCollapsed: boolean
|
||||
commentaryCollapsed: boolean
|
||||
dictionaryCollapsed: boolean
|
||||
showOriginalLine: boolean
|
||||
showLinkedLine: boolean
|
||||
showDefinitionPanel: boolean
|
||||
showCommentaryPanel: boolean
|
||||
showDictionaryPanel: boolean
|
||||
splitPercent: number
|
||||
notesPinned: boolean
|
||||
}>
|
||||
setLemmaFontSize(parsed.lemmaFontSize ?? 17)
|
||||
setLinkedFontSize(parsed.linkedFontSize ?? 15)
|
||||
setOriginalCollapsed(parsed.originalCollapsed ?? false)
|
||||
setLinkedCollapsed(parsed.linkedCollapsed ?? false)
|
||||
setDefinitionCollapsed(parsed.definitionCollapsed ?? false)
|
||||
setCommentaryCollapsed(parsed.commentaryCollapsed ?? false)
|
||||
setDictionaryCollapsed(parsed.dictionaryCollapsed ?? false)
|
||||
setShowOriginalLine(parsed.showOriginalLine ?? true)
|
||||
setShowLinkedLine(parsed.showLinkedLine ?? true)
|
||||
setShowDefinitionPanel(parsed.showDefinitionPanel ?? true)
|
||||
setShowCommentaryPanel(parsed.showCommentaryPanel ?? true)
|
||||
setShowDictionaryPanel(parsed.showDictionaryPanel ?? true)
|
||||
setSplitPercent(parsed.splitPercent ?? 50)
|
||||
setNotesPinned(parsed.notesPinned ?? false)
|
||||
} catch {
|
||||
localStorage.removeItem(studySettingsStorageKey)
|
||||
}
|
||||
@@ -457,9 +484,33 @@ function App() {
|
||||
originalCollapsed,
|
||||
linkedCollapsed,
|
||||
definitionCollapsed,
|
||||
commentaryCollapsed,
|
||||
dictionaryCollapsed,
|
||||
showOriginalLine,
|
||||
showLinkedLine,
|
||||
showDefinitionPanel,
|
||||
showCommentaryPanel,
|
||||
showDictionaryPanel,
|
||||
splitPercent,
|
||||
notesPinned,
|
||||
}),
|
||||
)
|
||||
}, [definitionCollapsed, lemmaFontSize, linkedCollapsed, linkedFontSize, originalCollapsed])
|
||||
}, [
|
||||
commentaryCollapsed,
|
||||
definitionCollapsed,
|
||||
dictionaryCollapsed,
|
||||
lemmaFontSize,
|
||||
linkedCollapsed,
|
||||
linkedFontSize,
|
||||
notesPinned,
|
||||
originalCollapsed,
|
||||
showCommentaryPanel,
|
||||
showDefinitionPanel,
|
||||
showDictionaryPanel,
|
||||
showLinkedLine,
|
||||
showOriginalLine,
|
||||
splitPercent,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (restoredSessionId.current === activeSessionId) return
|
||||
@@ -724,6 +775,34 @@ function App() {
|
||||
)
|
||||
}
|
||||
|
||||
function recordDisplaySetting(label: string) {
|
||||
recordTimeline({
|
||||
type: 'setting',
|
||||
label,
|
||||
})
|
||||
}
|
||||
|
||||
function beginSplitResize(event: ReactMouseEvent<HTMLDivElement>) {
|
||||
event.preventDefault()
|
||||
const reader = readerRef.current
|
||||
if (!reader) return
|
||||
|
||||
const resize = (moveEvent: MouseEvent) => {
|
||||
const rect = reader.getBoundingClientRect()
|
||||
const next = ((moveEvent.clientX - rect.left) / rect.width) * 100
|
||||
setSplitPercent(Math.min(68, Math.max(36, next)))
|
||||
}
|
||||
|
||||
const stop = () => {
|
||||
window.removeEventListener('mousemove', resize)
|
||||
window.removeEventListener('mouseup', stop)
|
||||
recordDisplaySetting('Adjusted Bible and study margin split')
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', resize)
|
||||
window.addEventListener('mouseup', stop, { once: true })
|
||||
}
|
||||
|
||||
function updateNoteDraft(value: string) {
|
||||
setNoteDraft(value)
|
||||
patchActiveSession({ noteDraft: value })
|
||||
@@ -739,7 +818,20 @@ function App() {
|
||||
function startTextDrag(event: DragEvent<HTMLElement>, value: string) {
|
||||
event.dataTransfer.setData('text/plain', value)
|
||||
event.dataTransfer.effectAllowed = 'copy'
|
||||
setNotesDrawerOpen(true)
|
||||
}
|
||||
|
||||
function scheduleNotesDragOpen() {
|
||||
if (notesPinned || notesDragOpenTimeout.current) return
|
||||
notesDragOpenTimeout.current = window.setTimeout(() => {
|
||||
setNotesDrawerOpen(true)
|
||||
notesDragOpenTimeout.current = null
|
||||
}, 550)
|
||||
}
|
||||
|
||||
function clearNotesDragOpen() {
|
||||
if (!notesDragOpenTimeout.current) return
|
||||
window.clearTimeout(notesDragOpenTimeout.current)
|
||||
notesDragOpenTimeout.current = null
|
||||
}
|
||||
|
||||
function renameActiveSession(title: string) {
|
||||
@@ -881,7 +973,7 @@ function App() {
|
||||
}
|
||||
|
||||
function renderOriginalLanguageLine() {
|
||||
if (!selectedVerse?.strongs.length) return null
|
||||
if (!showOriginalLine || !selectedVerse?.strongs.length) return null
|
||||
const isHebrew = selectedVerse.strongs.some((entry) => entry.strong.startsWith('H'))
|
||||
const direction = isHebrew ? 'rtl' : 'ltr'
|
||||
|
||||
@@ -891,11 +983,15 @@ function App() {
|
||||
className="margin-section-header"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setOriginalCollapsed((current) => !current)}
|
||||
onClick={() => {
|
||||
setOriginalCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled original language accordion')
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
setOriginalCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled original language accordion')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -943,7 +1039,7 @@ function App() {
|
||||
}
|
||||
|
||||
function renderLinkedEnglishLine() {
|
||||
if (!selectedVerse?.strongs.length) return null
|
||||
if (!showLinkedLine || !selectedVerse?.strongs.length) return null
|
||||
|
||||
return (
|
||||
<section className="english-strong-line" aria-label="English Strong's line">
|
||||
@@ -951,11 +1047,15 @@ function App() {
|
||||
className="margin-section-header"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setLinkedCollapsed((current) => !current)}
|
||||
onClick={() => {
|
||||
setLinkedCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled English linked text accordion')
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
setLinkedCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled English linked text accordion')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1003,6 +1103,8 @@ function App() {
|
||||
}
|
||||
|
||||
function renderCommentarySection() {
|
||||
if (!showCommentaryPanel) return null
|
||||
|
||||
if (activeSession?.commentaryEnabled === false) {
|
||||
return (
|
||||
<article className="resource-card">
|
||||
@@ -1021,11 +1123,15 @@ function App() {
|
||||
className="margin-section-header"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setCommentaryCollapsed((current) => !current)}
|
||||
onClick={() => {
|
||||
setCommentaryCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled commentary accordion')
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
setCommentaryCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled commentary accordion')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1060,6 +1166,8 @@ function App() {
|
||||
}
|
||||
|
||||
function renderDictionarySection() {
|
||||
if (!showDictionaryPanel) return null
|
||||
|
||||
if (activeSession?.dictionaryEnabled === false) {
|
||||
return (
|
||||
<article className="resource-card">
|
||||
@@ -1078,11 +1186,15 @@ function App() {
|
||||
className="margin-section-header"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setDictionaryCollapsed((current) => !current)}
|
||||
onClick={() => {
|
||||
setDictionaryCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled dictionary accordion')
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
setDictionaryCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled dictionary accordion')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1158,7 +1270,21 @@ function App() {
|
||||
<button type="button" onClick={() => shiftPage(-1)}>
|
||||
Previous page
|
||||
</button>
|
||||
<span>King James Version - {pageRange}</span>
|
||||
<div className="page-position">
|
||||
<span>{pageRange}</span>
|
||||
<div className="chapter-tabs" aria-label="Chapter selector">
|
||||
{chapters.map((chapter) => (
|
||||
<button
|
||||
key={chapter}
|
||||
type="button"
|
||||
className={chapter === selectedVerse?.chapter ? 'active' : ''}
|
||||
onClick={() => selectedVerse && jumpTo(selectedVerse.book, chapter, 1)}
|
||||
>
|
||||
{chapter}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick={() => shiftPage(1)}>
|
||||
Next page
|
||||
</button>
|
||||
@@ -1182,20 +1308,24 @@ function App() {
|
||||
{activeSession?.strongsEnabled === false ? (
|
||||
<p className="resource-disabled">Strong's links are disabled for this session.</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="margin-resource-grid">
|
||||
{renderOriginalLanguageLine()}
|
||||
{renderLinkedEnglishLine()}
|
||||
{selectedStrongEntry ? (
|
||||
{showDefinitionPanel && selectedStrongEntry ? (
|
||||
<article className="strong-focus" aria-label="Strong's definition">
|
||||
<header
|
||||
className="margin-section-header"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setDefinitionCollapsed((current) => !current)}
|
||||
onClick={() => {
|
||||
setDefinitionCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled Strong definition accordion')
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
setDefinitionCollapsed((current) => !current)
|
||||
recordDisplaySetting('Toggled Strong definition accordion')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1223,10 +1353,12 @@ function App() {
|
||||
) : null}
|
||||
</article>
|
||||
) : null}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
{renderCommentarySection()}
|
||||
{renderDictionarySection()}
|
||||
<div className="margin-resource-grid">
|
||||
{renderCommentarySection()}
|
||||
{renderDictionarySection()}
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
@@ -1617,8 +1749,15 @@ function App() {
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!originalCollapsed}
|
||||
onChange={(event) => setOriginalCollapsed(!event.target.checked)}
|
||||
checked={showOriginalLine}
|
||||
onChange={(event) => {
|
||||
setShowOriginalLine(event.target.checked)
|
||||
recordDisplaySetting(
|
||||
event.target.checked
|
||||
? 'Show original language line'
|
||||
: 'Hide original language line',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Show original language line
|
||||
</label>
|
||||
@@ -1640,8 +1779,13 @@ function App() {
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!linkedCollapsed}
|
||||
onChange={(event) => setLinkedCollapsed(!event.target.checked)}
|
||||
checked={showLinkedLine}
|
||||
onChange={(event) => {
|
||||
setShowLinkedLine(event.target.checked)
|
||||
recordDisplaySetting(
|
||||
event.target.checked ? 'Show English linked text' : 'Hide English linked text',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Show English linked text
|
||||
</label>
|
||||
@@ -1652,12 +1796,47 @@ function App() {
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!definitionCollapsed}
|
||||
onChange={(event) => setDefinitionCollapsed(!event.target.checked)}
|
||||
checked={showDefinitionPanel}
|
||||
onChange={(event) => {
|
||||
setShowDefinitionPanel(event.target.checked)
|
||||
recordDisplaySetting(
|
||||
event.target.checked ? 'Show Strong definition' : 'Hide Strong definition',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Show expanded definition
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section className="modal-section">
|
||||
<h3>Reference Panels</h3>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showCommentaryPanel}
|
||||
onChange={(event) => {
|
||||
setShowCommentaryPanel(event.target.checked)
|
||||
recordDisplaySetting(
|
||||
event.target.checked ? 'Show commentary panel' : 'Hide commentary panel',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Show commentary
|
||||
</label>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showDictionaryPanel}
|
||||
onChange={(event) => {
|
||||
setShowDictionaryPanel(event.target.checked)
|
||||
recordDisplaySetting(
|
||||
event.target.checked ? 'Show dictionary panel' : 'Hide dictionary panel',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Show dictionary
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -1669,32 +1848,58 @@ function App() {
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={notesDrawerOpen ? 'notes-drawer open' : 'notes-drawer'}
|
||||
className={[
|
||||
'notes-drawer',
|
||||
notesDrawerOpen || notesPinned ? 'open' : '',
|
||||
notesPinned ? 'pinned' : '',
|
||||
].filter(Boolean).join(' ')}
|
||||
aria-label="Notes drawer"
|
||||
onMouseEnter={() => setNotesDrawerOpen(true)}
|
||||
onMouseLeave={() => setNotesDrawerOpen(false)}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault()
|
||||
setNotesDrawerOpen(true)
|
||||
scheduleNotesDragOpen()
|
||||
}}
|
||||
onDragLeave={clearNotesDragOpen}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault()
|
||||
clearNotesDragOpen()
|
||||
appendNoteClip(event.dataTransfer.getData('text/plain'))
|
||||
setNotesDrawerOpen(true)
|
||||
}}
|
||||
>
|
||||
<div className="notes-drawer-grip" aria-hidden="true">
|
||||
<button
|
||||
type="button"
|
||||
className="notes-drawer-grip"
|
||||
aria-label={notesDrawerOpen || notesPinned ? 'Close notes drawer' : 'Open notes drawer'}
|
||||
onClick={() => {
|
||||
if (notesPinned) return
|
||||
setNotesDrawerOpen((current) => !current)
|
||||
}}
|
||||
>
|
||||
<span>N</span>
|
||||
<span>O</span>
|
||||
<span>T</span>
|
||||
<span>E</span>
|
||||
<span>S</span>
|
||||
</div>
|
||||
</button>
|
||||
<div className="notes-workspace">
|
||||
<header>
|
||||
<span className="eyebrow">Notes</span>
|
||||
<h2>Study Notes</h2>
|
||||
<div>
|
||||
<span className="eyebrow">Notes</span>
|
||||
<h2>Study Notes</h2>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="pin-notes"
|
||||
onClick={() => {
|
||||
const nextPinned = !notesPinned
|
||||
setNotesPinned(nextPinned)
|
||||
setNotesDrawerOpen(nextPinned)
|
||||
recordDisplaySetting(nextPinned ? 'Pinned notes drawer' : 'Unpinned notes drawer')
|
||||
}}
|
||||
>
|
||||
{notesPinned ? 'Unpin' : 'Pin'}
|
||||
</button>
|
||||
</header>
|
||||
<textarea
|
||||
value={noteDraft}
|
||||
@@ -1751,8 +1956,16 @@ function App() {
|
||||
)
|
||||
}
|
||||
|
||||
const splitTemplate = `${splitPercent}fr 6px ${100 - splitPercent}fr`
|
||||
|
||||
return (
|
||||
<main className={railOpen ? 'app-shell menu-open' : 'app-shell'}>
|
||||
<main
|
||||
className={[
|
||||
'app-shell',
|
||||
railOpen ? 'menu-open' : '',
|
||||
notesPinned ? 'notes-pinned' : '',
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
<aside
|
||||
className="tool-rail"
|
||||
aria-label="Main tools"
|
||||
@@ -1792,14 +2005,17 @@ function App() {
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<section className="reader">
|
||||
<section className="reader" ref={readerRef}>
|
||||
{loading ? (
|
||||
<div className="status-panel">Loading local library...</div>
|
||||
) : error ? (
|
||||
<div className="status-panel error">{error}</div>
|
||||
) : selectedVerse ? (
|
||||
<>
|
||||
<header className="top-bar">
|
||||
<header
|
||||
className="top-bar"
|
||||
style={{ gridTemplateColumns: splitTemplate }}
|
||||
>
|
||||
<div className="bible-top-tools">
|
||||
<div className="page-controls" aria-label="Page selectors">
|
||||
<label>
|
||||
@@ -1885,12 +2101,18 @@ function App() {
|
||||
<button type="submit">Go</button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="split-spacer" aria-hidden="true" />
|
||||
<div className="study-top-tools" aria-label="Study margin tools">
|
||||
<span>Study margin tools</span>
|
||||
<button type="button" onClick={() => setSettingsModalOpen(true)}>
|
||||
Margin settings
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className={`study-desk ${viewMode}`}>
|
||||
<div
|
||||
className={`study-desk ${viewMode}`}
|
||||
style={{ gridTemplateColumns: splitTemplate }}
|
||||
>
|
||||
{viewMode === 'compare' ? (
|
||||
<div className="page-system compare-system" aria-label="Compare translations">
|
||||
<div className="compare-pages">
|
||||
@@ -1918,6 +2140,13 @@ function App() {
|
||||
{renderPageTurner()}
|
||||
{renderBookTabs()}
|
||||
</div>
|
||||
<div
|
||||
className="split-resizer"
|
||||
role="separator"
|
||||
aria-label="Resize Bible and study margin"
|
||||
aria-orientation="vertical"
|
||||
onMouseDown={beginSplitResize}
|
||||
/>
|
||||
{renderStudyMargin()}
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user