marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

commit 5ec023afa32753527a057c560f4d7c1da8e68b74
parent ece368e219e9165e8094446c1dbbf4150948c39a
Author: David Eisinger <[email protected]>
Date:   Sat, 11 Oct 2025 21:40:43 -0400

URL truncation

Diffstat:
Minternal/web/app.js | 23++++++++++++++++-------
Minternal/web/index.html | 4++--
Minternal/web/styles.css | 6+++++-
Minternal/web/user.html | 4++--
4 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/internal/web/app.js b/internal/web/app.js @@ -23,6 +23,14 @@ createApp({ this.initView(); }, methods: { + normalizeResults(arr) { + return arr.map((x) => ({ + ...x, + description: x.description ?? null, + public: !!x.public, + urlTrunc: true, + })); + }, async initView() { // Determine logged-in user from server-embedded data if (window.MARKY_ME) this.me = { username: window.MARKY_ME }; @@ -51,7 +59,8 @@ createApp({ // Initialize from server-embedded data when available try { if (window.MARKY_INITIAL && window.MARKY_INITIAL.view === 'self' && window.MARKY_INITIAL.user === this.me.username) { - this.results = Array.isArray(window.MARKY_INITIAL.results) ? window.MARKY_INITIAL.results : []; + const r = Array.isArray(window.MARKY_INITIAL.results) ? window.MARKY_INITIAL.results : []; + this.results = this.normalizeResults(r); } } catch {} // Do not auto-fetch all bookmarks on initial load @@ -60,7 +69,8 @@ createApp({ // Initialize from server-embedded data when available; no network fallback try { if (window.MARKY_INITIAL && window.MARKY_INITIAL.view === 'public' && window.MARKY_INITIAL.user === this.viewUser) { - this.results = Array.isArray(window.MARKY_INITIAL.results) ? window.MARKY_INITIAL.results : []; + const r = Array.isArray(window.MARKY_INITIAL.results) ? window.MARKY_INITIAL.results : []; + this.results = this.normalizeResults(r); } } catch {} } @@ -212,11 +222,7 @@ createApp({ const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); const arr = await res.json(); - this.results = arr.map((x) => ({ - ...x, - description: x.description ?? null, - public: !!x.public, - })); + this.results = this.normalizeResults(arr); } catch (e) { console.error(e); } finally { @@ -265,5 +271,8 @@ createApp({ // Our API wraps matches with guillemets « » — convert to <mark> return ex.replace(/«/g, '<mark>').replace(/»/g, '</mark>'); }, + toggleURL(b) { + b.urlTrunc = !b.urlTrunc; + }, }, }).mount("#app"); diff --git a/internal/web/index.html b/internal/web/index.html @@ -89,7 +89,7 @@ <h3> <a :href="b.url" target="_blank" rel="noopener noreferrer">{{ b.title || b.url }}</a> </h3> - <p class="url">{{ b.url }}</p> + <p :class="['url', { truncate: b.urlTrunc }]" :title="b.url" @click="toggleURL(b)">{{ b.url }}</p> <p v-if="b.referrer" class="referrer">Referrer: <a :href="b.referrer" target="_blank" rel="noopener noreferrer">{{ b.referrer }}</a></p> <div v-if="b.editing" class="edit-form"> <label>URL <input v-model.trim="b.editUrl" type="url" required /></label> @@ -132,7 +132,7 @@ <div class="results"> <article v-for="b in results" :key="b.id" class="card"> <h3><a :href="b.url" target="_blank" rel="noopener noreferrer">{{ b.title || b.url }}</a></h3> - <p class="url">{{ b.url }}</p> + <p :class="['url', { truncate: b.urlTrunc }]" :title="b.url" @click="toggleURL(b)">{{ b.url }}</p> <p v-if="b.referrer" class="referrer">Referrer: <a :href="b.referrer" target="_blank" rel="noopener noreferrer">{{ b.referrer }}</a></p> <div v-if="b.description" class="desc" v-html="renderMarkdown(b.description)"></div> <p class="action-links"> diff --git a/internal/web/styles.css b/internal/web/styles.css @@ -44,11 +44,15 @@ button.btn { border: none; } .card.private { background: #f6f7f8; } @media (prefers-color-scheme: dark) { .card.private { background: #202225; } } .card h3 { margin: 0 0 6px 0; font-size: 16px; } +/* Allow grid children to shrink so long strings don't expand layout */ +.grid > * { min-width: 0; } +.card { min-width: 0; } .archive-link { display:none; } .icon { display:none; } .edit-form { display: grid; gap: 8px; margin: 8px 0; } .edit-form .actions { display: flex; gap: 8px; } -.url { color: #666; font-size: 12px; margin: 0 0 6px 0; word-break: break-all; } +.url { color: #666; font-size: 12px; margin: 0 0 6px 0; overflow-wrap: anywhere; word-break: break-word; } +.url.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; max-width: 100%; } .referrer { color: #999; font-size: 12px; margin: 0 0 6px 0; word-break: break-all; } .referrer a { color: inherit; } .action-links { margin: 6px 0 0 0; font-size: 13px; color: #555; } diff --git a/internal/web/user.html b/internal/web/user.html @@ -49,7 +49,7 @@ <div class="results"> <article v-for="b in results" :key="b.id" :class="['card', { private: !b.public }]"> <h3><a :href="b.url" target="_blank" rel="noopener noreferrer">{{ b.title || b.url }}</a></h3> - <p class="url">{{ b.url }}</p> + <p :class="['url', { truncate: b.urlTrunc }]" :title="b.url" @click="toggleURL(b)">{{ b.url }}</p> <p v-if="b.referrer" class="referrer">Referrer: <a :href="b.referrer" target="_blank" rel="noopener noreferrer">{{ b.referrer }}</a></p> <div v-if="b.editing" class="edit-form"> <label>URL <input v-model.trim="b.editUrl" type="url" required /></label> @@ -91,7 +91,7 @@ <div class="results"> <article v-for="b in results" :key="b.id" class="card"> <h3><a :href="b.url" target="_blank" rel="noopener noreferrer">{{ b.title || b.url }}</a></h3> - <p class="url">{{ b.url }}</p> + <p :class="['url', { truncate: b.urlTrunc }]" :title="b.url" @click="toggleURL(b)">{{ b.url }}</p> <p v-if="b.referrer" class="referrer">Referrer: <a :href="b.referrer" target="_blank" rel="noopener noreferrer">{{ b.referrer }}</a></p> <div v-if="b.description" class="desc" v-html="renderMarkdown(b.description)"></div> <p class="action-links">