1
0
mirror of https://github.com/XFox111/TabsAsideExtension.git synced 2026-07-02 19:52:47 +03:00

Compare commits

..

15 Commits

Author SHA1 Message Date
xfox111 e803636c35 chore: release candidate 7 2025-07-13 16:54:54 +03:00
xfox111 c8b4ef3e15 fix: tabs in collection list view overlap on overflow on Firefox 2025-07-13 16:50:02 +03:00
xfox111 eeefd1feff fix: collection context menus flickering in Firefox popup view 2025-07-13 16:20:17 +03:00
xfox111 53adbd4f75 fix: adding forbidden tabs via "Add selected tabs" 2025-07-11 13:53:27 +03:00
xfox111 3eed3b4b01 fix: list view horizontal overflow in sidebar on firefox 2025-07-09 00:49:09 +03:00
xfox111 525130b7e9 fix: collection collapses on tab/group drag 2025-07-06 12:34:02 +03:00
xfox111 1a274348e0 fix!: incorrect byte calculation for cloud storage (changed encoding) 2025-07-06 12:17:17 +03:00
xfox111 2c8cfa1583 chore: release candidate 6 2025-07-03 12:58:37 +03:00
xfox111 e844a68f49 chore(ui): remove fixed collection height for list view 2025-07-03 12:56:58 +03:00
xfox111 794f6e3af0 chore(locale): clarify action icon option for slaivc locales (ru, uk, pl) 2025-07-03 12:06:25 +03:00
xfox111 d85d10dc58 chore(ui): increase tab's dnd handle area 2025-07-03 11:46:53 +03:00
xfox111 213cc84602 chore: reduce delay and increase tolerance for mouse drag and drop 2025-07-03 11:33:05 +03:00
xfox111 9675b65e81 fix: pinned tab opens and closes infinitely if pwa window is opened 2025-07-03 11:24:53 +03:00
xfox111 6bce330a8f chore: dependabot reviewers deprecation 2025-06-03 18:55:01 +03:00
xfox111 405f9163f2 !feat: tabGroups API for Firefox 139 2025-05-23 16:45:50 +03:00
22 changed files with 101 additions and 99 deletions
+2
View File
@@ -0,0 +1,2 @@
* @XFox111
locales/pt_BR.yml @maisondasilva @XFox111
+3 -9
View File
@@ -12,9 +12,7 @@ updates:
directory: "/" # Location of package manifests
target-branch: "next"
assignees:
- "xfox111"
reviewers:
- "xfox111"
- "XFox111"
schedule:
interval: monthly
rebase-strategy: disabled
@@ -24,9 +22,7 @@ updates:
directory: "/"
target-branch: "next"
assignees:
- "xfox111"
reviewers:
- "xfox111"
- "XFox111"
schedule:
interval: monthly
rebase-strategy: disabled
@@ -36,9 +32,7 @@ updates:
directory: "/"
target-branch: "next"
assignees:
- "xfox111"
reviewers:
- "xfox111"
- "XFox111"
schedule:
interval: monthly
rebase-strategy: disabled
+1 -1
View File
@@ -257,7 +257,7 @@ export default defineBackground(() =>
for (const openWindow of openWindows)
{
if (openWindow.incognito)
if (openWindow.incognito || openWindow.type !== "normal")
continue;
const activeTabs: Tabs.Tab[] = openWindow.tabs!.filter(tab =>
@@ -27,7 +27,7 @@ export const useStyles_CollectionView = makeStyles({
},
verticalRoot:
{
height: "560px"
maxHeight: "560px"
},
empty:
{
@@ -74,7 +74,8 @@ export const useStyles_CollectionView = makeStyles({
{
gridAutoFlow: "row",
width: "100%",
paddingBottom: tokens.spacingVerticalS
paddingBottom: tokens.spacingVerticalS,
gridAutoRows: import.meta.env.FIREFOX ? "min-content" : undefined
},
dragOverlay:
{
@@ -50,26 +50,30 @@ export default function CollectionView({ collection, index: collectionIndex, dra
<CollectionHeader dragHandleProps={ activatorProps } dragHandleRef={ setActivatorNodeRef } />
{ collection.items.length < 1 ?
<div className={ cls.empty }>
<CollectionsRegular fontSize={ 32 } />
<Body1Strong>{ i18n.t("collections.empty") }</Body1Strong>
</div>
:
<div className={ mergeClasses(cls.list, !tilesView && cls.verticalList) }>
<SortableContext
items={ collection.items.map((_, index) => [collectionIndex, index].join("/")) }
strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy }
>
{ collection.items.map((i, index) =>
i.type === "group" ?
<GroupView
key={ index } group={ i } indices={ [collectionIndex, index] } />
:
<TabView key={ index } tab={ i } indices={ [collectionIndex, index] } />
) }
</SortableContext>
</div>
{ (!activeItem || activeItem.item.type !== "collection") && !dragOverlay &&
<>
{ collection.items.length < 1 ?
<div className={ cls.empty }>
<CollectionsRegular fontSize={ 32 } />
<Body1Strong>{ i18n.t("collections.empty") }</Body1Strong>
</div>
:
<div className={ mergeClasses(cls.list, !tilesView && cls.verticalList) }>
<SortableContext
items={ collection.items.map((_, index) => [collectionIndex, index].join("/")) }
strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy }
>
{ collection.items.map((i, index) =>
i.type === "group" ?
<GroupView
key={ index } group={ i } indices={ [collectionIndex, index] } />
:
<TabView key={ index } tab={ i } indices={ [collectionIndex, index] } />
) }
</SortableContext>
</div>
}
</>
}
</div >
</CollectionContext.Provider>
@@ -58,8 +58,6 @@ export const useStyles_TabView = makeStyles({
display: "grid",
gridTemplateColumns: "auto 1fr auto",
alignItems: "center",
gap: tokens.spacingHorizontalSNudge,
paddingLeft: tokens.spacingHorizontalS,
borderBottomLeftRadius: tokens.borderRadiusMedium,
borderBottomRightRadius: tokens.borderRadiusMedium,
@@ -72,6 +70,9 @@ export const useStyles_TabView = makeStyles({
icon:
{
cursor: "grab",
padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalSNudge}`,
height: "32px",
boxSizing: "border-box",
"&:active":
{
@@ -83,7 +83,6 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
ref={ setActivatorNodeRef } { ...activatorProps }
src={ graphics[tab.url]?.icon ?? faviconPlaceholder }
onError={ e => e.currentTarget.src = faviconPlaceholder }
height={ 20 } width={ 20 }
className={ cls.icon } draggable={ false } />
<Tooltip relationship="description" content={ tab.title ?? tab.url }>
@@ -12,6 +12,7 @@ import saveTabsToCollection from "@/utils/saveTabsToCollection";
export default function CollectionHeader({ dragHandleRef, dragHandleProps }: CollectionHeaderProps): React.ReactElement
{
const [contextOpen, setContextOpen] = useState<boolean>(false);
const [listLocation] = useSettings("listLocation");
const isTab: boolean = listLocation === "tab" || listLocation === "pinned";
const { updateCollection } = useCollections();
@@ -53,7 +54,7 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
mergeClasses(
cls.toolbar,
"CollectionView__toolbar",
alwaysShowToolbars === true && cls.showToolbar
(alwaysShowToolbars === true || contextOpen) && cls.showToolbar
) }
>
{ tabCount < 1 ?
@@ -61,10 +62,10 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
{ isTab ? i18n.t("collections.menu.add_all") : i18n.t("collections.menu.add_selected") }
</Button>
:
<OpenCollectionButton />
<OpenCollectionButton onOpenChange={ (_, e) => setContextOpen(e.open) } />
}
<CollectionMoreButton onAddSelected={ handleAddSelected } />
<CollectionMoreButton onAddSelected={ handleAddSelected } onOpenChange={ (_, e) => setContextOpen(e.open) } />
</div>
</div>
);
@@ -1,14 +1,14 @@
import { useDialog } from "@/contexts/DialogProvider";
import { useDangerStyles } from "@/hooks/useDangerStyles";
import useSettings from "@/hooks/useSettings";
import { Button, Menu, MenuDivider, MenuItem, MenuList, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components";
import { Button, Menu, MenuDivider, MenuItem, MenuList, MenuOpenChangeData, MenuOpenEvent, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components";
import * as ic from "@fluentui/react-icons";
import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext";
import { useCollections } from "../../contexts/CollectionsProvider";
import exportCollectionToBookmarks from "../../utils/exportCollectionToBookmarks";
import EditDialog from "../EditDialog";
export default function CollectionMoreButton({ onAddSelected }: CollectionMoreButtonProps): React.ReactElement
export default function CollectionMoreButton({ onAddSelected, onOpenChange }: CollectionMoreButtonProps): React.ReactElement
{
const [listLocation] = useSettings("listLocation");
const isTab: boolean = listLocation === "tab" || listLocation === "pinned";
@@ -56,7 +56,7 @@ export default function CollectionMoreButton({ onAddSelected }: CollectionMoreBu
);
return (
<Menu>
<Menu onOpenChange={ onOpenChange }>
<Tooltip relationship="label" content={ i18n.t("common.tooltips.more") }>
<MenuTrigger>
<Button appearance="subtle" icon={ <ic.MoreVertical20Regular /> } />
@@ -94,4 +94,5 @@ export default function CollectionMoreButton({ onAddSelected }: CollectionMoreBu
export type CollectionMoreButtonProps =
{
onAddSelected?: () => void;
onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void;
};
@@ -1,13 +1,13 @@
import { useDialog } from "@/contexts/DialogProvider";
import useSettings from "@/hooks/useSettings";
import browserLocaleKey from "@/utils/browserLocaleKey";
import { Menu, MenuButtonProps, MenuItem, MenuList, MenuPopover, MenuTrigger, SplitButton } from "@fluentui/react-components";
import { Menu, MenuButtonProps, MenuItem, MenuList, MenuOpenChangeData, MenuOpenEvent, MenuPopover, MenuTrigger, SplitButton } from "@fluentui/react-components";
import * as ic from "@fluentui/react-icons";
import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext";
import { useCollections } from "../../contexts/CollectionsProvider";
import { openCollection } from "../../utils/opener";
export default function OpenCollectionButton(): React.ReactElement
export default function OpenCollectionButton({ onOpenChange }: OpenCollectionButtonProps): React.ReactElement
{
const [defaultAction] = useSettings("defaultRestoreAction");
const { removeItem } = useCollections();
@@ -54,7 +54,7 @@ export default function OpenCollectionButton(): React.ReactElement
};
return (
<Menu>
<Menu onOpenChange={ onOpenChange }>
<MenuTrigger disableButtonEnhancement>
{ (triggerProps: MenuButtonProps) => defaultAction === "restore" ?
<SplitButton
@@ -95,3 +95,8 @@ export default function OpenCollectionButton(): React.ReactElement
</Menu>
);
}
export type OpenCollectionButtonProps =
{
onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void;
};
@@ -46,6 +46,10 @@ export const useStyles_CollectionListView = makeStyles({
listView:
{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(360px, 1fr))"
"@media screen and (min-width: 360px)":
{
gridTemplateColumns: "repeat(auto-fit, minmax(360px, 1fr))"
}
}
});
@@ -33,7 +33,7 @@ export default function CollectionListView(): ReactElement
const [active, setActive] = useState<DndItem | null>(null);
const sensors = useSensors(
useSensor(MouseSensor, { activationConstraint: { delay: 100, tolerance: 0 } }),
useSensor(MouseSensor, { activationConstraint: { delay: 10, tolerance: 20 } }),
useSensor(TouchSensor, { activationConstraint: { delay: 300, tolerance: 20 } })
);
+18 -2
View File
@@ -1,8 +1,24 @@
import { TabItem } from "@/models/CollectionModels";
import sendNotification from "@/utils/sendNotification";
import { Tabs } from "wxt/browser";
export default async function getSelectedTabs(): Promise<TabItem[]>
{
const tabs: Tabs.Tab[] = await browser.tabs.query({ currentWindow: true, highlighted: true });
return tabs.filter(i => i.url).map(i => ({ type: "tab", url: i.url!, title: i.title }));
let tabs: Tabs.Tab[] = await browser.tabs.query({ currentWindow: true, highlighted: true });
const tabCount: number = tabs.length;
tabs = tabs.filter(i =>
i.url
&& new URL(i.url).protocol !== "about:"
&& new URL(i.url).hostname !== "newtab"
);
if (tabs.length < tabCount)
await sendNotification({
title: i18n.t("notifications.partial_save.title"),
message: i18n.t("notifications.partial_save.message"),
icon: "/notification_icons/save_warning.png"
});
return tabs.map(i => ({ type: "tab", url: i.url!, title: i.title }));
}
+4 -7
View File
@@ -68,13 +68,10 @@ async function createGroup(group: GroupItem, windowId: number, discard?: boolean
createProperties: { windowId }
});
// Grouping support came in 138, tabGroups is expected to be in 139
// TODO: Remove this check once the API is available
if (!import.meta.env.FIREFOX)
await chrome.tabGroups.update(groupId, {
title: group.title,
color: group.color
});
await chrome.tabGroups.update(groupId, {
title: group.title,
color: group.color
});
}
async function manageWindow(handle: (windowId: number) => Promise<void>, windowProps?: Windows.CreateCreateDataType): Promise<void>
@@ -14,7 +14,7 @@ export default async function getCollectionsFromCloud(): Promise<CollectionItem[
const chunks: Record<string, string> =
await browser.storage.sync.get(getChunkKeys(0, chunkCount)) as Record<string, string>;
const data: string = decompress(Object.values(chunks).join(), { inputEncoding: "StorageBinaryString" });
const data: string = decompress(Object.values(chunks).join(), { inputEncoding: "Base64" });
return parseCollections(data);
}
@@ -14,7 +14,7 @@ export default async function saveCollectionsToCloud(collections: CollectionItem
return;
}
const data: string = compress(serializeCollections(collections), { outputEncoding: "StorageBinaryString" });
const data: string = compress(serializeCollections(collections), { outputEncoding: "Base64" });
const chunks: string[] = splitIntoChunks(data);
if (chunks.length > collectionStorage.maxChunkCount)
+1 -1
View File
@@ -84,7 +84,7 @@ options_page:
icon_action:
title: "Po kliknięciu ikony rozszerzenia:"
options:
action: "Wykonaj domyślną akcję"
action: "Zapisz karty (domyślna akcja)"
context: "Pokaż menu kontekstowe"
open: "Otwórz listę kolekcji"
change_shortcuts: "Zmień skróty klawiszowe"
+1 -1
View File
@@ -84,7 +84,7 @@ options_page:
icon_action:
title: "При нажатии на иконку расширения:"
options:
action: "Выполнить действие по умолчанию"
action: "Сохранить вкладки (действие по умолчанию)"
context: "Показать контекстное меню"
open: "Открыть список коллекций"
change_shortcuts: "Изменить горячие клавиши"
+1 -1
View File
@@ -84,7 +84,7 @@ options_page:
icon_action:
title: "При натисканні на іконку розширення:"
options:
action: "Виконати дію за замовчуванням"
action: "Зберегти вкладки (дія за замовчуванням)"
context: "Показати контекстне меню"
open: "Відкрити список колекцій"
change_shortcuts: "Змінити гарячі клавіші"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "tabs-aside",
"private": true,
"version": "3.0.0-rc4",
"version": "3.0.0-rc7",
"type": "module",
"scripts": {
"dev": "wxt",
+10 -31
View File
@@ -88,17 +88,9 @@ async function createCollectionFromTabs(tabs: Tabs.Tab[]): Promise<[CollectionIt
tabs.every(i => i.groupId === tabs[0].groupId)
)
{
// TODO: Remove the check when Firefox 139 is out
if (!import.meta.env.FIREFOX)
{
const group = await browser.tabGroups!.get(tabs[0].groupId);
collection.title = group.title;
collection.color = group.color;
}
else
{
collection.color = "blue";
}
const group = await chrome.tabGroups.get(tabs[0].groupId);
collection.title = group.title;
collection.color = group.color;
tabs.forEach(i =>
collection.items.push({ type: "tab", url: i.url!, title: i.title })
@@ -123,27 +115,14 @@ async function createCollectionFromTabs(tabs: Tabs.Tab[]): Promise<[CollectionIt
if (!activeGroup || activeGroup !== tab.groupId)
{
activeGroup = tab.groupId;
const group = await chrome.tabGroups.get(activeGroup);
// TODO: Remove the check when Firefox 139 is out
if (import.meta.env.FIREFOX)
{
collection.items.push({
type: "group",
color: "blue",
items: []
});
}
else
{
const group = await browser.tabGroups!.get(activeGroup);
collection.items.push({
type: "group",
color: group.color,
title: group.title,
items: []
});
}
collection.items.push({
type: "group",
color: group.color,
title: group.title,
items: []
});
}
(collection.items[collection.items.length - 1] as GroupItem).items.push({
+5 -7
View File
@@ -24,7 +24,8 @@ export default defineConfig({
description: "__MSG_manifest_description__",
homepage_url: "https://github.com/xfox111/TabsAsideExtension/",
action: {
action:
{
default_title: "__MSG_manifest_name__"
},
@@ -36,7 +37,8 @@ export default defineConfig({
"tabs",
"notifications",
"contextMenus",
"bookmarks"
"bookmarks",
"tabGroups"
],
commands:
@@ -78,17 +80,13 @@ export default defineConfig({
gecko:
{
id: "tabsaside@xfox111.net",
strict_min_version: "138.0"
strict_min_version: "139.0"
}
};
// @ts-expect-error author key in Firefox has a different format
manifest.author = "__MSG_manifest_author__";
}
else
{
manifest.permissions!.push("tabGroups");
}
return manifest;
}