Skip to content

Commit 4d0f930

Browse files
committed
feat: in-loader bookmarking, bug fixes
1 parent 369d75d commit 4d0f930

File tree

4 files changed

+82
-21
lines changed

4 files changed

+82
-21
lines changed

src/components/QuickLinks.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ const QuickLinks = ({ cls, nav = true }) => {
100100
/>
101101
)}
102102
</div>
103-
<div className="mt-3 text-sm font-medium text-center">{link.name}</div>
103+
<div className="mt-3 text-sm font-medium text-center w-full px-1 overflow-hidden whitespace-nowrap text-ellipsis">
104+
{link.name}
105+
</div>
104106
</div>
105107
))}
106108
<div className={`${linkItem} cursor-pointer`} onClick={() => setOpen(true)}>

src/pages/Settings.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ const Settings = () => {
4141
[settings, fq]
4242
);
4343

44-
const showKeywordTip = fq && filtered.length && !filtered.some(s => s.name.toLowerCase().includes(fq)) &&
45-
filtered.some(s => s.keywords.some(kw => kw.toLowerCase().includes(fq)));
44+
const showKeywordTip = !!fq && filtered.length > 0 && !filtered.some(s => s.name.toLowerCase().includes(fq)) &&
45+
filtered.some(s => s.keywords.some(kw => kw.toLowerCase().includes(fq)));
4646

4747
return (
4848
<div className="flex flex-col h-screen">
@@ -62,7 +62,7 @@ const Settings = () => {
6262
</div>
6363

6464
{showKeywordTip && <div className="mt-2 text-xs text-gray-400 text-center px-2">May contain what you're looking for</div>}
65-
{fq && matchCount > 0 && <div className="mt-2 text-xs text-gray-400 text-center px-2">Found {matchCount} matching settings</div>}
65+
{fq && matchCount > 1 && <div className="mt-2 text-xs text-gray-400 text-center px-2">Found {matchCount} matching settings</div>}
6666

6767
<div className="flex flex-col gap-3 mt-5">
6868
{filtered.map(({ name, icon: Icon, items }) => {

src/static/loader.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
Menu,
3939
Plus,
4040
Search,
41-
PanelBottomClose
41+
PanelBottomClose,
42+
Star
4243
} from "lucide";
4344

4445
createIcons({
@@ -50,7 +51,8 @@
5051
Menu,
5152
Plus,
5253
Search,
53-
PanelBottomClose
54+
PanelBottomClose,
55+
Star
5456
},
5557
});
5658
</script>
@@ -169,6 +171,9 @@
169171
<div class="ub group flex flex-1 border-0 rounded-[8px] items-center pl-2 ml-1.5 mr-1.5 h-[calc(100%-5px)]">
170172
<i data-lucide="search" class="w-3.5 h-3.5 mr-2"></i>
171173
<input id="url" class="outline-hidden flex-1 text-[13px] bg-transparent" placeholder="Search or type any URL" onmousedown="this.focus()"/>
174+
<div class="flex items-center justify-center rounded-md w-6 mr-1 has-tooltip" id="bookmark-btn">
175+
<i data-lucide="star" class="w-4" id="bookmark"></i>
176+
</div>
172177
</div>
173178
<div class="has-tooltip w-6 h-6 duration-100 ease-in rounded-md flex justify-center items-center relative" id="tabs-btn">
174179
<i data-lucide="folder" class="w-4"></i>

src/static/scripts/tabs.js

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export const TYPE = {
7171
class TabManager {
7272
constructor(arr) {
7373
const stored = JSON.parse(localStorage.getItem('options')) || {};
74-
74+
7575
Object.assign(this, {
7676
unsupported: CONFIG.unsupported,
7777
filter: CONFIG.filter,
@@ -95,7 +95,7 @@ class TabManager {
9595

9696
const els = ['tabs-cont', 'tab-btn', 'fcn', 'url', 'class-portal']
9797
.reduce((acc, id) => ({ ...acc, [id]: document.getElementById(id) }), {});
98-
98+
9999
Object.assign(this, {
100100
tc: els['tabs-cont'],
101101
ab: els['tab-btn'],
@@ -145,7 +145,7 @@ class TabManager {
145145
ex = (() => {
146146
const endpoints = Object.values(TYPE)
147147
.map(p => {
148-
switch(p) {
148+
switch (p) {
149149
case TYPE.scr: return 'scramjet';
150150
case TYPE.uv: return 'uv/service';
151151
case TYPE.uv1: return 'assignments';
@@ -227,7 +227,7 @@ class TabManager {
227227
if (this.isNewTab(t.url)) {
228228
f.src = t.url;
229229
f.onload = () => {
230-
try { contentObserver.unbind(); contentObserver.bind(); } catch {}
230+
try { contentObserver.unbind(); contentObserver.bind(); } catch { }
231231
};
232232
}
233233
this.ic.appendChild(f);
@@ -254,6 +254,7 @@ class TabManager {
254254
handler.navigate(decodedUrl, this, activeTab, iframe);
255255
if (this.ui) this.ui.value = decodedUrl;
256256
console.log('[info] back(): navigated to', decodedUrl);
257+
this.emitNewFrame();
257258
}
258259
};
259260

@@ -275,6 +276,7 @@ class TabManager {
275276
handler.navigate(decodedUrl, this, activeTab, iframe);
276277
if (this.ui) this.ui.value = decodedUrl;
277278
console.log('[info] forward(): navigated to', decodedUrl);
279+
this.emitNewFrame();
278280
}
279281
};
280282

@@ -305,7 +307,7 @@ class TabManager {
305307
try {
306308
const newUrl = f.contentWindow.location.href;
307309
if (newUrl && newUrl !== t.url && newUrl !== 'about:blank') this.updateTabMeta(t, f, newUrl);
308-
} catch {}
310+
} catch { }
309311
});
310312
};
311313

@@ -332,8 +334,8 @@ class TabManager {
332334
f.style.opacity = 1;
333335
return;
334336
}
335-
} catch {}
336-
337+
} catch { }
338+
337339
const decodedUrl = this.ex(newUrl);
338340
const hist = this.history.get(t.id) || { urls: [decodedUrl], position: 0 };
339341

@@ -365,6 +367,7 @@ class TabManager {
365367
if (t.active && this.ui && t.url !== this.newTabUrl) {
366368
this.ui.value = decodedUrl;
367369
this.showBg(false);
370+
this.emitNewFrame();
368371
}
369372
};
370373

@@ -379,6 +382,7 @@ class TabManager {
379382
this.updateAddBtn();
380383
this.track(t.id);
381384
if (this.ui) this.ui.value = '';
385+
this.emitNewFrame();
382386
};
383387

384388

@@ -397,6 +401,7 @@ class TabManager {
397401
this.tabs[newIdx].active = true;
398402
this.showActive();
399403
if (this.ui) this.ui.value = this.isNewTab(this.tabs[newIdx].url) ? '' : this.ex(this.tabs[newIdx].url);
404+
this.emitNewFrame();
400405
}
401406
this.render();
402407
this.updateAddBtn();
@@ -412,6 +417,23 @@ class TabManager {
412417
const activeTab = this.active();
413418
this.ui.value = activeTab && !this.isNewTab(activeTab.url) ? this.ex(activeTab.url) : '';
414419
}
420+
this.emitNewFrame();
421+
};
422+
423+
returnMeta = () => {
424+
const t = this.active();
425+
if (!t) return { name: '', url: '' };
426+
const url = (t.url && !this.isNewTab(t.url)) ? this.ex(t.url) : '';
427+
return { name: t.title || '', url };
428+
};
429+
430+
emitNewFrame = () => {
431+
const t = this.active();
432+
const meta = this.returnMeta();
433+
const detail = { ...meta, tabId: t?.id };
434+
const ev = new CustomEvent('newFrame', { detail });
435+
try { document.dispatchEvent(ev); } catch (err) { }
436+
try { window.dispatchEvent(ev); } catch (err) { }
415437
};
416438

417439
updateUrl = async (input) => {
@@ -436,7 +458,7 @@ class TabManager {
436458
t.url = this.newTabUrl;
437459
t.title = this.newTabTitle;
438460
if (this.ui) this.ui.value = '';
439-
f.onload = () => { try { contentObserver.unbind(); contentObserver.bind(); } catch {} };
461+
f.onload = () => { try { contentObserver.unbind(); contentObserver.bind(); } catch { } };
440462
this.showActive();
441463
this.render();
442464
return;
@@ -455,6 +477,7 @@ class TabManager {
455477
try { t.title = new URL(url).hostname.replace('www.', ''); } catch { t.title = input; }
456478
this.showActive();
457479
this.render();
480+
if (t.active) this.emitNewFrame();
458481
};
459482

460483
getTabWidth = () => {
@@ -483,25 +506,24 @@ class TabManager {
483506
render = (() => {
484507
const tabTemplate = (t, w, i, op, showClose) => `
485508
<div ${t.justAdded ? 'data-m="bounce-up" data-m-duration="0.2"' : ''}
486-
class="tab-item relative flex items-center rounded-b-none justify-between pl-2.5 pr-1.5 py-[0.28rem] rounded-[6px] cursor-pointer transition-all duration-200 ease-in-out ${
487-
t.active ? `border border-b-0 text-[${op.bodyText || '#8a9bb8'}]` : 'hover:bg-[#cccccc2f]'
488-
} ${i === 0 ? 'ml-0' : '-ml-px'}"
509+
class="tab-item relative flex items-center rounded-b-none justify-between pl-2.5 pr-1.5 py-[0.28rem] rounded-[6px] cursor-pointer transition-all duration-200 ease-in-out ${t.active ? `border border-b-0 text-[${op.bodyText || '#8a9bb8'}]` : 'hover:bg-[#cccccc2f]'
510+
} ${i === 0 ? 'ml-0' : '-ml-px'}"
489511
style="width:${w}px;min-width:${this.minW}px;background-color:${t.active ? op.urlBarBg || '#1d303f' : undefined}"
490512
data-tab-id="${t.id}">
491513
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-globe-icon lucide-globe"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
492514
<span class="text-[12px] font-medium truncate flex-1 mr-2 ml-1.5" title="${this.escapeHTML(t.title)}">${this.escapeHTML(t.title)}</span>
493515
${showClose ? `<button class="close-tab shrink-0 w-4 h-4 rounded-full hover:bg-[#b6bfc748] active:bg-[#d0dbe467] flex items-center justify-center transition-colors" data-tab-id="${t.id}" title="Close ${this.escapeHTML(t.title)}"><svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>` : ''}
494516
</div>`.trim();
495517

496-
return function() {
518+
return function () {
497519
const w = this.getTabWidth();
498520
const op = JSON.parse(localStorage.getItem('options') || '{}');
499521
const showClose = this.tabs.length > 1;
500-
522+
501523
this.tc.innerHTML = this.tabs
502524
.map((t, i) => tabTemplate(t, w, i, op, showClose))
503525
.join('');
504-
526+
505527
this.tabs.forEach(t => delete t.justAdded);
506528
};
507529
})();
@@ -555,7 +577,7 @@ window.addEventListener('load', async () => {
555577
{ path: '/s_sw.js', scope: '/scramjet/' },
556578
{ path: '/uv/sw.js' },
557579
];
558-
580+
559581
for (const sw of sws) {
560582
try {
561583
await navigator.serviceWorker.register(sw.path, sw.scope ? { scope: sw.scope } : undefined);
@@ -600,4 +622,36 @@ window.addEventListener('load', async () => {
600622

601623
(tabManager.options.showTb ?? true) && domMap['tabs-btn']();
602624
Object.entries(domMap).forEach(([id, fn]) => document.getElementById(id)?.addEventListener('click', fn));
625+
626+
document.getElementById("bookmark-btn").addEventListener("click", () => {
627+
const bookmark = document.getElementById("bookmark");
628+
const setBookmark = add => {
629+
const old = JSON.parse(localStorage.getItem('options'));
630+
const metaInfo = tabManager.returnMeta();
631+
const result = {
632+
...old,
633+
quickLinks: add
634+
? [...old.quickLinks, { link: metaInfo.url, icon: "null", name: metaInfo.name }]
635+
: old.quickLinks.filter(q => !(q.link === metaInfo.url && q.name === metaInfo.name))
636+
};
637+
localStorage.setItem('options', JSON.stringify(result));
638+
};
639+
640+
if (bookmark.getAttribute("fill") === "currentColor") {
641+
bookmark.setAttribute("fill", "none");
642+
setBookmark(false);
643+
} else {
644+
bookmark.setAttribute("fill", "currentColor");
645+
setBookmark(true);
646+
}
647+
});
648+
649+
document.addEventListener('newFrame', e => {
650+
const bookmark = document.getElementById("bookmark");
651+
const options = JSON.parse(localStorage.getItem('options')) || { quickLinks: [] };
652+
bookmark.setAttribute(
653+
"fill",
654+
options.quickLinks.some(q => q.link === e.detail.url) ? "currentColor" : "none"
655+
);
656+
});
603657
});

0 commit comments

Comments
 (0)