From 8db3b3d3635cd86bdf65a028eb3f5a267266f36a Mon Sep 17 00:00:00 2001 From: Franzz Date: Sun, 20 Sep 2026 00:21:30 +0200 Subject: [PATCH] Change settings button position --- resources/lang/en.json | 2 +- resources/lang/fr.json | 2 +- src/App.vue | 53 ++++++++++++++++++++++++++++++-- src/components/BookmarkRail.vue | 36 ++++++++++++++++++++-- src/components/SettingsPanel.vue | 18 +---------- src/scripts/icons.js | 14 +++------ src/scripts/journal.js | 27 ++++++++++++++++ src/styles/_app.scss | 10 ++++++ src/styles/_book.scss | 22 ++++++++++--- src/styles/_mobile.scss | 49 +++++++++++++++++++++++------ src/styles/_rail.scss | 52 +++++++++++++++++++++++++++++++ src/styles/_var.scss | 3 +- 12 files changed, 240 insertions(+), 48 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 223daa9..1a16e6c 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -47,7 +47,7 @@ "loading": "Turning pages…", "today": "Today", "go_to_writing": "Back to today's page", - "empty_entry": "Blank page", + "new_entry": "Start a new page", "earlier": "Earlier entries", "later": "Later entries" }, diff --git a/resources/lang/fr.json b/resources/lang/fr.json index 7c605aa..3be4783 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -47,7 +47,7 @@ "loading": "On tourne les pages…", "today": "Aujourd'hui", "go_to_writing": "Revenir à la page du jour", - "empty_entry": "Page blanche", + "new_entry": "Commencer une nouvelle page", "earlier": "Entrées précédentes", "later": "Entrées suivantes" }, diff --git a/src/App.vue b/src/App.vue index 96828eb..b9b50f3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -35,6 +35,7 @@ export default { bCalendarOpen: false, bSettingsOpen: false, bMenuOpen: false, + bSigningOut: false, bRailOpen: false, iCurrentId: 0, sCurrentDay: '', @@ -127,13 +128,51 @@ export default { this.$refs.book?.goToWriting(); }, + /** + * The + at the end of the rail: seal the page being written and turn to + * a fresh one. Landing there should mean you can simply write, the same + * way opening the book does. + */ + async onNewEntry() { + this.bRailOpen = false; + + try { + await this.journal.startNewEntry(); + this.$nextTick(() => this.$refs.book?.goToWriting()); + } + catch(oError) { + this.notify(oError.desc_lang_text || oError.message, true); + } + }, + /* Account */ onSignedIn(asUser) { Object.assign(this.user, asUser); }, - async onSignedOut() { + /** + * The logout call is what ends the session; the local reset only + * follows it, so a failed call leaves the reader where they were. + */ + async signOut() { + if(this.bSigningOut) return; + + this.bSigningOut = true; + + try { + await this.api.post('logout'); + this.clearSession(); + } + catch(oError) { + this.notify(oError.desc_lang_text || oError.message || this.lang.get('error.unexpected'), true); + } + finally { + this.bSigningOut = false; + } + }, + + clearSession() { this.bSettingsOpen = false; this.bMenuOpen = false; @@ -259,6 +298,16 @@ export default { {{ lang.get('account.settings') }} + + @@ -287,6 +336,7 @@ export default { :loading="journal.loading" @pick="onPickBookmark" @load-older="onLoadOlder" + @new="onNewEntry" /> @@ -304,7 +354,6 @@ export default { diff --git a/src/components/BookmarkRail.vue b/src/components/BookmarkRail.vue index 7f73285..8b18f0a 100644 --- a/src/components/BookmarkRail.vue +++ b/src/components/BookmarkRail.vue @@ -40,6 +40,10 @@ const RAIL_REACH = 3; * Each is stamped with the date and time the entry was started, in the timezone * it was written in - so a book kept across a move still reads as the local * time of each moment. Clicking one turns the book to that page. + * + * A page nothing has been written on is not one of them: an empty tab marks + * nothing, and a book of them would be a rail of blanks. The + at the end of + * the rail stands there instead, and starts the page it would have marked. */ export default { components: { @@ -54,15 +58,18 @@ export default { hasOlder: {type: Boolean, default: false}, loading: {type: Boolean, default: false} }, - emits: ['pick', 'load-older'], + emits: ['pick', 'load-older', 'new'], inject: ['lang'], computed: { tabs() { - return this.bookmarks.map((oBookmark) => ({ + //A bookmark is a page with something on it. The one page that can be + //empty is the one being written, and it gets its tab with its first + //words - closed entries are never blank, the book discards those. + return this.bookmarks.filter((oBookmark) => (oBookmark.preview || '') !== '').map((oBookmark) => ({ id: oBookmark.id, date: formatShortDate(oBookmark.time, oBookmark.timezone, this.lang.locale), time: formatTime(oBookmark.time, oBookmark.timezone, this.lang.locale), - preview: oBookmark.preview || this.lang.get('book.empty_entry'), + preview: oBookmark.preview, open: (oBookmark.status === 'open'), dye: dyeForDay(getDayKey(oBookmark.time, oBookmark.timezone)) })); @@ -89,6 +96,16 @@ export default { }, hasAfter() { return (this.shown.length > 0) && (this.shown.at(-1).id !== this.tabs.at(-1)?.id); + }, + /** + * Whether the book is open at a page still to be written on. + * + * That page has no tab, so the + is what marks it - and the rail would + * otherwise show nothing at all as the place you are reading. + */ + onBlankPage() { + const oOpen = this.bookmarks.find((oBookmark) => oBookmark.id === this.openId); + return (this.currentId === this.openId) && (oOpen !== undefined) && ((oOpen.preview || '') === ''); } }, watch: { @@ -162,6 +179,19 @@ export default { {{ oTab.preview }} + + + - diff --git a/src/scripts/icons.js b/src/scripts/icons.js index 7377f1a..ca31132 100644 --- a/src/scripts/icons.js +++ b/src/scripts/icons.js @@ -38,16 +38,10 @@ const asIcons = { 'M12 11.6a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2z', 'M4.8 20c.6-3.7 3.5-5.8 7.2-5.8s6.6 2.1 7.2 5.8' ], + //A cog: six teeth around a hub settings: [ - 'M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4z', - 'M12 3v2.3', - 'M12 18.7V21', - 'M3 12h2.3', - 'M18.7 12H21', - 'M5.6 5.6l1.7 1.7', - 'M16.7 16.7l1.7 1.7', - 'M18.4 5.6l-1.7 1.7', - 'M7.3 16.7l-1.7 1.7' + 'M10.3 6L10.4 3.1h3.2L13.7 6a6.2 6.2 0 0 1 2.6 1.5l2.6-1.3 1.6 2.7-2.5 1.6a6.2 6.2 0 0 1 0 3l2.5 1.6-1.6 2.7-2.6-1.3a6.2 6.2 0 0 1-2.6 1.5l-.1 2.9h-3.2L10.3 18a6.2 6.2 0 0 1-2.6-1.5l-2.6 1.3-1.6-2.7 2.5-1.6a6.2 6.2 0 0 1 0-3L3.5 8.9l1.6-2.7 2.6 1.3A6.2 6.2 0 0 1 10.3 6z', + 'M12 14.8a2.8 2.8 0 1 0 0-5.6 2.8 2.8 0 0 0 0 5.6z' ], signOut: [ 'M14.4 8V5.2H5.6v13.6h8.8V16', @@ -61,6 +55,8 @@ const asIcons = { 'M10.5 10.9v5.7', 'M13.5 10.9v5.7' ], + //A blank page waiting to be started + plus: ['M12 5.6v12.8', 'M5.6 12h12.8'], close: ['M6.6 6.6l10.8 10.8', 'M17.4 6.6 6.6 17.4'], check: ['M5.6 12.7l4.2 4.2 8.6-9.6'], alert: [ diff --git a/src/scripts/journal.js b/src/scripts/journal.js index e508bf9..755b6e6 100644 --- a/src/scripts/journal.js +++ b/src/scripts/journal.js @@ -172,6 +172,33 @@ export default class Journal { return oEntry; } + /** + * Start a page of one's own, rather than writing on in the last one. + * + * A page nothing has been written on is already that page, so it is handed + * straight back - it is only a page with writing on it that has to be sealed + * first, which is what makes the next one a new entry and not the same + * thought carried on. + */ + async startNewEntry() { + const oEntry = this.openEntry; + if(oEntry && (oEntry.content.trim() === '')) return oEntry; + + //The close carries the content itself, but the save state goes with it - + //flushing here is what keeps the indicator from sitting on "unsaved" + //about a page that has already been sealed. + if(this.isDirty) await this.save(); + + await this.closeEntry(); + + //A new page goes at the end of the book, so the loaded window has to be + //the end of the book before one can be appended to it - reading back in + //the journal would otherwise slot it into the middle of the flow. + if(this.hasNewer) await this.load(); + + return this.startWriting(); + } + write(sContent) { const oEntry = this.openEntry; if(!oEntry) return; diff --git a/src/styles/_app.scss b/src/styles/_app.scss index 8d712ee..f1ab72f 100644 --- a/src/styles/_app.scss +++ b/src/styles/_app.scss @@ -206,3 +206,13 @@ background-color: color.$paper-shade; } } + +.account-menu__item--bad { + color: color.$bad; + + &:hover, + &:focus-visible { + color: color.$bad; + background-color: rgba(155, 70, 49, 0.09); + } +} diff --git a/src/styles/_book.scss b/src/styles/_book.scss index 10feeba..6c4c6f3 100644 --- a/src/styles/_book.scss +++ b/src/styles/_book.scss @@ -170,28 +170,40 @@ border-right: 1px solid color.$rule-margin; } -/* Where an entry begins, the margin says when it was written */ +/* Where an entry begins, the margin says when it was written. + * + * Sized off the ruled line like the writing it annotates, so it follows the + * hand in use instead of holding one fixed size across six of them - a stamp + * in a wide hand at a fixed size is wider than the column it sits in. Both + * spans are led at half a rule, so the usual date-over-time comes to exactly + * one ruled line and a date long enough to wrap still lands on the book's + * rhythm rather than between it. */ .page__stamp { + --stamp-size: calc(var(--book-line) * #{var.$hand-scale} * 0.5); + position: absolute; right: 0.7rem; width: calc(100% - 0.7rem); text-align: right; font-family: var.$font-hand; - line-height: 1.05; + line-height: calc(var(--book-line) / 2); color: color.$ink-soft; pointer-events: none; } .page__stamp-date { display: block; - font-size: 1.05rem; + font-size: var(--stamp-size); font-weight: 600; - white-space: nowrap; + + //A written-out month in a wide hand, or a locale that spells the date out + //at length, wraps inside the margin instead of spilling over the page. + overflow-wrap: break-word; } .page__stamp-time { display: block; - font-size: 0.95rem; + font-size: calc(var(--stamp-size) * 0.9); color: color.$ink-faint; } diff --git a/src/styles/_mobile.scss b/src/styles/_mobile.scss index 48e1eea..cf1a4da 100644 --- a/src/styles/_mobile.scss +++ b/src/styles/_mobile.scss @@ -77,23 +77,19 @@ } .page__margin { - flex-basis: 3.1rem; + flex-basis: 4.1rem; padding-right: 0.4rem; } + //A phone gives the margin less room, so the stamp is written smaller in it + //rather than wider than it. .page__stamp { + --stamp-size: calc(var(--book-line) * #{var.$hand-scale} * 0.42); + right: 0.4rem; width: calc(100% - 0.4rem); } - .page__stamp-date { - font-size: 0.9rem; - } - - .page__stamp-time { - font-size: 0.82rem; - } - .page__column, .page__measure, .page__input, @@ -175,6 +171,41 @@ } } + /* In the drawer there is no page block for a tab to come out from under, so + * the + is a card like the tabs are - drawn in the light ink the cover asks + * for rather than in paper tones, and it stays put instead of sliding out. */ + .rail__new { + width: 100%; + min-height: 2.4rem; + margin-left: 0; + padding-left: 0; + color: rgba(254, 244, 225, 0.6); + background-color: rgba(254, 244, 225, 0.06); + border: 1px dashed rgba(254, 244, 225, 0.3); + border-radius: var.$block-radius; + + &:hover, + &:focus-visible { + transform: none; + color: color.$paper; + background-color: rgba(254, 244, 225, 0.12); + } + } + + .rail__new--current { + transform: none; + color: color.$paper; + background-color: rgba(254, 244, 225, 0.14); + border-style: solid; + border-color: rgba(254, 244, 225, 0.35); + box-shadow: inset -0.2rem 0 0 color.$ribbon; + + &:hover, + &:focus-visible { + transform: none; + } + } + .calendar { width: min(19rem, calc(100vw - 1.5rem)); } diff --git a/src/styles/_rail.scss b/src/styles/_rail.scss index 17ebc43..dfa112d 100644 --- a/src/styles/_rail.scss +++ b/src/styles/_rail.scss @@ -159,6 +159,58 @@ white-space: nowrap; } +/* The page nobody has written on yet. + * + * Shaped like a tab, because that is what it becomes the moment something is + * written on it - but a short one, drawn in outline: there is no date to stamp + * on it and no day for it to take a dye from. It stands in the stack directly + * after the last tab, which is where its own tab will be. + */ +.rail__new { + flex: 0 0 auto; + align-self: flex-start; + display: flex; + align-items: center; + justify-content: center; + width: 4.75rem; + min-height: 1.9rem; + //The list's own gap sets it off from the last tab + margin-left: calc(-1 * var.$tab-tuck); + padding-left: var.$tab-tuck; + + color: color.$ink-faint; + background-color: rgba(251, 241, 220, 0.5); + //No left edge: like a tab, it comes out from under the page block + border: 1px dashed color.$paper-deep; + border-left: 0; + border-radius: 0 var.$block-radius var.$block-radius 0; + + transition: transform var.$trans-mid ease-out, background-color var.$trans-quick, color var.$trans-quick; + + &:hover, + &:focus-visible { + transform: translateX(0.45rem); + background-color: color.$paper; + color: color.$ink-soft; + } +} + +/* The book is open at that page, so the + is doing a bookmark's job - and wears + * the same ribbon the tab of the page being written on would. */ +.rail__new--current { + transform: translateX(0.3rem); + color: color.$ink-soft; + background-color: color.$paper; + border-style: solid; + border-color: color.$paper-edge; + box-shadow: 0.12rem 0.15rem 0.5rem rgba(90, 54, 31, 0.22), inset -0.2rem 0 0 color.$ribbon; + + &:hover, + &:focus-visible { + transform: translateX(0.65rem); + } +} + /* The rail shows a few tabs either side of where the reading is. These two * say the book carries on past them, and walk the window along. */ .rail__more { diff --git a/src/styles/_var.scss b/src/styles/_var.scss index 75e580f..937a16d 100644 --- a/src/styles/_var.scss +++ b/src/styles/_var.scss @@ -14,7 +14,8 @@ $block-spacing: 1rem; $block-radius: 3px; //The book -$page-margin-width: 4.25rem; //the left column that carries date stamps +$page-margin-width: 5.25rem; //the left column that carries date stamps: wide + //enough for a written-out date in the widest hand $page-padding: 2.25rem; $spine-width: 2.5rem; $book-max-width: 72rem; //leaves the rail room to show its dates