Skip to content

Commit 3a4c97d

Browse files
committed
v1.3.0: Redesigned popup with synth LED toggles, gentle glow theme, popup-aware background handler
1 parent cea8482 commit 3a4c97d

8 files changed

Lines changed: 137 additions & 203 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v1.3.0 (2026-05-30)
4+
5+
- **Redesigned** settings popup: replaced old toggle blocks with two minimalist synth-style LED indicators on a compact 84x36 panel ([popup.html](./popup.html), [popup.js](./popup.js))
6+
- **Refined** overlay brand colors: soft blue `#60a5fa` / green `#4ade80` with multi-layer diffused glow replacing the hard border ring ([overlay.css](./overlay.css#L6-L14))
7+
- **Gentle** selection/active/hover states: layered box-shadow (`0 0 8px` + `0 0 20px`) fading radially for a subtle luminous aura ([overlay.css](./overlay.css#L84-L86))
8+
- **Extended** `getTabsAndCapture` to work from popup context (no `sender.tab` fallback) ([background.js](./background.js#L71-L83))
9+
310
## v1.2.0 (2026-05-17)
411

512
- **Added** `_execute_action` command with `Alt+W` shortcut for settings popup ([manifest.json](./manifest.json#L17-L21))

CHANGELOG.zh-CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# 更新日志
22

3+
## v1.3.0 (2026-05-30)
4+
5+
- **重设计** 设置弹窗:将原有文字开关替换为两个极简合成器风格 LED 指示灯,面板缩小至 84×36 像素 ([popup.html](./popup.html), [popup.js](./popup.js))
6+
- **精炼** overlay 品牌色:柔和蓝 `#60a5fa` / 绿 `#4ade80`,多层扩散辉光取代原先的硬边色圈 ([overlay.css](./overlay.css#L6-L14))
7+
- **柔化** 选中/活跃/悬停状态:层叠 box-shadow(`0 0 8px` + `0 0 20px`)径向衰减,营造柔和光晕 ([overlay.css](./overlay.css#L84-L86))
8+
- **扩展** `getTabsAndCapture`:支持无 `sender.tab` 的 popup 调用回退 ([background.js](./background.js#L71-L83))
9+
310
## v1.2.0 (2026-05-17)
411

512
- **新增** `_execute_action` 命令,绑定 `Alt+W` 快捷键打开设置面板 ([manifest.json](./manifest.json#L17-L21))

background.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* background.js - Service Worker v1.2.0
2+
* background.js - Service Worker v1.3.0
33
* Handles tab capture, caching, message routing
44
*/
55

@@ -47,6 +47,10 @@ const cache = {
4747
del(id) {
4848
this.size -= this.sizeOf(this.data.get(id));
4949
this.data.delete(id);
50+
this.capturing.delete(id);
51+
this.timers.delete(id);
52+
chrome.alarms.clear(`c_${id}`).catch(() => {});
53+
this.save();
5054
}
5155
};
5256

@@ -65,11 +69,17 @@ async function capture(tabId, windowId) {
6569

6670
const router = {
6771
async getTabsAndCapture({ tab }) {
68-
const wid = tab?.windowId;
69-
if (!wid) return { tabs: [] };
72+
let wid = tab?.windowId;
73+
let captureId = tab?.id;
74+
if (!wid) {
75+
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
76+
if (!activeTab) return { tabs: [] };
77+
wid = activeTab.windowId;
78+
captureId = activeTab.id;
79+
}
7080
const [tabs] = await Promise.all([
7181
chrome.tabs.query({ windowId: wid }),
72-
tab?.id ? capture(tab.id, wid) : null
82+
captureId ? capture(captureId, wid) : null
7383
]);
7484
return {
7585
tabs: tabs.map(t => ({
@@ -87,10 +97,7 @@ const router = {
8797
async closeTab({ tabId }) {
8898
try {
8999
await chrome.tabs.remove(tabId);
90-
cache.del(tabId), cache.capturing.delete(tabId);
91-
chrome.alarms.clear(`c_${tabId}`).catch(() => {});
92-
cache.timers.delete(tabId);
93-
cache.save();
100+
cache.del(tabId);
94101
return { ok: true };
95102
} catch (e) { return { ok: false, error: e.message }; }
96103
}
@@ -122,9 +129,7 @@ chrome.tabs.onUpdated.addListener(async (id, { status }, t) => {
122129
});
123130

124131
chrome.tabs.onRemoved.addListener(id => {
125-
cache.del(id), cache.capturing.delete(id), cache.timers.delete(id);
126-
chrome.alarms.clear(`c_${id}`).catch(() => {});
127-
cache.save();
132+
cache.del(id);
128133
});
129134

130135
chrome.commands.onCommand.addListener(cmd => {

content-script.js

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* content-script.js - Vertical Tab Switcher v1.2.0
2+
* content-script.js - Vertical Tab Switcher v1.3.0
33
* Overlay UI with keyboard/mouse navigation and preview
44
*/
55

@@ -25,6 +25,20 @@
2525
});
2626
}
2727

28+
function calcPreviewLayout(ratio) {
29+
const edge = Math.max(32, innerWidth * 0.03);
30+
const gap = 32;
31+
const listW = 280;
32+
const titleH = 28;
33+
const availW = innerWidth - edge * 2 - listW - gap;
34+
const availH = innerHeight - edge * 2 - titleH;
35+
let w = Math.min(availW, availH * ratio);
36+
let h = w / ratio;
37+
if (h > availH) { h = availH; w = h * ratio; }
38+
const listRect = DOM.list.getBoundingClientRect();
39+
return { w, h, titleH, previewX: listRect.right + gap, previewY: (innerHeight - h - titleH) / 2 };
40+
}
41+
2842
const DOM = {
2943
overlay: null, list: null, preview: null,
3044
savedPosition: '', savedTop: '', savedWidth: '', savedScrollY: 0,
@@ -134,25 +148,13 @@
134148
function positionList(previewSize = null) {
135149
if (!DOM.list) return;
136150
if (S.settings.preview && previewSize?.w > 0) {
137-
const edge = Math.max(32, innerWidth * 0.03);
138-
const gap = 32;
139-
const listW = 280;
140-
const titleH = 28;
141-
const availW = innerWidth - edge * 2 - listW - gap;
142-
const availH = innerHeight - edge * 2 - titleH;
143-
const ratio = previewSize.w / previewSize.h;
144-
let w = Math.min(availW, availH * ratio);
145-
let h = w / ratio;
146-
if (h > availH) { h = availH; w = h * ratio; }
147-
const totalW = listW + gap + w;
151+
const p = calcPreviewLayout(previewSize.w / previewSize.h);
152+
const totalW = 280 + 32 + p.w;
148153
const listX = (innerWidth - totalW) / 2;
149154
DOM.list.style.cssText = `position:absolute;left:${listX}px;top:50%;transform:translateY(-50%)`;
150155
} else if (S.settings.preview) {
151-
const edge = Math.max(32, innerWidth * 0.03);
152-
const gap = 32;
153-
const listW = 280;
154156
const previewW = Math.min(innerWidth * 0.35, innerHeight * 0.6);
155-
const listX = (innerWidth - listW - gap - previewW) / 2;
157+
const listX = (innerWidth - 280 - 32 - previewW) / 2;
156158
DOM.list.style.cssText = `position:absolute;left:${listX}px;top:50%;transform:translateY(-50%)`;
157159
} else {
158160
DOM.list.style.cssText = '';
@@ -170,40 +172,17 @@
170172
DOM.preview.classList.remove('vts-visible');
171173
DOM.preview.classList.add('vts-no-thumb');
172174
img.style.display = 'none';
173-
const edge = Math.max(32, innerWidth * 0.03);
174-
const gap = 32;
175-
const listW = 280;
176-
const titleH = 28;
177-
const availW = innerWidth - edge * 2 - listW - gap;
178-
const availH = innerHeight - edge * 2 - titleH;
179-
let w = Math.min(availW, availH * 1.6);
180-
let h = w / 1.6;
181-
if (h > availH) { h = availH; w = h * 1.6; }
182-
const listRect = DOM.list.getBoundingClientRect();
183-
const previewX = listRect.right + gap;
184-
const previewY = (innerHeight - h - titleH) / 2;
185-
Object.assign(DOM.preview.style, { width: w + 'px', height: h + titleH + 'px', left: previewX + 'px', top: previewY + 'px' });
175+
const p = calcPreviewLayout(1.6);
176+
Object.assign(DOM.preview.style, { width: p.w + 'px', height: p.h + p.titleH + 'px', left: p.previewX + 'px', top: p.previewY + 'px' });
186177
DOM.preview.classList.add('vts-visible');
187178
return;
188179
}
189180
DOM.preview.classList.remove('vts-no-thumb');
190181
img.style.display = '';
191182
DOM.preview.querySelector('.vts-preview-title').textContent = tab.title;
192183
img.onload = () => {
193-
const edge = Math.max(32, innerWidth * 0.03);
194-
const gap = 32;
195-
const listW = 280;
196-
const titleH = 28;
197-
const availW = innerWidth - edge * 2 - listW - gap;
198-
const availH = innerHeight - edge * 2 - titleH;
199-
const ratio = img.naturalWidth / img.naturalHeight;
200-
let w = Math.min(availW, availH * ratio);
201-
let h = w / ratio;
202-
if (h > availH) { h = availH; w = h * ratio; }
203-
const listRect = DOM.list.getBoundingClientRect();
204-
const previewX = listRect.right + gap;
205-
const previewY = (innerHeight - h - titleH) / 2;
206-
Object.assign(DOM.preview.style, { width: w + 'px', height: h + titleH + 'px', left: previewX + 'px', top: previewY + 'px' });
184+
const p = calcPreviewLayout(img.naturalWidth / img.naturalHeight);
185+
Object.assign(DOM.preview.style, { width: p.w + 'px', height: p.h + p.titleH + 'px', left: p.previewX + 'px', top: p.previewY + 'px' });
207186
DOM.preview.classList.add('vts-visible');
208187
};
209188
img.src = tab.thumbnail;

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "__MSG_extName__",
4-
"version": "1.2.0",
4+
"version": "1.3.0",
55
"author": "77-223255",
66
"homepage_url": "https://github.com/77-223255/VTS",
77
"minimum_chrome_version": "102",

overlay.css

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
/**
2-
* overlay.css - Vertical Tab Switcher styles v1.2.0
2+
* overlay.css - Vertical Tab Switcher styles v1.3.0
33
*/
44

55
:root {
66
--vts-z: 2147483647;
7-
--vts-sel: rgba(59, 130, 246, 0.85);
8-
--vts-sel-sh: rgba(59, 130, 246, 0.35);
9-
--vts-hover: rgba(59, 130, 246, 0.85);
10-
--vts-hover-sh: rgba(59, 130, 246, 0.35);
11-
--vts-active: rgba(34, 197, 94, 0.85);
12-
--vts-active-sh: rgba(34, 197, 94, 0.35);
7+
--vts-sel: rgba(96, 165, 250, 0.35);
8+
--vts-sel-sh: rgba(96, 165, 250, 0.14);
9+
--vts-sel-sh-outer: rgba(96, 165, 250, 0.05);
10+
--vts-hover: rgba(96, 165, 250, 0.25);
11+
--vts-hover-sh: rgba(96, 165, 250, 0.08);
12+
--vts-active: rgba(74, 222, 128, 0.3);
13+
--vts-active-sh: rgba(74, 222, 128, 0.12);
14+
--vts-active-sh-outer: rgba(74, 222, 128, 0.04);
1315
--vts-bg: rgba(40, 40, 40, 0.85);
1416
--vts-bg-h: rgba(50, 50, 55, 0.92);
1517
--vts-text: #e0e0e0;
@@ -79,9 +81,9 @@
7981
box-sizing: border-box;
8082
}
8183

82-
.vts-tab-item.vts-selected { border-color: var(--vts-sel); box-shadow: 0 0 0 3px var(--vts-sel-sh); }
83-
.vts-tab-item.vts-active-tab:not(.vts-selected) { border-color: var(--vts-active); box-shadow: 0 0 0 3px var(--vts-active-sh); }
84-
.vts-tab-item:hover { border-color: var(--vts-hover); box-shadow: 0 0 0 2px var(--vts-hover-sh); background: var(--vts-bg-h); }
84+
.vts-tab-item.vts-selected { border-color: var(--vts-sel); box-shadow: 0 0 8px 1px var(--vts-sel-sh), 0 0 20px 4px var(--vts-sel-sh-outer); }
85+
.vts-tab-item.vts-active-tab:not(.vts-selected) { border-color: var(--vts-active); box-shadow: 0 0 7px 1px var(--vts-active-sh), 0 0 18px 3px var(--vts-active-sh-outer); }
86+
.vts-tab-item:hover { border-color: var(--vts-hover); box-shadow: 0 0 5px 1px var(--vts-hover-sh); background: var(--vts-bg-h); }
8587

8688
.vts-tab-thumbnail {
8789
width: 100%;
@@ -206,12 +208,14 @@
206208

207209
@media (prefers-color-scheme: light) {
208210
:root {
209-
--vts-sel: rgba(37, 99, 235, 0.85);
210-
--vts-sel-sh: rgba(37, 99, 235, 0.35);
211-
--vts-hover: rgba(37, 99, 235, 0.85);
212-
--vts-hover-sh: rgba(37, 99, 235, 0.35);
213-
--vts-active: rgba(22, 163, 74, 0.85);
214-
--vts-active-sh: rgba(22, 163, 74, 0.35);
211+
--vts-sel: rgba(59, 130, 246, 0.45);
212+
--vts-sel-sh: rgba(59, 130, 246, 0.18);
213+
--vts-sel-sh-outer: rgba(59, 130, 246, 0.06);
214+
--vts-hover: rgba(59, 130, 246, 0.3);
215+
--vts-hover-sh: rgba(59, 130, 246, 0.1);
216+
--vts-active: rgba(34, 197, 94, 0.4);
217+
--vts-active-sh: rgba(34, 197, 94, 0.16);
218+
--vts-active-sh-outer: rgba(34, 197, 94, 0.05);
215219
--vts-bg: rgba(255,255,255,0.9);
216220
--vts-bg-h: rgba(250,250,250,0.95);
217221
--vts-text: #1a1a1a;

0 commit comments

Comments
 (0)