diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index ffb5902d..0624c84f 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -18,13 +18,13 @@ about: Something is not working ## Environment info OS: -Firefox: -Addon: +Firefox version: +Addon version:
diff --git a/README.md b/README.md index eb923305..ebf60914 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,6 @@ __Switch between tabs__ `alt+Up/Down` + `alt+space` - select tab and activate it Scroll - (optional) -__Open dashboard of panel__ -Right-click on panel's icon - __Expand/Fold parent tab__ Click on favicon of target tab. @@ -96,6 +93,8 @@ Move mouse cursor to panel's icon. ### userChrome.css +> Note: Starting with Firefox 69 you have to enable toolkit.legacyUserProfileCustomizations.stylesheets in about:config. + In 'Profile Directory' `(Menu > Help > Troubleshooting Information > Profile Directory)` create folder `chrome` with file `userChrome.css`: diff --git a/addon/_locales/en/messages.json b/addon/_locales/en/messages.json index d5a4d5ea..1432d3a0 100644 --- a/addon/_locales/en/messages.json +++ b/addon/_locales/en/messages.json @@ -5,5 +5,7 @@ }, "ActionTitle": { "message": "Open Sidebery" }, - "upgrading": { "message": "Upgrading..." } + "upgrading": { "message": "Upgrading..." }, + "unhandled_url": { "message": "Unhandled URL" }, + "copy": { "message": "Copy" } } diff --git a/addon/_locales/ru/messages.json b/addon/_locales/ru/messages.json index b1aab7ed..eaacb2c3 100644 --- a/addon/_locales/ru/messages.json +++ b/addon/_locales/ru/messages.json @@ -3,5 +3,7 @@ "ExtDesc": { "message": "Управление табами и закладками в боковой панели." }, "ActionTitle": { "message": "Открыть Sidebery" }, - "upgrading": { "message": "Обновление..." } + "upgrading": { "message": "Обновление..." }, + "unhandled_url": { "message": "Неподдерживаемый URL" }, + "copy": { "message": "Скопировать" } } diff --git a/addon/actions/panels.js b/addon/actions/panels.js index 1753de0a..1f4ace19 100644 --- a/addon/actions/panels.js +++ b/addon/actions/panels.js @@ -17,7 +17,15 @@ function updatePanels(newPanels) { Actions.updateReqHandlerDebounced() } +function savePanels(panels, delay = 500) { + if (this._savePanelsTimeout) clearTimeout(this._savePanelsTimeout) + this._savePanelsTimeout = setTimeout(() => { + browser.storage.local.set({ panels }) + }, delay) +} + export default { loadPanels, updatePanels, + savePanels, } \ No newline at end of file diff --git a/addon/group/group.html b/addon/group/group.html index 0239a6fd..400afd8b 100644 --- a/addon/group/group.html +++ b/addon/group/group.html @@ -22,6 +22,24 @@ + + + + + + + + + + + + + + + + + +
diff --git a/addon/manifest.json b/addon/manifest.json index 5fdcd3cb..59cfd142 100644 --- a/addon/manifest.json +++ b/addon/manifest.json @@ -8,7 +8,7 @@ }, "author": "mbnuqw", "name": "__MSG_ExtName__", - "version": "3.0.8", + "version": "3.1.0", "default_locale": "en", "description": "__MSG_ExtDesc__", "homepage_url": "https://github.com/mbnuqw/sidebery", diff --git a/addon/settings/settings.html b/addon/settings/settings.html index 6ae1610d..8e58db80 100644 --- a/addon/settings/settings.html +++ b/addon/settings/settings.html @@ -20,6 +20,8 @@ + + @@ -54,6 +56,10 @@ + + + +
{{t('settings.nav_settings_group')}}
+
{{t('settings.nav_settings_panels')}}
{{t('settings.nav_menu_tabs')}}
+
{{t('settings.nav_menu_tabs_panel')}}
{{t('settings.nav_menu_bookmarks')}}
+
{{t('settings.nav_menu_bookmarks_panel')}}
+ + + + + + + +

+
Copy
\ No newline at end of file diff --git a/package.json b/package.json index e57ba8a1..3d1997a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sidebery", - "version": "3.0.8", + "version": "3.1.0", "description": "Manage your tabs and bookmarks in sidebar", "main": "index.js", "scripts": { diff --git a/src/actions/menu.js b/src/actions/menu.js index 60af7b54..623087ec 100644 --- a/src/actions/menu.js +++ b/src/actions/menu.js @@ -4,17 +4,32 @@ import Logs from '../logs' * Load custom context menu */ async function loadCtxMenu() { - let ans = await browser.storage.local.get(['tabsMenu', 'bookmarksMenu']) || {} + let ans = await browser.storage.local.get([ + 'tabsMenu', + 'tabsPanelMenu', + 'bookmarksMenu', + 'bookmarksPanelMenu' + ]) || {} if (ans.tabsMenu && ans.tabsMenu.length) { this.state.tabsMenu = ans.tabsMenu Logs.push('[INFO] Tabs menu loaded') } + if (ans.tabsPanelMenu && ans.tabsPanelMenu.length) { + this.state.tabsPanelMenu = ans.tabsPanelMenu + Logs.push('[INFO] Tabs panel menu loaded') + } + if (ans.bookmarksMenu && ans.bookmarksMenu.length) { this.state.bookmarksMenu = ans.bookmarksMenu Logs.push('[INFO] Bookmarks menu loaded') } + + if (ans.bookmarksPanelMenu && ans.bookmarksPanelMenu.length) { + this.state.bookmarksPanelMenu = ans.bookmarksPanelMenu + Logs.push('[INFO] Bookmarks panel menu loaded') + } } export default { diff --git a/src/components/select-field.vue b/src/components/select-field.vue index e08f9f6c..a32cdfba 100644 --- a/src/components/select-field.vue +++ b/src/components/select-field.vue @@ -3,13 +3,16 @@ :data-inline="inline" :data-inactive="inactive" @mousedown="switchOption") - .label {{t(label)}} - select-input( - :label="optLabel" - :value="value" - :opts="opts" - :noneOpt="noneOpt" - @input="select") + .body + .label {{t(label)}} + select-input( + :label="optLabel" + :value="value" + :opts="opts" + :noneOpt="noneOpt" + :color="color" + :icon="icon" + @input="select") @@ -28,6 +31,8 @@ export default { inline: Boolean, optLabel: String, opts: Array, + color: String, + icon: String, noneOpt: String, }, @@ -43,7 +48,10 @@ export default { if (e.button === 2) i-- if (i >= this.opts.length) i = 0 if (i < 0) i = this.opts.length - 1 - this.$emit('input', this.opts[i]) + + let selected = this.opts[i] + if (selected && selected.value) this.$emit('input', selected.value) + else this.$emit('input', selected) }, select(option) { diff --git a/src/components/select-input.vue b/src/components/select-input.vue index 0ede04cd..e463e09d 100644 --- a/src/components/select-input.vue +++ b/src/components/select-input.vue @@ -1,10 +1,13 @@ @@ -15,6 +18,8 @@ export default { opts: Array, label: String, plurNum: [String, Number], + color: String, + icon: String, noneOpt: { type: String, default: () => 'none' @@ -26,8 +31,14 @@ export default { }, methods: { + isActive(opt) { + if (opt && opt.value !== undefined) return opt.value === this.value + else return this.value === opt + }, + select(option) { - this.$emit('input', option) + if (option && option.value) this.$emit('input', option.value) + else this.$emit('input', option) }, }, } diff --git a/src/components/style-color-field.vue b/src/components/style-color-field.vue index b3670455..c3a41532 100644 --- a/src/components/style-color-field.vue +++ b/src/components/style-color-field.vue @@ -1,5 +1,5 @@ @@ -107,6 +203,8 @@ import State from '../store/state' import Actions from '../actions' import { DEFAULT_TABS_MENU, DEFAULT_BOOKMARKS_MENU } from '../../defaults' +import { DEFAULT_TABS_PANEL_MENU } from '../../defaults' +import { DEFAULT_BOOKMARKS_PANEL_MENU } from '../../defaults' import TextInput from '../../components/text-input' import FooterSection from './footer' @@ -139,9 +237,27 @@ const BOOKMARKS_MENU_OPTS = { 'delete': 'menu.bookmark.delete_bookmark', } +const TABS_PANEL_MENU_OPTS = { + 'muteAllAudibleTabs': 'menu.tabs_panel.mute_all_audible', + 'closeTabsDuplicates': 'menu.tabs_panel.dedup', + 'undoRmTab': 'menu.tab.undo', + 'reloadTabs': 'menu.tabs_panel.reload', + 'discardTabs': 'menu.tabs_panel.discard', + 'collapseInactiveBranches': 'menu.tabs_panel.collapse_inact_branches', + 'closeTabs': 'menu.tabs_panel.close', + 'openPanelConfig': 'menu.common.conf', +} + +const BOOKMARKS_PANEL_MENU_OPTS = { + 'collapseAllFolders': 'menu.bookmark.collapse_all', + 'openPanelConfig': 'menu.common.conf', +} + const SECTIONS = [ 'menu_editor_tabs', + 'menu_editor_tabs_panel', 'menu_editor_bookmarks', + 'menu_editor_bookmarks_panel', ] export default { @@ -155,7 +271,9 @@ export default { selected: '', active: false, tabsOpts: TABS_MENU_OPTS, + tabsPanelOpts: TABS_PANEL_MENU_OPTS, bookmarksOpts: BOOKMARKS_MENU_OPTS, + bookmarksPanelOpts: BOOKMARKS_PANEL_MENU_OPTS, } }, @@ -195,6 +313,41 @@ export default { return all.filter(option => !active.includes(option)) }, + tabsPanelMenu() { + let out = [] + let group = {} + for (let i = 0; i < State.tabsPanelMenu.length; i++) { + let item = State.tabsPanelMenu[i] + if (typeof item === 'string') { + if (group.type !== 'list') { + group = { type: 'list', name: '', options: [], i } + out.push(group) + } + group.options.push(item) + } + if (item instanceof Array) { + if (group.type !== 'sub') { + group = { type: 'sub', name: '', i } + if (item[0] instanceof Object) { + group.name = item[0].name + group.options = item.slice(1) + } else { + group.options = item + } + out.push(group) + group = {} + } + } + } + return out + }, + + disabledTabsPanelMenu() { + const all = Object.keys(TABS_PANEL_MENU_OPTS) + const active = State.tabsPanelMenu.reduce((a, v) => a.concat(v), []) + return all.filter(option => !active.includes(option)) + }, + bookmarksMenu() { let out = [] let group = {} @@ -229,6 +382,41 @@ export default { const active = State.bookmarksMenu.reduce((a, v) => a.concat(v), []) return all.filter(option => !active.includes(option)) }, + + bookmarksPanelMenu() { + let out = [] + let group = {} + for (let i = 0; i < State.bookmarksPanelMenu.length; i++) { + let item = State.bookmarksPanelMenu[i] + if (typeof item === 'string') { + if (group.type !== 'list') { + group = { type: 'list', name: '', options: [], i } + out.push(group) + } + group.options.push(item) + } + if (item instanceof Array) { + if (group.type !== 'sub') { + group = { type: 'sub', name: '', i } + if (item[0] instanceof Object) { + group.name = item[0].name + group.options = item.slice(1) + } else { + group.options = item + } + out.push(group) + group = {} + } + } + } + return out + }, + + disabledBookmarksPanelMenu() { + const all = Object.keys(BOOKMARKS_PANEL_MENU_OPTS) + const active = State.bookmarksPanelMenu.reduce((a, v) => a.concat(v), []) + return all.filter(option => !active.includes(option)) + }, }, mounted() { @@ -285,6 +473,10 @@ export default { State.tabsMenu = JSON.parse(JSON.stringify(DEFAULT_TABS_MENU)) Actions.saveCtxMenu() }, + resetTabsPanelMenu() { + State.tabsPanelMenu = JSON.parse(JSON.stringify(DEFAULT_TABS_PANEL_MENU)) + Actions.saveCtxMenu() + }, /** * Reset bookmarks menu @@ -293,6 +485,10 @@ export default { State.bookmarksMenu = JSON.parse(JSON.stringify(DEFAULT_BOOKMARKS_MENU)) Actions.saveCtxMenu() }, + resetBookmarksPanelMenu() { + State.bookmarksPanelMenu = JSON.parse(JSON.stringify(DEFAULT_BOOKMARKS_PANEL_MENU)) + Actions.saveCtxMenu() + }, /** * Restore option diff --git a/src/sidebar/components/containered-tabs-dashboard.vue b/src/page.settings/components/panel-config.vue similarity index 53% rename from src/sidebar/components/containered-tabs-dashboard.vue rename to src/page.settings/components/panel-config.vue index 179c8254..a389670a 100644 --- a/src/sidebar/components/containered-tabs-dashboard.vue +++ b/src/page.settings/components/panel-config.vue @@ -1,6 +1,9 @@ @@ -153,26 +142,22 @@ import TextInput from '../../components/text-input' import ToggleField from '../../components/toggle-field' import SelectField from '../../components/select-field' -import IconSelectField from '../../components/icon-select-field' -import ColorSelectField from '../../components/color-select-field' +import TextField from '../../components/text-field' import State from '../store/state' import Actions from '../actions' -import ScrollBox from './scroll-box' const HOSTS_RULE_RE = /^.+$/m const PROXY_HOST_RE = /^.{3,65536}$/ const PROXY_PORT_RE = /^\d{2,5}$/ export default { - name: 'ContainerDashboard', + name: 'PanelConfig', components: { TextInput, - ScrollBox, + TextField, ToggleField, SelectField, - IconSelectField, - ColorSelectField, }, props: { @@ -186,36 +171,48 @@ export default { data() { return { iconOpts: [ - 'fingerprint', - 'briefcase', - 'dollar', - 'cart', - 'circle', - 'gift', - 'vacation', - 'food', - 'fruit', - 'pet', - 'tree', - 'chill', - 'fence', + { value: 'fingerprint', icon: 'fingerprint' }, + { value: 'briefcase', icon: 'briefcase' }, + { value: 'dollar', icon: 'dollar' }, + { value: 'cart', icon: 'cart' }, + { value: 'circle', icon: 'circle' }, + { value: 'gift', icon: 'gift' }, + { value: 'vacation', icon: 'vacation' }, + { value: 'food', icon: 'food' }, + { value: 'fruit', icon: 'fruit' }, + { value: 'pet', icon: 'pet' }, + { value: 'tree', icon: 'tree' }, + { value: 'chill', icon: 'chill' }, + { value: 'fence', icon: 'fence' }, ], colorOpts: [ - 'blue', - 'turquoise', - 'green', - 'yellow', - 'orange', - 'red', - 'pink', - 'purple', - 'toolbar', + { value: 'blue', color: 'blue' }, + { value: 'turquoise', color: 'turquoise' }, + { value: 'green', color: 'green' }, + { value: 'yellow', color: 'yellow' }, + { value: 'orange', color: 'orange' }, + { value: 'red', color: 'red' }, + { value: 'pink', color: 'pink' }, + { value: 'purple', color: 'purple' }, + { value: 'toolbar', color: 'toolbar' }, ], proxyOpts: ['http', 'https', 'socks4', 'socks', 'direct'], } }, computed: { + isBookmarks() { + return this.conf.type === 'bookmarks' + }, + + isDefault() { + return this.conf.type === 'default' + }, + + isContainer() { + return this.conf.type === 'ctx' + }, + id() { return this.conf.cookieStoreId || '' }, @@ -289,19 +286,18 @@ export default { }, }, - watch: { - index(index) { - this.init() - if (index === -1) this.$refs.name.focus() - } - }, - mounted() { this.init() - this.$refs.name.focus() }, methods: { + onWheel(e) { + let scrollOffset = this.$el.scrollTop + let maxScrollOffset = this.$el.scrollHeight - this.$el.offsetHeight + if (scrollOffset === 0 && e.deltaY < 0) e.preventDefault() + if (scrollOffset === maxScrollOffset && e.deltaY > 0) e.preventDefault() + }, + onEnter() { this.$emit('close') }, @@ -333,10 +329,7 @@ export default { updateName() { if (!this.name) return - - // Create new container or update - if (!this.id) this.createNew() - else this.updateContainer() + this.updateContainer() }, async updateIcon(icon) { @@ -349,49 +342,6 @@ export default { this.updateContainer() }, - async createNew() { - const details = { - name: this.name, - color: this.color, - icon: this.icon, - } - return await browser.contextualIdentities.create(details) - }, - - dedupTabs() { - if (!this.conf.tabs || this.conf.tabs.length === 0) return - const toClose = [] - this.conf.tabs.map((t, i) => { - for (let j = i + 1; j < this.conf.tabs.length; j++) { - if (this.conf.tabs[j].url === t.url) toClose.push(this.conf.tabs[j].id) - } - }) - browser.tabs.remove(toClose) - this.$emit('close') - }, - - reloadAllTabs() { - if (!this.conf.tabs || this.conf.tabs.length === 0) return - this.conf.tabs.map(t => browser.tabs.reload(t.id)) - this.$emit('close') - }, - - closeAllTabs() { - if (!this.conf.tabs || this.conf.tabs.length === 0) return - Actions.removeTabs(this.conf.tabs.map(t => t.id)) - this.$emit('close') - }, - - async remove() { - if (!this.id) return - browser.contextualIdentities.remove(this.id) - this.$emit('close') - }, - - move(step) { - Actions.movePanel(this.id, step) - }, - async init() { await this.$nextTick() if (this.$refs.name) this.$refs.name.recalcTextHeight() @@ -417,11 +367,10 @@ export default { async togglePanelNoEmpty() { this.conf.noEmpty = !this.conf.noEmpty if (this.conf.noEmpty) { - const panel = State.panelsMap[this.id] + let panel = State.panels.find(p => p.cookieStoreId === this.id) if (panel && panel.tabs && !panel.tabs.length) { await browser.tabs.create({ windowId: State.windowId, - index: panel.startIndex, cookieStoreId: panel.cookieStoreId, active: true, }) @@ -433,7 +382,8 @@ export default { async toggleIncludeHosts() { if (!this.conf.includeHostsActive) { if (!State.permAllUrls) { - Actions.openSettings('all-urls') + window.location.hash = 'all-urls' + State.selectedPanel = null this.switchProxy('direct') return } @@ -458,7 +408,8 @@ export default { async toggleExcludeHosts() { if (!this.conf.excludeHostsActive) { if (!State.permAllUrls) { - Actions.openSettings('all-urls') + window.location.hash = 'all-urls' + State.selectedPanel = null this.switchProxy('direct') return } @@ -484,14 +435,15 @@ export default { // Check permissions if (type !== 'direct') { if (!State.permAllUrls) { - Actions.openSettings('all-urls') + window.location.hash = 'all-urls' + State.selectedPanel = null this.switchProxy('direct') return } } - const panel = State.panelsMap[this.id] - if (!panel || !panel.tabs) return + const panel = State.panels.find(p => p.cookieStoreId === this.id) + if (!panel) return this.conf.proxy = { type, diff --git a/src/page.settings/components/styles-editor.vue b/src/page.settings/components/styles-editor.vue index 8736508e..dfd92022 100644 --- a/src/page.settings/components/styles-editor.vue +++ b/src/page.settings/components/styles-editor.vue @@ -9,77 +9,77 @@ :name="'--bg'" @change="updateCSSVar('bg')" @toggle="toggleCSSVar('bg')") - .separator + color-style-field( v-model="cssVars.title_fg" :label="'styles.title_color'" :name="'--title-fg'" @change="updateCSSVar('title_fg')" @toggle="toggleCSSVar('title_fg')") - .separator + color-style-field( v-model="cssVars.sub_title_fg" :label="'styles.sub_title_color'" :name="'--sub-title-fg'" @change="updateCSSVar('sub_title_fg')" @toggle="toggleCSSVar('sub_title_fg')") - .separator + color-style-field( v-model="cssVars.label_fg" :label="'styles.label_color'" :name="'--label-fg'" @change="updateCSSVar('label_fg')" @toggle="toggleCSSVar('label_fg')") - .separator + color-style-field( v-model="cssVars.label_fg_hover" :label="'styles.label_color_hover'" :name="'--label-fg-hover'" @change="updateCSSVar('label_fg_hover')" @toggle="toggleCSSVar('label_fg_hover')") - .separator + color-style-field( v-model="cssVars.label_fg_active" :label="'styles.label_color_active'" :name="'--label-fg-active'" @change="updateCSSVar('label_fg_active')" @toggle="toggleCSSVar('label_fg_active')") - .separator + color-style-field( v-model="cssVars.info_fg" :label="'styles.info_color'" :name="'--info-fg'" @change="updateCSSVar('info_fg')" @toggle="toggleCSSVar('info_fg')") - .separator + color-style-field( v-model="cssVars.true_fg" :label="'styles.true_color'" :name="'--true-fg'" @change="updateCSSVar('true_fg')" @toggle="toggleCSSVar('true_fg')") - .separator + color-style-field( v-model="cssVars.false_fg" :label="'styles.false_color'" :name="'--false-fg'" @change="updateCSSVar('false_fg')" @toggle="toggleCSSVar('false_fg')") - .separator + color-style-field( v-model="cssVars.active_fg" :label="'styles.active_color'" :name="'--active-fg'" @change="updateCSSVar('active_fg')" @toggle="toggleCSSVar('active_fg')") - .separator + color-style-field( v-model="cssVars.inactive_fg" :label="'styles.inactive_color'" :name="'--inactive-fg'" @change="updateCSSVar('inactive_fg')" @toggle="toggleCSSVar('inactive_fg')") - .separator + color-style-field( v-model="cssVars.favicons_placehoder_bg" :label="'styles.favi_placeholder_color'" @@ -95,35 +95,35 @@ :name="'--btn-bg'" @change="updateCSSVar('btn_bg')" @toggle="toggleCSSVar('btn_bg')") - .separator + color-style-field( v-model="cssVars.btn_bg_hover" :label="'styles.btn_bg_color_hover'" :name="'--btn-bg-hover'" @change="updateCSSVar('btn_bg_hover')" @toggle="toggleCSSVar('btn_bg_hover')") - .separator + color-style-field( v-model="cssVars.btn_bg_active" :label="'styles.btn_bg_color_active'" :name="'--btn-bg-active'" @change="updateCSSVar('btn_bg_active')" @toggle="toggleCSSVar('btn_bg_active')") - .separator + color-style-field( v-model="cssVars.btn_fg" :label="'styles.btn_fg_color'" :name="'--btn-fg'" @change="updateCSSVar('btn_fg')" @toggle="toggleCSSVar('btn_fg')") - .separator + color-style-field( v-model="cssVars.btn_fg_hover" :label="'styles.btn_fg_color_hover'" :name="'--btn-fg-hover'" @change="updateCSSVar('btn_fg_hover')" @toggle="toggleCSSVar('btn_fg_hover')") - .separator + color-style-field( v-model="cssVars.btn_fg_active" :label="'styles.btn_fg_color_active'" @@ -140,7 +140,7 @@ :or="'---'" @change="updateCSSVar('scroll_progress_h')" @toggle="toggleCSSVar('scroll_progress_h')") - .separator + color-style-field( v-model="cssVars.scroll_progress_bg" :label="'styles.scroll_progress_color'" @@ -157,21 +157,21 @@ or="---" @change="updateCSSVar('ctx_menu_font')" @toggle="toggleCSSVar('ctx_menu_font')") - .separator + color-style-field( v-model="cssVars.ctx_menu_bg" :label="'styles.menu_bg_color'" :name="'--ctx-menu-bg'" @change="updateCSSVar('ctx_menu_bg')" @toggle="toggleCSSVar('ctx_menu_bg')") - .separator + color-style-field( v-model="cssVars.ctx_menu_fg" :label="'styles.menu_opt_fg_color'" :name="'--ctx-menu-fg'" @change="updateCSSVar('ctx_menu_fg')" @toggle="toggleCSSVar('ctx_menu_fg')") - .separator + color-style-field( v-model="cssVars.ctx_menu_bg_hover" :label="'styles.menu_opt_fg_color_hover'" @@ -187,7 +187,7 @@ :name="'--nav-btn-fg'" @change="updateCSSVar('nav_btn_fg')" @toggle="toggleCSSVar('nav_btn_fg')") - .separator + style-field( v-model="cssVars.nav_btn_width" :label="'styles.nav_btn_width'" @@ -195,7 +195,7 @@ or="---" @change="updateCSSVar('nav_btn_width')" @toggle="toggleCSSVar('nav_btn_width')") - .separator + style-field( v-model="cssVars.nav_btn_height" :label="'styles.nav_btn_height'" @@ -213,7 +213,7 @@ :or="'---'" @change="updateCSSVar('pinned_dock_overlay_bg')" @toggle="toggleCSSVar('pinned_dock_overlay_bg')") - .separator + style-field( v-model="cssVars.pinned_dock_overlay_shadow" :label="'styles.pinned_dock_overlay_shadow'" @@ -231,7 +231,7 @@ :or="'---'" @change="updateCSSVar('tabs_height')" @toggle="toggleCSSVar('tabs_height')") - .separator + style-field( v-model="cssVars.tabs_indent" :label="'styles.tabs_indent'" @@ -239,7 +239,7 @@ :or="'---'" @change="updateCSSVar('tabs_indent')" @toggle="toggleCSSVar('tabs_indent')") - .separator + style-field( v-model="cssVars.tabs_font" :label="'styles.tabs_font'" @@ -247,7 +247,7 @@ :or="'---'" @change="updateCSSVar('tabs_font')" @toggle="toggleCSSVar('tabs_font')") - .separator + style-field( v-model="cssVars.tabs_count_font" :label="'styles.tabs_count_font'" @@ -255,70 +255,70 @@ :or="'---'" @change="updateCSSVar('tabs_count_font')" @toggle="toggleCSSVar('tabs_count_font')") - .separator + color-style-field( v-model="cssVars.tabs_fg" :label="'styles.tabs_fg_color'" :name="'--tabs-fg'" @change="updateCSSVar('tabs_fg')" @toggle="toggleCSSVar('tabs_fg')") - .separator + color-style-field( v-model="cssVars.tabs_fg_hover" :label="'styles.tabs_fg_color_hover'" :name="'--tabs-fg-hover'" @change="updateCSSVar('tabs_fg_hover')" @toggle="toggleCSSVar('tabs_fg_hover')") - .separator + color-style-field( v-model="cssVars.tabs_fg_active" :label="'styles.tabs_fg_color_active'" :name="'--tabs-fg-active'" @change="updateCSSVar('tabs_fg_active')" @toggle="toggleCSSVar('tabs_fg_active')") - .separator + color-style-field( v-model="cssVars.tabs_bg_hover" :label="'styles.tabs_bg_color_hover'" :name="'--tabs-bg-hover'" @change="updateCSSVar('tabs_bg_hover')" @toggle="toggleCSSVar('tabs_bg_hover')") - .separator + color-style-field( v-model="cssVars.tabs_bg_active" :label="'styles.tabs_bg_color_active'" :name="'--tabs-bg-active'" @change="updateCSSVar('tabs_bg_active')" @toggle="toggleCSSVar('tabs_bg_active')") - .separator + color-style-field( v-model="cssVars.tabs_activated_bg" :label="'styles.tabs_active_bg_color'" :name="'--tabs-activated-bg'" @change="updateCSSVar('tabs_activated_bg')" @toggle="toggleCSSVar('tabs_activated_bg')") - .separator + color-style-field( v-model="cssVars.tabs_activated_fg" :label="'styles.tabs_active_fg_color'" :name="'--tabs-activated-fg'" @change="updateCSSVar('tabs_activated_fg')" @toggle="toggleCSSVar('tabs_activated_fg')") - .separator + color-style-field( v-model="cssVars.tabs_selected_bg" :label="'styles.tabs_selected_bg_color'" :name="'--tabs-selected-bg'" @change="updateCSSVar('tabs_selected_bg')" @toggle="toggleCSSVar('tabs_selected_bg')") - .separator + color-style-field( v-model="cssVars.tabs_selected_fg" :label="'styles.tabs_selected_fg_color'" :name="'--tabs-selected-fg'" @change="updateCSSVar('tabs_selected_fg')" @toggle="toggleCSSVar('tabs_selected_fg')") - .separator + style-field( v-model="cssVars.tabs_border" :label="'styles.tabs_border'" @@ -326,7 +326,7 @@ :or="'---'" @change="updateCSSVar('tabs_border')" @toggle="toggleCSSVar('tabs_border')") - .separator + style-field( v-model="cssVars.tabs_activated_border" :label="'styles.tabs_activated_border'" @@ -334,7 +334,7 @@ :or="'---'" @change="updateCSSVar('tabs_activated_border')" @toggle="toggleCSSVar('tabs_activated_border')") - .separator + style-field( v-model="cssVars.tabs_selected_border" :label="'styles.tabs_selected_border'" @@ -342,7 +342,7 @@ :or="'---'" @change="updateCSSVar('tabs_selected_border')" @toggle="toggleCSSVar('tabs_selected_border')") - .separator + style-field( v-model="cssVars.tabs_shadow" :label="'styles.tabs_shadow'" @@ -350,7 +350,7 @@ :or="'---'" @change="updateCSSVar('tabs_shadow')" @toggle="toggleCSSVar('tabs_shadow')") - .separator + style-field( v-model="cssVars.tabs_activated_shadow" :label="'styles.tabs_activated_shadow'" @@ -358,7 +358,7 @@ :or="'---'" @change="updateCSSVar('tabs_activated_shadow')" @toggle="toggleCSSVar('tabs_activated_shadow')") - .separator + style-field( v-model="cssVars.tabs_selected_shadow" :label="'styles.tabs_selected_shadow'" @@ -376,7 +376,7 @@ :or="'---'" @change="updateCSSVar('bookmarks_bookmark_height')" @toggle="toggleCSSVar('bookmarks_bookmark_height')") - .separator + style-field( v-model="cssVars.bookmarks_folder_height" :label="'styles.bookmarks_folder_height'" @@ -384,7 +384,7 @@ :or="'---'" @change="updateCSSVar('bookmarks_folder_height')" @toggle="toggleCSSVar('bookmarks_folder_height')") - .separator + style-field( v-model="cssVars.bookmarks_separator_height" :label="'styles.bookmarks_separator_height'" @@ -392,7 +392,7 @@ :or="'---'" @change="updateCSSVar('bookmarks_separator_height')" @toggle="toggleCSSVar('bookmarks_separator_height')") - .separator + style-field( v-model="cssVars.bookmarks_bookmark_font" :label="'styles.bookmarks_bookmark_font'" @@ -400,7 +400,7 @@ :or="'---'" @change="updateCSSVar('bookmarks_bookmark_font')" @toggle="toggleCSSVar('bookmarks_bookmark_font')") - .separator + style-field( v-model="cssVars.bookmarks_folder_font" :label="'styles.bookmarks_folder_font'" @@ -408,91 +408,91 @@ :or="'---'" @change="updateCSSVar('bookmarks_folder_font')" @toggle="toggleCSSVar('bookmarks_folder_font')") - .separator + color-style-field( v-model="cssVars.bookmarks_node_title_fg" :label="'styles.bookmarks_fg_color'" :name="'--bookmarks-node-title-fg'" @change="updateCSSVar('bookmarks_node_title_fg')" @toggle="toggleCSSVar('bookmarks_node_title_fg')") - .separator + color-style-field( v-model="cssVars.bookmarks_node_title_fg_hover" :label="'styles.bookmarks_fg_color_hover'" :name="'--bookmarks-node-title-fg-hover'" @change="updateCSSVar('bookmarks_node_title_fg_hover')" @toggle="toggleCSSVar('bookmarks_node_title_fg_hover')") - .separator + color-style-field( v-model="cssVars.bookmarks_node_title_fg_active" :label="'styles.bookmarks_fg_color_active'" :name="'--bookmarks-node-title-fg-active'" @change="updateCSSVar('bookmarks_node_title_fg_active')" @toggle="toggleCSSVar('bookmarks_node_title_fg_active')") - .separator + color-style-field( v-model="cssVars.bookmarks_node_bg_hover" :label="'styles.bookmarks_bg_color_hover'" :name="'--bookmarks-node-bg-hover'" @change="updateCSSVar('bookmarks_node_bg_hover')" @toggle="toggleCSSVar('bookmarks_node_bg_hover')") - .separator + color-style-field( v-model="cssVars.bookmarks_node_bg_active" :label="'styles.bookmarks_bg_color_active'" :name="'--bookmarks-node-bg-active'" @change="updateCSSVar('bookmarks_node_bg_active')" @toggle="toggleCSSVar('bookmarks_node_bg_active')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_closed_fg" :label="'styles.bookmarks_closed_dir_fg_color'" :name="'--bookmarks-folder-closed-fg'" @change="updateCSSVar('bookmarks_folder_closed_fg')" @toggle="toggleCSSVar('bookmarks_folder_closed_fg')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_closed_fg_hover" :label="'styles.bookmarks_closed_dir_fg_color_hover'" :name="'--bookmarks-folder-closed-fg-hover'" @change="updateCSSVar('bookmarks_folder_closed_fg_hover')" @toggle="toggleCSSVar('bookmarks_folder_closed_fg_hover')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_closed_fg_active" :label="'styles.bookmarks_closed_dir_fg_color_active'" :name="'--bookmarks-folder-closed-fg-active'" @change="updateCSSVar('bookmarks_folder_closed_fg_active')" @toggle="toggleCSSVar('bookmarks_folder_closed_fg_active')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_open_fg" :label="'styles.bookmarks_open_dir_fg_color'" :name="'--bookmarks-folder-expanded-fg'" @change="updateCSSVar('bookmarks_folder_open_fg')" @toggle="toggleCSSVar('bookmarks_folder_open_fg')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_open_fg_hover" :label="'styles.bookmarks_open_dir_fg_color_hover'" :name="'--bookmarks-folder-expanded-fg-hover'" @change="updateCSSVar('bookmarks_folder_open_fg_hover')" @toggle="toggleCSSVar('bookmarks_folder_open_fg_hover')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_open_fg_active" :label="'styles.bookmarks_open_dir_fg_color_active'" :name="'--bookmarks-folder-expanded-fg-active'" @change="updateCSSVar('bookmarks_folder_open_fg_active')" @toggle="toggleCSSVar('bookmarks_folder_open_fg_active')") - .separator + color-style-field( v-model="cssVars.bookmarks_folder_empty_fg" :label="'styles.bookmarks_empty_dir_fg_color'" :name="'--bookmarks-folder-empty-fg'" @change="updateCSSVar('bookmarks_folder_empty_fg')" @toggle="toggleCSSVar('bookmarks_folder_empty_fg')") - .separator + color-style-field( v-model="cssVars.bookmarks_open_bookmark_fg" :label="'styles.bookmarks_open_fg_color'" diff --git a/src/page.settings/settings.js b/src/page.settings/settings.js index 39f3de68..71158855 100644 --- a/src/page.settings/settings.js +++ b/src/page.settings/settings.js @@ -50,6 +50,7 @@ export default new Vue({ Actions.loadCtxMenu() await Actions.loadSettings() Actions.loadKeybindings() + Actions.loadPanels() }, methods: { diff --git a/src/page.settings/settings.vue b/src/page.settings/settings.vue index 85c47d54..f774e027 100644 --- a/src/page.settings/settings.vue +++ b/src/page.settings/settings.vue @@ -4,7 +4,7 @@ @scroll.passive="onScroll") section(ref="settings_general") h2 {{t('settings.general_title')}} - ToggleField( + ToggleField.-first.-last( label="settings.native_scrollbars" :inline="true" :value="$store.state.nativeScrollbars" @@ -12,12 +12,11 @@ section(ref="settings_menu") h2 {{t('settings.ctx_menu_title')}} - ToggleField( + ToggleField.-first( label="settings.ctx_menu_native" :inline="true" :value="$store.state.ctxMenuNative" @input="setOpt('ctxMenuNative', $event)") - .separator select-field( label="settings.autoHide_ctx_menu" optLabel="settings.autoHide_ctx_menu_" @@ -25,13 +24,11 @@ :value="$store.state.autoHideCtxMenu" :opts="$store.state.autoHideCtxMenuOpts" @input="setOpt('autoHideCtxMenu', $event)") - .separator - ToggleField( + ToggleField.-last( label="settings.ctx_menu_render_inact" :inline="true" :value="$store.state.ctxMenuRenderInact" @input="setOpt('ctxMenuRenderInact', $event)") - .separator .ctrls .btn(@click="switchView('menu_editor')") {{t('settings.ctx_menu_editor')}} @@ -42,39 +39,28 @@ :inline="true" :value="$store.state.navBarInline" @input="setOpt('navBarInline', $event)") - .separator toggle-field( label="settings.hide_settings_btn" :inline="true" :value="$store.state.hideSettingsBtn" @input="setOpt('hideSettingsBtn', $event)") - .separator - toggle-field( - label="settings.hide_add_btn" - :inline="true" - :value="$store.state.hideAddBtn" - @input="setOpt('hideAddBtn', $event)") - .separator toggle-field( label="settings.nav_btn_count" :inline="true" :value="$store.state.navBtnCount" @input="setOpt('navBtnCount', $event)") - .separator toggle-field( label="settings.hide_empty_panels" :inline="true" :value="$store.state.hideEmptyPanels" @input="setOpt('hideEmptyPanels', $event)") - .separator select-field( label="settings.nav_mid_click" optLabel="settings.nav_mid_click_" :value="$store.state.navMidClickAction" :opts="$store.state.navMidClickActionOpts" @input="setOpt('navMidClickAction', $event)") - .separator - toggle-field( + toggle-field.-last( label="settings.nav_switch_panels_wheel" :inline="true" :value="$store.state.navSwitchPanelsWheel" @@ -82,40 +68,72 @@ section(ref="settings_group") h2 {{t('settings.group_title')}} - .separator - select-field( + select-field.-last( label="settings.group_layout" optLabel="settings.group_layout_" :value="$store.state.groupLayout" :opts="$store.state.groupLayoutOpts" @input="setOpt('groupLayout', $event)") + section(ref="settings_panels") + h2 {{t('settings.panels_title')}} + transition-group(name="panel" tag="div"): .panel-card( + v-for="(panel, i) in $store.state.panels" + :key="panel.cookieStoreId || panel.type" + :data-color="panel.color" + :data-first="i === 0" + :data-last="i === $store.state.panels.length - 1") + .panel-card-body(@click="$store.state.selectedPanel = panel") + .panel-card-icon: svg: use(:xlink:href="'#' + panel.icon") + .panel-card-name {{panel.name}} + .panel-card-ctrls + .panel-card-ctrl.-down( + :data-inactive="i === $store.state.panels.length - 1" + @click="movePanel(panel, 1)") + svg: use(xlink:href="#icon_expand") + .panel-card-ctrl.-up( + :data-inactive="i === 0" + @click="movePanel(panel, -1)") + svg: use(xlink:href="#icon_expand") + .panel-card-ctrl.-rm( + :data-inactive="panel.type === 'bookmarks' || panel.type === 'default'" + @click="removePanel(panel)") + svg: use(xlink:href="#icon_delete") + .ctrls: .btn(@click="createPanel") Create panel + transition(name="panel-config") + .panel-config-layer( + v-if="$store.state.selectedPanel" + @click="$store.state.selectedPanel = null") + .panel-config-box(@click.stop="") + panel-config.dashboard(:conf="$store.state.selectedPanel") + section(ref="settings_tabs") h2 {{t('settings.tabs_title')}} + toggle-field( + label="settings.activate_on_mouseup" + :inline="true" + :value="$store.state.activateOnMouseUp" + @input="setOpt('activateOnMouseUp', $event)") toggle-field( label="settings.activate_last_tab_on_panel_switching" :inline="true" :value="$store.state.activateLastTabOnPanelSwitching" @input="setOpt('activateLastTabOnPanelSwitching', $event)") - .separator toggle-field( label="settings.skip_empty_panels" :inline="true" :value="$store.state.skipEmptyPanels" @input="setOpt('skipEmptyPanels', $event)") - .separator toggle-field( label="settings.show_tab_rm_btn" :inline="true" :value="$store.state.showTabRmBtn" @input="setOpt('showTabRmBtn', $event)") - .separator toggle-field( label="settings.hide_inactive_panel_tabs" :inline="true" :value="$store.state.hideInact" @input="toggleHideInact") - .separator select-field( label="settings.activate_after_closing" optLabel="settings.activate_after_closing_" @@ -123,7 +141,6 @@ :opts="$store.state.activateAfterClosingOpts" @input="setOpt('activateAfterClosing', $event)") .sub-fields - .separator select-field( label="settings.activate_after_closing_prev_rule" optLabel="settings.activate_after_closing_rule_" @@ -131,8 +148,7 @@ :inactive="!activateAfterClosingNextOrPrev" :opts="$store.state.activateAfterClosingPrevRuleOpts" @input="setOpt('activateAfterClosingPrevRule', $event)") - .separator - select-field( + select-field.-last( label="settings.activate_after_closing_next_rule" optLabel="settings.activate_after_closing_rule_" :value="$store.state.activateAfterClosingNextRule" @@ -148,8 +164,7 @@ :value="$store.state.pinnedTabsPosition" :opts="$store.state.pinnedTabsPositionOpts" @input="setOpt('pinnedTabsPosition', $event)") - .separator - toggle-field( + toggle-field.-last( label="settings.pinned_tabs_list" :inline="true" :inactive="$store.state.pinnedTabsPosition !== 'panel'" @@ -163,14 +178,12 @@ :inline="true" :value="$store.state.tabsTree" @input="setOpt('tabsTree', $event)") - .separator toggle-field( label="settings.group_on_open_layout" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.groupOnOpen" @input="setOpt('groupOnOpen', $event)") - .separator select-field( label="settings.tabs_tree_limit" optLabel="settings.tabs_tree_limit_" @@ -178,28 +191,24 @@ :value="$store.state.tabsTreeLimit" :opts="$store.state.tabsTreeLimitOpts" @input="setOpt('tabsTreeLimit', $event)") - .separator toggle-field( label="settings.hide_folded_tabs" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.hideFoldedTabs" @input="toggleHideFoldedTabs") - .separator toggle-field( label="settings.auto_fold_tabs" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.autoFoldTabs" @input="setOpt('autoFoldTabs', $event)") - .separator toggle-field( label="settings.auto_exp_tabs" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.autoExpandTabs" @input="setOpt('autoExpandTabs', $event)") - .separator select-field( label="settings.rm_child_tabs" optLabel="settings.rm_child_tabs_" @@ -207,21 +216,18 @@ :value="$store.state.rmChildTabs" :opts="$store.state.rmChildTabsOpts" @input="setOpt('rmChildTabs', $event)") - .separator toggle-field( label="settings.tabs_child_count" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.tabsChildCount" @input="setOpt('tabsChildCount', $event)") - .separator toggle-field( label="settings.tabs_lvl_dots" :inline="true" :inactive="!$store.state.tabsTree" :value="$store.state.tabsLvlDots" @input="setOpt('tabsLvlDots', $event)") - .separator toggle-field( label="settings.discard_folded" :inline="true" @@ -229,8 +235,7 @@ :value="$store.state.discardFolded" @input="setOpt('discardFolded', $event)") .sub-fields - .separator - num-field( + num-field.-last( label="settings.discard_folded_delay" unitLabel="settings.discard_folded_delay_" :inactive="!$store.state.tabsTree || !$store.state.discardFolded" @@ -247,35 +252,30 @@ :inline="true" :value="$store.state.bookmarksPanel" @input="setOpt('bookmarksPanel', $event)") - .separator toggle-field( label="settings.open_bookmark_new_tab" :inline="true" :inactive="!$store.state.bookmarksPanel" :value="$store.state.openBookmarkNewTab" @input="setOpt('openBookmarkNewTab', $event)") - .separator toggle-field( label="settings.auto_close_bookmarks" :inline="true" :inactive="!$store.state.bookmarksPanel" :value="$store.state.autoCloseBookmarks" @input="setOpt('autoCloseBookmarks', $event)") - .separator toggle-field( label="settings.auto_rm_other" :inline="true" :inactive="!$store.state.bookmarksPanel" :value="$store.state.autoRemoveOther" @input="setOpt('autoRemoveOther', $event)") - .separator toggle-field( label="settings.show_bookmark_len" :inline="true" :inactive="!$store.state.bookmarksPanel" :value="$store.state.showBookmarkLen" @input="setOpt('showBookmarkLen', $event)") - .separator toggle-field( label="settings.highlight_open_bookmarks" :inline="true" @@ -283,8 +283,7 @@ :value="$store.state.highlightOpenBookmarks" @input="setOpt('highlightOpenBookmarks', $event)") .sub-fields - .separator - toggle-field( + toggle-field.-last( label="settings.activate_open_bookmark_tab" :inline="true" :inactive="!$store.state.bookmarksPanel || !$store.state.highlightOpenBookmarks" @@ -299,33 +298,28 @@ :value="$store.state.fontSize" :opts="$store.state.fontSizeOpts" @input="setOpt('fontSize', $event)") - .separator toggle-field( label="settings.animations" :inline="true" :value="$store.state.animations" @input="setOpt('animations', $event)") - .separator toggle-field( label="settings.bg_noise" :inline="true" :value="$store.state.bgNoise" @input="setOpt('bgNoise', $event)") - .separator select-field( label="settings.theme" optLabel="settings.theme_" :value="$store.state.theme" :opts="$store.state.themeOpts" @input="setOpt('theme', $event)") - .separator - select-field( + select-field.-last( label="settings.switch_style" optLabel="settings.style_" :value="$store.state.style" :opts="$store.state.styleOpts" @input="setOpt('style', $event)") - .separator .ctrls .btn(@click="switchView('styles_editor')") {{t('settings.edit_styles')}} @@ -336,7 +330,6 @@ :inline="true" :value="$store.state.hScrollThroughPanels" @input="setOpt('hScrollThroughPanels', $event)") - .separator select-field( label="settings.scroll_through_tabs" optLabel="settings.scroll_through_tabs_" @@ -344,49 +337,42 @@ :opts="$store.state.scrollThroughTabsOpts" @input="setOpt('scrollThroughTabs', $event)") .sub-fields - .separator toggle-field( label="settings.scroll_through_visible_tabs" :inline="true" :value="$store.state.scrollThroughVisibleTabs" :inactive="!$store.state.tabsTree || $store.state.scrollThroughTabs === 'none'" @input="setOpt('scrollThroughVisibleTabs', $event)") - .separator toggle-field( label="settings.scroll_through_tabs_except_overflow" :inline="true" :value="$store.state.scrollThroughTabsExceptOverflow" :inactive="$store.state.scrollThroughTabs === 'none'" @input="setOpt('scrollThroughTabsExceptOverflow', $event)") - .separator select-field( label="settings.tab_double_click" optLabel="settings.tab_action_" :value="$store.state.tabDoubleClick" :opts="$store.state.tabDoubleClickOpts" @input="setOpt('tabDoubleClick', $event)") - .separator select-field( label="settings.tab_long_left_click" optLabel="settings.tab_action_" :value="$store.state.tabLongLeftClick" :opts="$store.state.tabLongLeftClickOpts" @input="setOpt('tabLongLeftClick', $event)") - .separator select-field( label="settings.tab_long_right_click" optLabel="settings.tab_action_" :value="$store.state.tabLongRightClick" :opts="$store.state.tabLongRightClickOpts" @input="setOpt('tabLongRightClick', $event)") - .separator select-field( label="settings.tabs_panel_left_click_action" optLabel="settings.tabs_panel_action_" :value="$store.state.tabsPanelLeftClickAction" :opts="$store.state.tabsPanelLeftClickActionOpts" @input="setOpt('tabsPanelLeftClickAction', $event)") - .separator select-field( label="settings.tabs_panel_double_click_action" optLabel="settings.tabs_panel_action_" @@ -394,8 +380,7 @@ :value="$store.state.tabsPanelDoubleClickAction" :opts="$store.state.tabsPanelDoubleClickActionOpts" @input="setOpt('tabsPanelDoubleClickAction', $event)") - .separator - select-field( + select-field.-last( label="settings.tabs_panel_right_click_action" optLabel="settings.tabs_panel_action_" :value="$store.state.tabsPanelRightClickAction" @@ -404,20 +389,19 @@ section(ref="settings_keybindings") h2 {{t('settings.kb_title')}} - .hm(v-for="(k, i) in $store.state.keybindings", :key="k.name") - .keybinding( - :is-focused="k.focus" - @click="changeKeybinding(k, i)") - .label {{t('settings.' + k.description)}} - .value {{normalizeShortcut(k.shortcut)}} - input( - type="text" - ref="keybindingInputs" - tabindex="-1" - @blur="onKBBlur(k, i)" - @keydown.prevent.stop="onKBKey($event, k, i)" - @keyup.prevent.stop="onKBKeyUp($event, k, i)") - .separator + .keybinding( + v-for="(k, i) in $store.state.keybindings", :key="k.name" + :is-focused="k.focus" + @click="changeKeybinding(k, i)") + .label {{t('settings.' + k.description)}} + .value {{normalizeShortcut(k.shortcut)}} + input( + type="text" + ref="keybindingInputs" + tabindex="-1" + @blur="onKBBlur(k, i)" + @keydown.prevent.stop="onKBKey($event, k, i)" + @keyup.prevent.stop="onKBKeyUp($event, k, i)") .ctrls: .btn(@click="resetKeybindings") {{t('settings.reset_kb')}} section(ref="settings_permissions") @@ -431,10 +415,8 @@ label="settings.all_urls_label" :inline="true" :value="$store.state.permAllUrls" + :note="t('settings.all_urls_info')" @input="togglePermAllUrls") - .box: .info {{t('settings.all_urls_info')}} - - .separator .permission( ref="tab_hide" @@ -444,8 +426,8 @@ label="settings.tab_hide_label" :inline="true" :value="$store.state.permTabHide" + :note="t('settings.tab_hide_info')" @input="togglePermTabHide") - .box: .info {{t('settings.tab_hide_info')}} section(ref="settings_snapshots") h2 {{t('settings.snapshots_title')}} @@ -457,7 +439,6 @@ :unit="$store.state.snapIntervalUnit" :unitOpts="$store.state.snapIntervalUnitOpts" @input="setOpt('snapInterval', $event[0]), setOpt('snapIntervalUnit', $event[1])") - .separator num-field( label="settings.snap_limit" unitLabel="settings.snap_limit_" @@ -466,7 +447,6 @@ :unit="$store.state.snapLimitUnit" :unitOpts="$store.state.snapLimitUnitOpts" @input="setOpt('snapLimit', $event[0]), setOpt('snapLimitUnit', $event[1])") - .separator .ctrls .btn(@click="switchView('snapshots')") {{t('settings.snapshots_view_label')}} @@ -478,6 +458,8 @@ .size ~{{info.sizeStr}} .del-btn(@click="deleteStoredData(info.name)") {{t('settings.storage_delete_prop')}} .open-btn(@click="openStoredData(info.name)") {{t('settings.storage_open_prop')}} + .ctrls + .btn(@click="calcStorageInfo") {{t('settings.update_storage_info')}} section(ref="settings_help") h2 {{t('settings.help_title')}} @@ -495,8 +477,6 @@ href="https://github.com/mbnuqw/sidebery/issues/new/choose") {{t('settings.repo_bug')}} .btn.-warn(@click="resetSettings") {{t('settings.reset_settings')}} - .separator - .ctrls .info(v-if="$store.state.osInfo") OS: {{$store.state.osInfo.os}} .info(v-if="$store.state.ffInfo") Firefox: {{$store.state.ffInfo.version}} @@ -519,12 +499,13 @@ import Utils from '../utils' import { translate } from '../mixins/dict' import { DEFAULT_SETTINGS } from '../defaults' +import { DEFAULT_CTX_TABS_PANEL } from '../defaults' import State from './store/state' import Actions from './actions' import ToggleField from '../components/toggle-field' import SelectField from '../components/select-field' import NumField from '../components/num-field' -import InfoField from '../components/info-field' +import PanelConfig from './components/panel-config' import FooterSection from './components/footer' const VALID_SHORTCUT = /^((Ctrl|Alt|Command|MacCtrl)\+)((Shift|Alt)\+)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right|F\d\d?)$|^((Ctrl|Alt|Command|MacCtrl)\+)?((Shift|Alt)\+)?(F\d\d?)$/ @@ -534,6 +515,7 @@ const SECTIONS = [ 'settings_menu', 'settings_nav', 'settings_group', + 'settings_panels', 'settings_tabs', 'settings_pinned_tabs', 'settings_tabs_tree', @@ -552,8 +534,8 @@ export default { ToggleField, SelectField, NumField, - InfoField, FooterSection, + PanelConfig, }, data() { @@ -827,8 +809,10 @@ export default { * Reset settings */ resetSettings() { - Actions.resetSettings() - Actions.saveSettings() + if (window.confirm(translate('settings.reset_confirm'))) { + Actions.resetSettings() + Actions.saveSettings() + } }, /** @@ -1085,6 +1069,56 @@ export default { browser.runtime.reload() } }, + + /** + * Remove panel + */ + async removePanel(panel) { + if (!panel || !panel.name) return + if (panel.type !== 'ctx') return + + let preMsg = translate('settings.panel_remove_confirm_1') + let postMsg = translate('settings.panel_remove_confirm_2') + if (window.confirm(preMsg + panel.name + postMsg)) { + if (panel.cookieStoreId) { + await browser.contextualIdentities.remove(panel.cookieStoreId) + } + let index = State.panels.findIndex(p => { + return p.cookieStoreId === panel.cookieStoreId + }) + if (index > -1) State.panels.splice(index, 1) + } + }, + + /** + * Move panel + */ + movePanel(panel, dir) { + Actions.movePanel(panel.cookieStoreId || panel.id, dir) + }, + + /** + * Create panel-container + */ + async createPanel() { + const details = { + name: 'New Panel ' + (State.panels.length - 1), + color: 'blue', + icon: 'fingerprint', + } + let container = await browser.contextualIdentities.create(details) + State.panels.push({ + ...DEFAULT_CTX_TABS_PANEL, + ...container, + id: container.cookieStoreId, + cookieStoreId: container.cookieStoreId, + name: container.name, + icon: container.icon, + color: container.color, + }) + + State.selectedPanel = State.panels[State.panels.length - 1] + }, }, } diff --git a/src/page.settings/store/state.js b/src/page.settings/store/state.js index 19759d44..67dd72f5 100644 --- a/src/page.settings/store/state.js +++ b/src/page.settings/store/state.js @@ -1,7 +1,9 @@ import Manifest from '../../../addon/manifest.json' import { DEFAULT_SETTINGS, SETTINGS_OPTIONS } from '../../defaults' import { DEFAULT_TABS_MENU } from '../../defaults' +import { DEFAULT_TABS_PANEL_MENU } from '../../defaults' import { DEFAULT_BOOKMARKS_MENU } from '../../defaults' +import { DEFAULT_BOOKMARKS_PANEL_MENU } from '../../defaults' export default { version: Manifest.version, @@ -15,6 +17,7 @@ export default { activeView: 'Settings', activeSection: 'settings_general', + selectedPanel: null, navLock: false, highlightedField: '', highlight: { @@ -22,8 +25,12 @@ export default { tabHide: false, }, - tabsMenu: JSON.parse(JSON.stringify(DEFAULT_TABS_MENU)), + panels: [], + + tabsMenu: JSON.parse(JSON.stringify(DEFAULT_TABS_MENU)), + tabsPanelMenu: JSON.parse(JSON.stringify(DEFAULT_TABS_PANEL_MENU)), bookmarksMenu: JSON.parse(JSON.stringify(DEFAULT_BOOKMARKS_MENU)), + bookmarksPanelMenu: JSON.parse(JSON.stringify(DEFAULT_BOOKMARKS_PANEL_MENU)), ...SETTINGS_OPTIONS, ...DEFAULT_SETTINGS, diff --git a/src/page.url/url.js b/src/page.url/url.js index 11124e05..d0644d90 100644 --- a/src/page.url/url.js +++ b/src/page.url/url.js @@ -3,6 +3,16 @@ import { noiseBg } from '../noise-bg' import Utils from '../utils' void (async function() { + let linkEl = document.getElementById('url') + let copyBtnEl = document.getElementById('copy_btn') + + // Update labels + let pageTitle = browser.i18n.getMessage('unhandled_url') + if (pageTitle) document.title = pageTitle + + let copyBtnLabel = browser.i18n.getMessage('copy') + if (copyBtnLabel) copyBtnEl.innerText = copyBtnLabel + // Load settings and set theme let { settings } = await browser.storage.local.get({ settings: DEFAULT_SETTINGS }) let style = settings ? settings.style : 'dark' @@ -38,7 +48,6 @@ void (async function() { const hash = location.hash if (!hash) return const url = hash.slice(1) - const linkEl = document.getElementById('url') linkEl.innerText = url linkEl.addEventListener('click', () => { const selection = window.getSelection() @@ -46,6 +55,11 @@ void (async function() { range.selectNode(linkEl) selection.addRange(range) }) + + // Setup copy button + copyBtnEl.addEventListener('click', () => { + navigator.clipboard.writeText(url) + }) })() /** diff --git a/src/sidebar/actions.js b/src/sidebar/actions.js index 4b11acdc..0e1debac 100644 --- a/src/sidebar/actions.js +++ b/src/sidebar/actions.js @@ -8,7 +8,6 @@ import TabsActions from './actions/tabs' import BookmarksActions from './actions/bookmarks' import StylesActions from './actions/styles' import CtxMenuActions from './actions/menu' -import DashboardsActions from './actions/dashboards' import MiscActions from './actions/misc' const Actions = { @@ -20,7 +19,6 @@ const Actions = { ...BookmarksActions, ...StylesActions, ...CtxMenuActions, - ...DashboardsActions, ...MiscActions, } diff --git a/src/sidebar/actions/dashboards.js b/src/sidebar/actions/dashboards.js deleted file mode 100644 index f21db9e3..00000000 --- a/src/sidebar/actions/dashboards.js +++ /dev/null @@ -1,52 +0,0 @@ -import Actions from '../actions' - -/** - * Open panel menu by nav index. - * - * Special values: - * -1 - new container - * -2 - hidden panels - */ -async function openDashboard(i) { - if (i < -2 || i >= this.state.panels.length) return - - Actions.closeCtxMenu() - Actions.resetSelection() - this.state.dashboardIsOpen = true - - if (i === -2) { - this.state.dashboard = { - dashboard: 'HiddenPanelsDashboard', - panels: this.state.panels.filter(b => !b.bookmarks && b.inactive), - } - return - } - - this.state.panelIndex = i - - if (i === -1) this.state.dashboard = { dashboard: 'TabsDashboard', name: '', new: true } - else this.state.dashboard = this.state.panels[i] -} - -/** - * Close dashboard - */ -function closeDashboard() { - this.state.dashboardIsOpen = false - this.state.dashboard = null - - if (this.state.panelIndex < 0 && this.state.lastPanelIndex >= 0) { - this.state.panelIndex = this.state.lastPanelIndex - return - } - - let currentPanel = this.state.panels[this.state.panelIndex] - if (currentPanel && currentPanel.inactive) { - this.actions.switchToNeighbourPanel() - } -} - -export default { - openDashboard, - closeDashboard, -} \ No newline at end of file diff --git a/src/sidebar/actions/menu.js b/src/sidebar/actions/menu.js index a9d98674..23907246 100644 --- a/src/sidebar/actions/menu.js +++ b/src/sidebar/actions/menu.js @@ -7,18 +7,25 @@ const xmlSerializer = new XMLSerializer() /** * Open context menu */ -async function openCtxMenu(x, y) { +async function openCtxMenu(type, x, y) { if (this.state.ctxMenuNative) browser.menus.removeAll() if (!this.state.selected.length) return let nodeType, options, opts = [] - if (typeof this.state.selected[0] === 'number') { + if (type === 'tab') { nodeType = 'tab' options = this.state.tabsMenu - } else { + } + else if (type === 'bookmark') { nodeType = 'bookmark' options = this.state.bookmarksMenu } + else if (type === 'tabsPanel') { + options = this.state.tabsPanelMenu + } + else if (type === 'bookmarksPanel') { + options = this.state.bookmarksPanelMenu + } for (let optName of options) { if (optName instanceof Array) { @@ -55,6 +62,7 @@ async function openCtxMenu(x, y) { } opts = normalizeMenu(opts, this.state.ctxMenuNative) + if (!opts.length) return if (this.state.ctxMenuNative) { let parentId, parentName @@ -65,7 +73,7 @@ async function openCtxMenu(x, y) { parentName = opt.name continue } - createNativeOption(nodeType, opt, parentId, parentName) + this.actions.createNativeOption(nodeType, opt, parentId, parentName) } parentId = undefined parentName = undefined @@ -114,7 +122,7 @@ function normalizeMenu(menu, isNative) { if (!group.options.length) return false if (group.options[0].name && group.options.length === 1) return false if (group.options.length === 1 && group.options[0] === 'separator') { - return false + return isNative } return true }) @@ -125,6 +133,7 @@ function normalizeMenu(menu, isNative) { } function createNativeOption(ctx, option, parentId, parentName) { + if (!ctx) ctx = 'all' if (option === 'separator') { browser.menus.create({ type: 'separator', @@ -136,15 +145,19 @@ function createNativeOption(ctx, option, parentId, parentName) { if (option instanceof Array) { for (let opt of option) { - createNativeOption(ctx, opt, parentId) + this.actions.createNativeOption(ctx, opt, parentId) } return } let icon - if (option.icon && RGB_COLORS[option.color]) { + if (option.icon) { + let rgbColor = RGB_COLORS[option.color] + let alpha = option.inactive ? '64' : 'ff' + if (!rgbColor) rgbColor = '#686868' + alpha + let s = xmlSerializer.serializeToString(document.getElementById(option.icon)) - s = ' { if (c.proxified) c.proxified = false if (c.proxy) c.proxy.type = 'direct' @@ -191,7 +190,7 @@ function resetSelection() { for (let id of this.state.selected) { this.state.tabsMap[id].sel = false } - } else { + } else if (typeof id === 'string') { for (let id of this.state.selected) { this.state.bookmarksMap[id].sel = false } diff --git a/src/sidebar/actions/panels.js b/src/sidebar/actions/panels.js index ddb4afd0..e1256c0f 100644 --- a/src/sidebar/actions/panels.js +++ b/src/sidebar/actions/panels.js @@ -10,7 +10,7 @@ import { DEFAULT_CTX_TABS_PANEL, } from '../../defaults' -let recalcPanelScrollTimeout, savePanelsTimeout, updatePanelBoundsTimeout +let recalcPanelScrollTimeout, updatePanelBoundsTimeout /** * Load Contextual Identities and containers @@ -68,7 +68,6 @@ async function loadPanels() { panel.noEmpty = loadedPanel.noEmpty } panel.index = panels.length - panel.lastActiveTab = loadedPanel.lastActiveTab panels.push(panel) panelsMap[panel.cookieStoreId] = panel } @@ -110,7 +109,6 @@ async function loadPanels() { panel.includeHosts = loadedPanel.includeHosts panel.excludeHostsActive = loadedPanel.excludeHostsActive panel.excludeHosts = loadedPanel.excludeHosts - panel.lastActiveTab = loadedPanel.lastActiveTab panels.push(panel) panelsMap[panel.cookieStoreId] = panel @@ -174,7 +172,6 @@ async function updatePanels(newPanels) { panel.includeHosts = newPanel.includeHosts panel.excludeHostsActive = newPanel.excludeHostsActive panel.excludeHosts = newPanel.excludeHosts - panel.lastActiveTab = newPanel.lastActiveTab panels.push(panel) } @@ -255,17 +252,16 @@ async function savePanels() { includeHosts: panel.includeHosts, excludeHostsActive: panel.excludeHostsActive, excludeHosts: panel.excludeHosts, - lastActiveTab: panel.lastActiveTab, private: panel.private, bookmarks: panel.bookmarks, }) } const cleaned = JSON.parse(JSON.stringify(output)) - await browser.storage.local.set({ panels: cleaned }) -} -function savePanelsDebounced() { - if (savePanelsTimeout) clearTimeout(savePanelsTimeout) - savePanelsTimeout = setTimeout(() => Actions.savePanels(), 500) + browser.runtime.sendMessage({ + instanceType: 'bg', + action: 'savePanels', + arg: cleaned, + }) } /** @@ -330,7 +326,6 @@ function switchToPanel(index) { Actions.resetSelection() Actions.setPanel(index) - if (this.state.dashboardIsOpen) Actions.openDashboard(this.state.panelIndex) const panel = this.state.panels[this.state.panelIndex] if (panel.noEmpty && panel.tabs && !panel.tabs.length) { Actions.createTab(panel.cookieStoreId) @@ -414,7 +409,6 @@ async function switchPanel(dir = 0) { Actions.activateLastActiveTabOf(this.state.panelIndex) } - if (this.state.dashboardIsOpen) Actions.openDashboard(this.state.panelIndex) let panel = this.state.panels[this.state.panelIndex] if (panel.noEmpty && panel.tabs && !panel.tabs.length) { Actions.createTab(panel.cookieStoreId) @@ -441,34 +435,12 @@ function getActivePanel() { return Utils.cloneObject(this.state.panels[this.state.panelIndex]) } -async function movePanel(id, step) { - let index - if (id === 'bookmarks') index = this.state.panels.findIndex(p => p.bookmarks) - else index = this.state.panels.findIndex(p => p.cookieStoreId === id) - - if (index === -1) return - if (index + step < 0) return - if (index + step >= this.state.panels.length) return - - let panel = this.state.panels.splice(index, 1)[0] - this.state.panels.splice(index + step, 0, panel) - this.state.panelIndex = index + step - for (let i = 0; i < this.state.panels.length; i++) { - this.state.panels[i].index = i - } - - await Actions.loadTabs(false) - - Actions.savePanels() -} - export default { loadPanels, updatePanels, updatePanelsTabs, updatePanelsRanges, savePanels, - savePanelsDebounced, loadPanelIndex, setPanel, savePanelIndex, @@ -479,5 +451,4 @@ export default { switchPanel, goToActiveTabPanel, getActivePanel, - movePanel, } diff --git a/src/sidebar/actions/settings.js b/src/sidebar/actions/settings.js index 06e7a661..1d8f9ef3 100644 --- a/src/sidebar/actions/settings.js +++ b/src/sidebar/actions/settings.js @@ -93,7 +93,9 @@ function openSettings(section) { } } else { const conf = { url, windowId: this.state.windowId } - if (activePanel.tabs) conf.cookieStoreId = activePanel.cookieStoreId + if (activePanel && activePanel.tabs) { + conf.cookieStoreId = activePanel.cookieStoreId + } browser.tabs.create(conf) } } diff --git a/src/sidebar/actions/tabs.js b/src/sidebar/actions/tabs.js index 5ec6c418..e312b303 100644 --- a/src/sidebar/actions/tabs.js +++ b/src/sidebar/actions/tabs.js @@ -56,7 +56,10 @@ async function loadTabs(fresh = true) { const activePanelIsOk = activeTab.cookieStoreId === activePanel.cookieStoreId if (!activeTab.pinned && activePanelIsTabs && !activePanelIsOk) { const panel = this.state.panelsMap[activeTab.cookieStoreId] - if (panel) this.state.panelIndex = panel.index + if (panel) { + this.state.panelIndex = panel.index + this.state.lastPanelIndex = panel.index + } } } @@ -138,6 +141,8 @@ function findBranchStartIndex(array, subArray, startIndex) { similarity++ if (!similarity) startOffset++ else innerOffset++ + } else if (a.active && a.url === 'about:blank') { + similarity++ } } if (similarity === subArray.length) return i + startOffset @@ -217,7 +222,7 @@ async function restoreTabsTree() { for (let treeTab of branch) { let tab = this.state.tabs[tabIndex] - if (tab.url === treeTab.url) { + if (tab.url === treeTab.url || (tab.active && tab.url === 'about:blank')) { if (treeTab.isParent) parents[treeTab.id] = tab.id tab.isParent = treeTab.isParent tab.parentId = parents[treeTab.parentId] || -1 @@ -528,6 +533,25 @@ async function duplicateTabs(tabIds) { } } +/** + * Close tabs duplicates + */ +function dedupTabs(tabIds) { + if (!tabIds || !tabIds.length) return + + let urls = [] + let toRemove = [] + for (let id of tabIds) { + let tab = this.state.tabsMap[id] + if (!tab) return + + if (urls.includes(tab.url)) toRemove.push(tab.id) + else urls.push(tab.url) + } + + this.actions.removeTabs(toRemove) +} + /** * Create bookmarks from tabs */ @@ -861,6 +885,25 @@ async function toggleBranch(tabId) { else Actions.foldTabsBranch(tabId) } +/** + * Collaplse all inactive branches. + */ +function foldAllInactiveBranches(tabs = []) { + let isBranchActive = false + for (let i = tabs.length; i--; ) { + let tab = tabs[i] + if (!tab) break + if (tab.active && (tab.lvl > 0 || tab.isParent)) isBranchActive = true + if (tab.isParent && tab.parentId === -1) { + if (isBranchActive) { + isBranchActive = false + continue + } + this.actions.foldTabsBranch(tab.id) + } + } +} + /** * Drop to tabs panel */ @@ -1445,6 +1488,7 @@ export default { unmuteTabs, remuteTabs, duplicateTabs, + dedupTabs, bookmarkTabs, clearTabsCookies, @@ -1459,6 +1503,7 @@ export default { foldTabsBranch, expTabsBranch, toggleBranch, + foldAllInactiveBranches, dropToTabs, moveDroppedNodes, diff --git a/src/sidebar/components/bookmark.vue b/src/sidebar/components/bookmark.vue index 5e6ee728..447e2af2 100644 --- a/src/sidebar/components/bookmark.vue +++ b/src/sidebar/components/bookmark.vue @@ -69,7 +69,7 @@ export default { if (!State.selected.length) { Actions.selectItem(this.node.id) } else { - if (typeof State.selected[0] === 'number') return + if (typeof State.selected[0] !== 'string') return EventBus.$emit('updatePanelBounds') @@ -150,7 +150,7 @@ export default { Actions.stopMultiSelection() if (!State.ctxMenuNative) Actions.selectItem(this.node.id) - Actions.openCtxMenu(e.clientX, e.clientY) + Actions.openCtxMenu('bookmark', e.clientX, e.clientY) } }, @@ -173,7 +173,7 @@ export default { if (!State.selected.length) Actions.selectItem(this.node.id) - Actions.openCtxMenu() + Actions.openCtxMenu('bookmark') }, /** diff --git a/src/sidebar/components/bookmarks-dashboard.vue b/src/sidebar/components/bookmarks-dashboard.vue deleted file mode 100644 index a6a56c51..00000000 --- a/src/sidebar/components/bookmarks-dashboard.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - diff --git a/src/sidebar/components/bookmarks-folder.vue b/src/sidebar/components/bookmarks-folder.vue index 276fb83b..1d0a2d31 100644 --- a/src/sidebar/components/bookmarks-folder.vue +++ b/src/sidebar/components/bookmarks-folder.vue @@ -120,7 +120,7 @@ export default { if (!State.selected.length) { Actions.selectItem(this.node.id) } else { - if (typeof State.selected[0] === 'number') return + if (typeof State.selected[0] !== 'string') return EventBus.$emit('updatePanelBounds') @@ -195,7 +195,7 @@ export default { Actions.stopMultiSelection() if (!State.ctxMenuNative) Actions.selectItem(this.node.id) - Actions.openCtxMenu(e.clientX, e.clientY) + Actions.openCtxMenu('bookmark', e.clientX, e.clientY) } }, @@ -218,7 +218,7 @@ export default { if (!State.selected.length) Actions.selectItem(this.node.id) - Actions.openCtxMenu() + Actions.openCtxMenu('bookmark') }, /** diff --git a/src/sidebar/components/bookmarks-panel.vue b/src/sidebar/components/bookmarks-panel.vue index c659ca9b..a218f884 100644 --- a/src/sidebar/components/bookmarks-panel.vue +++ b/src/sidebar/components/bookmarks-panel.vue @@ -1,5 +1,9 @@