diff --git a/TabsAside.html b/TabsAside.html index d833af7..542fed5 100644 --- a/TabsAside.html +++ b/TabsAside.html @@ -27,21 +27,28 @@

+

+ + +

+
+

v1.0
Developed by Michael Gordeev (@xfox111)

+ + Set current tabs aside -
diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2f54b5c..f3f95d8 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -19,14 +19,24 @@ "message": "Options", "description": "Alternative text for options button in the pane" }, + "closePanel": + { + "message": "Close", + "description": "Alternative text for close panel button" + }, "loadOnRestore": { "message": "Load tabs on restore", "description": "Label for option" }, + "showDeleteDialog": + { + "message": "Show confirmation dialog before deleting an item", + "description": "Label for option" + }, "swapIconAction": { - "message": "Set tabs aside on extension icon click (Alt+P or right-click to open the pane)", + "message": "Set tabs aside on extension icon click (%TOGGLE_SHORTCUT% or right-click to open the pane)", "description": "Label for option" }, "github": @@ -111,47 +121,7 @@ }, "tabs": { - "message": "Tabs", - "description": "Collection tabs counter label" - }, - "ago": - { - "message": "ago", - "description": "Human friendly timestamp part (e.g. 15 hour(s) ago)" - }, - "minutes": - { - "message": "minute(s)", - "description": "Human friendly timestamp part (e.g. 15 minute(s) ago)" - }, - "hours": - { - "message": "hour(s)", - "description": "Human friendly timestamp part (e.g. 15 hour(s) ago)" - }, - "days": - { - "message": "day(s)", - "description": "Human friendly timestamp part (e.g. 15 day(s) ago)" - }, - "weeks": - { - "message": "week(s)", - "description": "Human friendly timestamp part (e.g. 15 week(s) ago)" - }, - "months": - { - "message": "month(s)", - "description": "Human friendly timestamp part (e.g. 15 month(s) ago)" - }, - "years": - { - "message": "years(s)", - "description": "Human friendly timestamp part (e.g. 15 years(s) ago)" - }, - "justNow": - { - "message": "Just now", - "description": "Human friendly timestamp part" + "message": "items", + "description": "Collection tabs counter label (e.g. 8 items)" } } \ No newline at end of file diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index 381f2be..b3ca9a9 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -19,14 +19,24 @@ "message": "Настройки", "description": "Alternative text for options button in the pane" }, + "closePanel": + { + "message": "Закрыть", + "description": "Alternative text for close panel button" + }, "loadOnRestore": { "message": "Загружать вкладки после открытия", "description": "Label for option" }, + "showDeleteDialog": + { + "message": "Показывать окно подтверждения перед удалением элемента", + "description": "Label for option" + }, "swapIconAction": { - "message": "Откладывать вкладки при нажатии на иконку расширения (Alt+P или правая кнопка мыши для открытия панели)", + "message": "Откладывать вкладки при нажатии на иконку расширения (%TOGGLE_SHORTCUT% или правая кнопка мыши для открытия панели)", "description": "Label for option" }, "github": @@ -111,47 +121,7 @@ }, "tabs": { - "message": "Вкладок", - "description": "Collection tabs counter label" - }, - "ago": - { - "message": "назад", - "description": "Human friendly timestamp part (e.g. 15 hour(s) ago)" - }, - "minutes": - { - "message": "минут(ы)", - "description": "Human friendly timestamp part (e.g. 15 minute(s) ago)" - }, - "hours": - { - "message": "час(ов)", - "description": "Human friendly timestamp part (e.g. 15 hour(s) ago)" - }, - "days": - { - "message": "дней", - "description": "Human friendly timestamp part (e.g. 15 day(s) ago)" - }, - "weeks": - { - "message": "недель", - "description": "Human friendly timestamp part (e.g. 15 week(s) ago)" - }, - "months": - { - "message": "месяц(ев)", - "description": "Human friendly timestamp part (e.g. 15 month(s) ago)" - }, - "years": - { - "message": "лет", - "description": "Human friendly timestamp part (e.g. 15 years(s) ago)" - }, - "justNow": - { - "message": "Только что", - "description": "Human friendly timestamp part" + "message": "вкладок", + "description": "Collection tabs counter label (e.g. 8 items)" } } \ No newline at end of file diff --git a/_locales/zh-CN/messages.json b/_locales/zh-CN/messages.json new file mode 100644 index 0000000..cf02a73 --- /dev/null +++ b/_locales/zh-CN/messages.json @@ -0,0 +1,127 @@ +{ + "name": + { + "message": "搁置的标签页", + "description": "Extension name. Displayed in the manifest and pane header" + }, + "description": + { + "message": "为Chromium浏览器提供旧版 Microsoft Edge 中的\"搁置标签页\" 功能", + "description": "Extension description" + }, + "author": + { + "message": "Michael \"XFox\" Gordeev", + "description": "Author name" + }, + "options": + { + "message": "设置", + "description": "Alternative text for options button in the pane" + }, + "closePanel": + { + "message": "关闭", + "description": "Alternative text for close panel button" + }, + "loadOnRestore": + { + "message": "在重新打开时加载页面", + "description": "Label for option" + }, + "showDeleteDialog": + { + "message": "在删除项目之前显示确认对话框", + "description": "Label for option" + }, + "swapIconAction": + { + "message": "点击拓展图标来搁置所有标签页 (按%TOGGLE_SHORTCUT%或右键来打开侧栏)", + "description": "Label for option" + }, + "github": + { + "message": "查看GitHub页面", + "description": "Link title" + }, + "contributors": + { + "message": "项目贡献者", + "description": "Link title" + }, + "feedback": + { + "message": "给我们反馈", + "description": "Link title" + }, + "buyMeACoffee": + { + "message": "给我买杯咖啡!", + "description": "Link title" + }, + "credits": + { + "message": "由Michael 'XFox' Gordeev开发", + "description": "Options menu credits" + }, + "setAside": + { + "message": "搁置当前的所有标签页", + "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" + }, + "nothingSaved": + { + "message": "你目前没有搁置的标签页", + "description": "Placeholder for empty pane" + }, + "removeTab": + { + "message": "从标签页集中移除标签页", + "description": "Button hint on a tab card" + }, + "restoreTabs": + { + "message": "恢复标签页", + "description": "Collection restore action link name" + }, + "more": + { + "message": "更多...", + "description": "Collections' more button title" + }, + "restoreNoRemove": + { + "message": "恢复但不移除标签页", + "description": "Context action item name" + }, + "removeCollection": + { + "message": "移除标签页集", + "description": "Collection remove action name" + }, + "removeCollectionConfirm": + { + "message": "你确定要移除这个标签页集吗?", + "description": "Prompt dialog content on collection deletion" + }, + "removeTabConfirm": + { + "message": "你确定要移除这个标签页吗?", + "description": "Prompt dialog content on one tab deletion" + }, + "togglePaneContext": + { + "message": "打开或关闭侧栏", + "description": "Context action name. Used in extension context menu and manifest shortcuts" + }, + "noTabsToSave": + { + "message": "没有可以搁置的标签页", + "description": "Alert dialog message when there's no tabs to save" + }, + "tabs": + { + "message": "项", + "description": "Collection tabs counter label (e.g. 8 items)" + } +} diff --git a/css/style.css b/css/style.css index 35f0eed..7262e8f 100644 --- a/css/style.css +++ b/css/style.css @@ -18,7 +18,9 @@ right: 0px; top: 0px; bottom: 0px; - overflow: auto; + overflow: hidden; + display: grid; + grid-template-rows: auto 1fr; width: 100%; min-width: 500px; @@ -36,7 +38,7 @@ aside[embedded] { - width: 40% !important; + width: 500px !important; } .tabsAside.pane[opened] @@ -47,37 +49,32 @@ /* Pane header*/ .tabsAside.pane > header { - margin: 20px 40px; + z-index: 1; + padding: 14px 20px 16px 20px; + box-shadow: 0px 0px 5px rgba(0,0,0,.5); + background-color: white; } .tabsAside.pane > header > div { display: grid; - grid-template-columns: 1fr auto; + grid-template-columns: 1fr auto auto; + grid-column-gap: 10px; + margin-bottom: 29px; } .tabsAside.pane > header > div > h1 { - margin: 10px 0px; - font-weight: normal; - font-size: 21pt; - } - - .tabsAside.pane > header > div > button - { - margin: auto; + margin: 0px 5px; + font-weight: 500; + font-size: 15pt; } .tabsAside.pane > header > div > nav { - top: 70px; - right: 40px; + top: 45px; + right: 55px; } - .tabsAside.pane > header nav > div - { - box-shadow: 0px 4px 5px -2px rgba(100, 100, 100, .5); - } - .tabsAside.pane > header nav > p { margin: 10px; @@ -88,25 +85,35 @@ text-decoration: none; } - .iconArrowRight + .saveTabs { - width: 13px; - height: 13px; - display: inline-block; - background-repeat: no-repeat; - background-size: 13px; - background-position: center; - background-image: url("chrome-extension://__MSG_@@extension_id__/icons/arrowRight.svg"); + display: inline-grid; + grid-template-columns: 16px auto; + grid-column-gap: 15px; + + font-weight: 600; } - .tabsAside.pane > header > hr + .iconArrowRight { - border: 1px solid #8a8a8a; + width: 16px; + height: 16px; + display: inline-block; + background-repeat: no-repeat; + background-size: 16px; + background-position: center; + background-image: url("chrome-extension://__MSG_@@extension_id__/icons/arrowRight.svg"); + margin: 2px; } + .tabsAside.pane section + { + overflow: auto; + } + .tabsAside.pane > section > h2 { - margin: 0px 40px; + margin: 20px; font-weight: normal; } @@ -116,11 +123,34 @@ transition: .2s; } + .collectionSet + { + background-color: white; + margin: 10px; + border-radius: 5px; + border: 1px solid #eee; + } + + .collectionSet:hover + { + box-shadow: 0px 0px 5px rgba(0, 0, 0, .25); + } + + .collectionSet .header > * + { + visibility: hidden; + } + + .collectionSet:hover .header > * + { + visibility: visible; + } + .collectionSet > .header { - margin: 0px 20px; + margin: 10px 10px 0px 20px; display: grid; - grid-template-columns: auto 1fr auto auto auto; + grid-template-columns: 1fr auto auto auto; grid-column-gap: 10px; align-items: center; } @@ -128,11 +158,14 @@ .collectionSet > .header > small { color: gray; + visibility: visible !important; } - .collectionSet > .header > span + .collectionSet > .header > h4 { - font-weight: 600; + margin: 0px; + visibility: visible !important; + font-weight: 500; } .collectionSet > .header > a @@ -149,8 +182,7 @@ /* Tabs collection */ .collectionSet > .set { - margin: 0px 0px 0px 20px; - padding: 10px 40px 10px 20px; + padding: 5px 10px; white-space: nowrap; overflow: auto; } @@ -179,14 +211,15 @@ display: inline-grid; grid-template-rows: 1fr auto; - box-shadow: 0px 0px 5px rgba(100, 100, 100, .5); transition: .25s; cursor: pointer; + + border: 1px solid #eee; + border-radius: 5px; } .collectionSet > .set > div:hover { - filter: brightness(120%); - box-shadow: 0px 0px 15px rgba(100, 100, 100, .5); + box-shadow: 0px 0px 5px rgba(100, 100, 100, .5); } .collectionSet > .set > div > div @@ -213,7 +246,7 @@ { width: 20px; height: 20px; - margin: 10px; + margin: 8px; background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon.png"); background-size: 20px; diff --git a/css/style.dark.css b/css/style.dark.css index 214f25f..42fca91 100644 --- a/css/style.dark.css +++ b/css/style.dark.css @@ -9,6 +9,16 @@ color: white; } + .tabsAside[darkmode] .pane header .iconArrowRight + { + filter: invert(); + } + + .tabsAside[darkmode] .pane header + { + background-color: #3b3b3b; + } + .tabsAside[darkmode] .saveTabs > div { filter: invert(); @@ -50,11 +60,18 @@ background: dimgray; } + .tabsAside[darkmode] .pane .collectionSet + { + background-color: #3b3b3b; + border-color: #444; + } + /* Tab style */ .tabsAside[darkmode] .pane .collectionSet > .set > div { background-color: #0c0c0c; background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png"); + border-color: #444; } .tabsAside[darkmode] .pane .collectionSet > .set > div > div diff --git a/css/style.generic.css b/css/style.generic.css index 2ce4f0f..1e5b2d4 100644 --- a/css/style.generic.css +++ b/css/style.generic.css @@ -17,7 +17,7 @@ .tabsAside { - font-family: 'DefaultFont'; + font-family: 'Segoe UI' ,'DefaultFont'; font-size: 14px; user-select: none; } @@ -41,15 +41,15 @@ /* Buttons style */ .tabsAside button { - width: 32px; - height: 32px; + width: 28px; + height: 28px; background-color: transparent; border: none; cursor: pointer; } .tabsAside button:hover { - background-color: #c6c6c6; + background-color: #f2f2f2; } .tabsAside button:active { @@ -62,25 +62,41 @@ user-select: none; position: absolute; - width: 250px; + width: 290px; - box-shadow: 0px 0px 10px black; + box-shadow: 0px 0px 10px rgba(0,0,0,.5); background-color: white; border-radius: 5px; z-index: 10; visibility: hidden; + padding: 4px 0px; } + .tabsAside nav hr + { + border: none; + height: 1px; + background-color: lightgray; + } + .tabsAside nav button { text-align: start; padding: 0px 10px; width: 100%; + + height: 32px; + font-family: 'Segoe UI' ,'DefaultFont'; } + .tabsAside nav button:hover + { + background-color: #eeee; + } + .tabsAside button + nav:active, .tabsAside button:focus + nav { @@ -91,7 +107,7 @@ .btn { background-repeat: no-repeat; - background-size: 15px; + background-size: 12px; background-position: center; } diff --git a/js/aside-script.js b/js/aside-script.js index 499a213..2f74be6 100644 --- a/js/aside-script.js +++ b/js/aside-script.js @@ -71,6 +71,9 @@ function Initialize() } document.querySelector(".tabsAside .saveTabs").onclick = SetTabsAside; + document.querySelector(".tabsAside header .btn.remove").addEventListener("click", () => + chrome.runtime.sendMessage({ command: "togglePane" }) + ); document.querySelector("nav > p > small").textContent = chrome.runtime.getManifest()["version"]; @@ -114,6 +117,26 @@ function Initialize() }) ); + // Deletion confirmation dialog + var showDeleteDialog = document.querySelector("#showDeleteDialog"); + chrome.storage.sync.get( + { "showDeleteDialog": true }, + values => showDeleteDialog.checked = values.showDeleteDialog + ); + chrome.storage.onChanged.addListener((changes, namespace) => + { + if (namespace == 'sync') + for (key in changes) + if (key === 'showDeleteDialog') + showDeleteDialog.checked = changes[key].newValue + }); + showDeleteDialog.addEventListener("click", () => + chrome.storage.sync.set( + { + "showDeleteDialog": showDeleteDialog.checked + }) + ); + document.querySelectorAll(".tabsAside.pane > header nav button").forEach(i => i.onclick = () => { @@ -142,6 +165,9 @@ function UpdateLocale() { document.querySelectorAll("*[loc]").forEach(i => i.textContent = chrome.i18n.getMessage(i.getAttribute("loc"))); document.querySelectorAll("*[loc_alt]").forEach(i => i.title = chrome.i18n.getMessage(i.getAttribute("loc_alt"))); + + var swapActionsLabel = document.querySelector("label[loc=swapIconAction]"); + chrome.commands.getAll((commands) => swapActionsLabel.textContent = swapActionsLabel.textContent.replace("%TOGGLE_SHORTCUT%", commands[2].shortcut)); } function AddCollection(collection) @@ -159,7 +185,7 @@ function AddCollection(collection) "
" + "
" + "" + collection.titles[i] + "" + - "" + + "" + "" + ""; } @@ -167,8 +193,7 @@ function AddCollection(collection) list.innerHTML += "
" + "
" + - "" + chrome.i18n.getMessage("tabs") + ": " + collection.links.length + "" + - "" + GetAgo(collection.timestamp) + "" + + "

" + new Date(collection.timestamp).toDateString() + "

" + "Restore tabs" + "
" + "" + @@ -177,6 +202,7 @@ function AddCollection(collection) "" + "
" + "" + + "" + collection.links.length + " " + chrome.i18n.getMessage("tabs") +"" + "
" + "
" + rawTabs + "
" + @@ -229,61 +255,47 @@ function RestoreTabs(collectionData, removeCollection = true) function RemoveTabs(collectionData) { - if (!confirm(chrome.i18n.getMessage("removeCollectionConfirm"))) - return; + chrome.storage.sync.get({ "showDeleteDialog": true }, values => + { + if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeCollectionConfirm"))) + return; - chrome.runtime.sendMessage( - { - command: "deleteTabs", - collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1 - }, - () => RemoveCollectionElement(collectionData) - ); + chrome.runtime.sendMessage( + { + command: "deleteTabs", + collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1 + }, + () => RemoveCollectionElement(collectionData) + ); + }); } function RemoveOneTab(tabData) { - if (!confirm(chrome.i18n.getMessage("removeTabConfirm"))) - return; + chrome.storage.sync.get({ "showDeleteDialog": true }, values => + { + if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeTabConfirm"))) + return; - chrome.runtime.sendMessage( - { - command: "removeTab", - collectionIndex: Array.prototype.slice.call(tabData.parentElement.parentElement.parentElement.children).indexOf(tabData.parentElement.parentElement) - 1, - tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData) - }, - () => - { - tabData.parentElement.previousElementSibling.children[0].textContent = chrome.i18n.getMessage("tabs") + ": " + (tabData.parentElement.children.length - 1); - if (tabData.parentElement.children.length < 2) + chrome.runtime.sendMessage( { - RemoveElement(tabData.parentElement.parentElement); - if (document.querySelector("tabsAside.pane > section").children.length < 2) - setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250); - } - else - RemoveElement(tabData); - }); -} - -function GetAgo(timestamp) -{ - var minutes = (Date.now() - timestamp) / 60000; - - if (minutes < 1) - return chrome.i18n.getMessage("justNow"); - else if (minutes < 60) - return Math.floor(minutes) + " " + chrome.i18n.getMessage("minutes") + " " + chrome.i18n.getMessage("ago"); - else if (minutes < 24 * 60) - return Math.floor(minutes / 60) + " " + chrome.i18n.getMessage("hours") + " " + chrome.i18n.getMessage("ago"); - else if (minutes < 7 * 24 * 60) - return Math.floor(minutes / 24 / 60) + " " + chrome.i18n.getMessage("days") + " " + chrome.i18n.getMessage("ago"); - else if (minutes < 30 * 24 * 60) - return Math.floor(minutes / 7 / 24 / 60) + " " + chrome.i18n.getMessage("weeks") + " " + chrome.i18n.getMessage("ago"); - else if (minutes < 365 * 24 * 60) - return Math.floor(minutes / 30 / 24 / 60) + " " + chrome.i18n.getMessage("months") + " " + chrome.i18n.getMessage("ago"); - else - return Math.floor(minutes / 365 / 24 / 60) + " " + chrome.i18n.getMessage("years") + " " + chrome.i18n.getMessage("ago"); + command: "removeTab", + collectionIndex: Array.prototype.slice.call(tabData.parentElement.parentElement.parentElement.children).indexOf(tabData.parentElement.parentElement) - 1, + tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData) + }, + () => + { + tabData.parentElement.previousElementSibling.children[0].textContent = chrome.i18n.getMessage("tabs") + ": " + (tabData.parentElement.children.length - 1); + if (tabData.parentElement.children.length < 2) + { + RemoveElement(tabData.parentElement.parentElement); + if (document.querySelector("tabsAside.pane > section").children.length < 2) + setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250); + } + else + RemoveElement(tabData); + }); + }); } function RemoveElement(el) @@ -294,7 +306,7 @@ function RemoveElement(el) function RemoveCollectionElement(el) { - RemoveElement(el); - if (el.parentElement.children.length < 2) + if (el.parentElement.children.length < 3) setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250); + RemoveElement(el); } \ No newline at end of file diff --git a/js/background.js b/js/background.js index 19c6cbf..5a3ec6d 100644 --- a/js/background.js +++ b/js/background.js @@ -116,6 +116,15 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => RemoveTab(message.collectionIndex, message.tabIndex); sendResponse(); break; + case "togglePane": + chrome.tabs.query( + { + active: true, + currentWindow: true + }, + (tabs) => TogglePane(tabs[0]) + ) + break; } }); diff --git a/manifest.json b/manifest.json index 773f2d7..3abdee2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name": "__MSG_name__", - "version": "1.6.1", + "version": "1.7", "manifest_version": 2, "description": "__MSG_description__", "author": "__MSG_author__", @@ -38,7 +38,8 @@ "description": "__MSG_setAside__", "suggested_key": { - "default": "Alt+Left" + "default": "Alt+Left", + "mac": "MacCtrl+T" } }, "toggle-pane": @@ -46,8 +47,9 @@ "description": "__MSG_togglePaneContext__", "suggested_key": { - "default": "Alt+P" + "default": "Alt+P", + "mac": "Command+Shift+P" } } } -} \ No newline at end of file +}