Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/_locales/dict.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,9 @@ export const commonTranslations: Translations = {
ja: 'パネルを削除',
},
// - Tab
'menu.tab.recently_closed': {
en: 'Recently closed tabs',
},
'menu.tab.undo': {
en: 'Undo close tab',
de: 'Geschlossenen Tab wiederherstellen',
Expand Down
1 change: 1 addition & 0 deletions src/defaults/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MenuConf } from 'src/types'
export const TABS_MENU: MenuConf = [
{ opts: ['undoRmTab', 'mute', 'reload', 'bookmark'] },
'separator-1',
'recentlyClosedTabs',
{
name: '%menu.tab.move_to_sub_menu_name',
opts: ['moveToNewWin', 'moveToWin', 'separator-5', 'moveToPanel', 'moveToNewPanel'],
Expand Down
1 change: 1 addition & 0 deletions src/page.setup/components/menu-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ interface MenuEditorGroup {

const TABS_MENU_OPTS: Record<string, string> = {
undoRmTab: 'menu.tab.undo',
recentlyClosedTabs: 'menu.tab.recently_closed',
pin: 'menu.tab.pin',
reload: 'menu.tab.reload',
bookmark: 'menu.tab.bookmark',
Expand Down
19 changes: 19 additions & 0 deletions src/services/menu.fg.options.tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ export const tabsMenuOptions: Record<string, () => MenuOption | MenuOption[] | u
onClick: () => Tabs.undoRmTab(),
}),

recentlyClosedTabs: () => {
const sub: MenuOption[] = Tabs.recentlyRemoved.slice(0, 15).map(rmt => ({
label: rmt.title || rmt.url,
tooltip: rmt.url,
img: rmt.favIconUrl,
icon: rmt.favIconUrl ? undefined : rmt.favPlaceholder?.replace(/^#/, ''),
onClick: () => Tabs.reopenRecentlyRemoved(rmt, false, false),
onAltClick: () => Tabs.reopenRecentlyRemoved(rmt, true, true),
}))
const option: MenuOption = {
label: translate('menu.tab.recently_closed'),
icon: 'icon_undo',
sub,
inactive: sub.length === 0,
}
if (!Settings.state.ctxMenuRenderInact && option.inactive) return
return option
},

moveToNewWin: () => {
return {
label: translate(`menu.tab.move_to_new_${Windows.incognito ? 'priv_window' : 'window'}`),
Expand Down
30 changes: 16 additions & 14 deletions src/services/menu.fg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function open(type: MenuType, x?: number, y?: number, customForced?: bool
for (const block of blocks) {
for (const opt of block.opts) {
if (opt.sub && opt.sub.length && opt.label) {
const parentId = createNativeSubMenuOption(opt.label, nodeType)
const parentId = createNativeSubMenuOption(opt, nodeType)
for (const subOpt of opt.sub) {
createNativeOption(nodeType, subOpt, parentId)
}
Expand Down Expand Up @@ -307,6 +307,16 @@ function getBase64SVGIcon(icon: string, rgbColor: string): string | undefined {
}
}

function getNativeOptionIcon(option: T.MenuOption): string | undefined {
if (!Settings.state.ctxMenuRenderIcons) return
if (option.img) return option.img
if (option.icon) {
const alpha = option.inactive ? '64' : 'ff'
const rgbColor = option.color ? D.RGB_COLORS[option.color] : '#686868' + alpha
return getBase64SVGIcon(option.icon, rgbColor)
}
}

function createNativeOption(
ctx: browser.menus.ContextType,
option: T.MenuOption,
Expand All @@ -318,17 +328,7 @@ function createNativeOption(
return
}

let icon
if (Settings.state.ctxMenuRenderIcons) {
if (option.img) {
icon = option.img
} else if (option.icon) {
const alpha = option.inactive ? '64' : 'ff'
const rgbColor = option.color ? D.RGB_COLORS[option.color] : '#686868' + alpha

icon = getBase64SVGIcon(option.icon, rgbColor)
}
}
const icon = getNativeOptionIcon(option)

const optProps: browser.menus.CreateProperties = {
type: 'normal',
Expand All @@ -350,14 +350,16 @@ function createNativeOption(
browser.menus.create(optProps)
}

function createNativeSubMenuOption(title: string, ctx?: browser.menus.ContextType): string {
function createNativeSubMenuOption(option: T.MenuOption, ctx?: browser.menus.ContextType): string {
if (!ctx) ctx = 'all'
const optProps: browser.menus.CreateProperties = {
type: 'normal',
contexts: [ctx],
viewTypes: ['sidebar'],
title: title,
title: option.label,
}
const icon = getNativeOptionIcon(option)
if (icon) optProps.icons = { '16': icon }
return browser.menus.create(optProps)
}

Expand Down
59 changes: 58 additions & 1 deletion src/services/tabs.fg.rm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ItemInfo, RecentlyClosedTabInfo, Tab } from 'src/types'
import { DstPlaceInfo, ItemInfo, RecentlyClosedTabInfo, Tab } from 'src/types'
import { ConfirmationType } from 'src/enums'
import { NOID } from 'src/defaults'
import { translate } from 'src/dict'
Expand Down Expand Up @@ -516,3 +516,60 @@ export async function undoRmTab(): Promise<void> {
if (session && session.tab?.sessionId) await browser.sessions.restore(session.tab.sessionId)
}
}

export function getRecentlyRemovedBranch(rootTab: RecentlyClosedTabInfo): RecentlyClosedTabInfo[] {
const branch: RecentlyClosedTabInfo[] = [rootTab]

const startIndex = Tabs.recentlyRemoved.findIndex(t => t.id === rootTab.id)
if (startIndex === -1) return branch

for (let i = startIndex + 1; i < Tabs.recentlyRemoved.length; i++) {
const tab = Tabs.recentlyRemoved[i]
if (!tab) break
if (rootTab.lvl >= tab.lvl) break
branch.push(tab)
}

return branch
}

export async function reopenRecentlyRemoved(
targetTab: RecentlyClosedTabInfo,
inactive: boolean,
branch: boolean
): Promise<void> {
if (!targetTab.isParent) branch = false
if (branch) inactive = true

const tabsBranch = getRecentlyRemovedBranch(targetTab)
const rcTabs = branch ? tabsBranch : [targetTab]
const panelId = Sidebar.activePanelId
const panel = Sidebar.panelsById[panelId]
if (!Utils.isTabsPanel(panel)) return

const dst: DstPlaceInfo = {
panelId,
discarded: inactive,
index: Tabs.getIndexForNewTab(panel),
parentId: Tabs.getParentForNewTab(panel),
}

const tabsToOpen: ItemInfo[] = rcTabs.map(rct => ({
id: rct.id,
title: rct.title,
url: rct.url,
container: rct.containerId,
parentId: rct.parentId,
}))
if (!inactive && tabsToOpen.length) tabsToOpen[0].active = true

await Tabs.open(tabsToOpen, dst)

if (rcTabs.length === 1) tabsBranch.forEach(t => t.lvl--)
for (const tab of rcTabs) {
const index = Tabs.recentlyRemoved.findIndex(t => t.id === tab.id)
if (index !== -1) Tabs.recentlyRemoved.splice(index, 1)
}

Tabs.reactive.recentlyRemovedLen = Tabs.recentlyRemoved.length
}
68 changes: 2 additions & 66 deletions src/sidebar/components/sub-panel.closed-tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -177,74 +177,10 @@ function onTabDragStart(e: DragEvent, tab: T.RecentlyClosedTabInfo) {
Sidebar.closeSubPanel()
}

function getBranch(rootTab: T.RecentlyClosedTabInfo): T.RecentlyClosedTabInfo[] {
const branch: T.RecentlyClosedTabInfo[] = [rootTab]

const startIndex = Tabs.recentlyRemoved.findIndex(t => t.id === rootTab.id)
if (startIndex === -1) return branch

for (let i = startIndex + 1; i < Tabs.recentlyRemoved.length; i++) {
const tab = Tabs.recentlyRemoved[i]
if (!tab) break
if (rootTab.lvl >= tab.lvl) break
branch.push(tab)
}

return branch
}
const getBranch = Tabs.getRecentlyRemovedBranch

async function openTabs(targetTab: T.RecentlyClosedTabInfo, inactive: boolean, branch: boolean) {
if (!targetTab.isParent) branch = false
if (branch) inactive = true

const tabsBranch = getBranch(targetTab)
const rcTabs = branch ? tabsBranch : [targetTab]
const panelId = Sidebar.activePanelId
const panel = Sidebar.panelsById[panelId]
if (!Utils.isTabsPanel(panel)) return

const dst: T.DstPlaceInfo = {
panelId,
discarded: inactive,
index: Tabs.getIndexForNewTab(panel),
parentId: Tabs.getParentForNewTab(panel),
}

const tabsToOpen: T.ItemInfo[] = []
for (const rct of rcTabs) {
tabsToOpen.push({
id: rct.id,
title: rct.title,
url: rct.url,
container: rct.containerId,
parentId: rct.parentId,
})
}
if (!inactive && tabsToOpen.length) tabsToOpen[0].active = true

await Tabs.open(tabsToOpen, dst)

// Trigger flash animation
// const els = []
// for (const tab of tabs) {
// const id = `rmt${tab.id}`
// const tabEl = document.getElementById(id)
// if (!tabEl) continue
// tabEl.setAttribute('data-flash', 'true')
// els.push(tabEl)
// }
// await Utils.sleep(500)
// els.forEach(el => el.removeAttribute('data-flash'))

// Or remove from list
if (rcTabs.length === 1) tabsBranch.forEach(t => t.lvl--)
for (const tab of rcTabs) {
const index = Tabs.recentlyRemoved.findIndex(t => t.id === tab.id)
if (index !== -1) Tabs.recentlyRemoved.splice(index, 1)
}

await Tabs.reopenRecentlyRemoved(targetTab, inactive, branch)
if (!Tabs.recentlyRemoved.length) Sidebar.closeSubPanel()

Tabs.reactive.recentlyRemovedLen = Tabs.recentlyRemoved.length
}
</script>