TabHub Devlog (v1.0.2 + v1.0.3)

This blog post mainly talk about the technical side behinds TabHub directory system. There are a lot of new features implemented in TabHub but I can't cover everything in this single blog post. If you guys want to learn more about those features, you can follow: https://tabhub.medium.com/ or read the CHANGELOG below.

New features introduction
Brand new UI/UX
There's nothing to talk much about this new UI/UX. It's obvious. I put a lot of effort to improve the browsing experience of TabHub users.

Quick Action Mode

So after receiving a few feedback from some folks on the Internet and my co-founder, I decided to add this new feature to replace OneTab main functionality completely. As a past user of OneTab extension, what I did notice is how annoying when you mistakenly click on the extension and it automatically collapse all your active tabs. I hate that! A lot! So I add a new mechanism called "✅ Remember action". One click on TabHub icon only take action immediately if the check box is checked.

I also have a source code for this little piece component if you want to use it somewhere or in your own apps.
const TabCardsStack = ({ tabs }: Props) => {
return (
<div>
{tabs.map((tab, index) => (
<div
style={{
position: 'absolute',
marginLeft: (index % 2 === 0 ? -1 : 1) * (tabs.length - index) * 9,
}}>
<AnimatedComponent.OpacityFadeInDiv delay={(tabs.length - index) * 25}>
<WindowTabIcon
avatarSize={60 - (tabs.length - index)}
windowTab={tab}
style={{
rotate: `${(tabs.length - index) * index}deg`,
borderRadius: 15,
width: 70 - (tabs.length - index),
height: 70 - (tabs.length - index),
}}
/>
</AnimatedComponent.OpacityFadeInDiv>
</div>
))}
</div>
);
};
Nested-directory data structure
As highlighted in our previous blog post, TabHub has been envisioned as an evolution of Chrome's bookmark manager—an upgrade for your "friendly neighborhood" tool. This realization prompted an important question that has been swirling in my mind: "Should we venture into building our own link management system?" It is only natural to contemplate such a question when running a startup from the ground up, as it demands careful consideration of where to invest our resources and prioritize our efforts.
After extensive deliberation, not only focusing on technical decisions but also assessing the potential benefits and scalability of forthcoming features, I arrived at a resolute decision. TabHub will indeed include a custom-built link system within our platform. The advantages stemming from this core feature, coupled with the long-term scalability and growth prospects it offers, solidified my conviction that investing in its development is the right path forward.

By taking this bold step, we are poised to offer users an elevated experience that goes beyond traditional bookmark management. Our custom link system will provide enhanced organization, efficient searching, and seamless synchronization, empowering you to effortlessly navigate and retrieve your saved resources. This strategic move exemplifies our dedication to crafting a truly comprehensive and indispensable tab and link management solution.
Designing a nested-folder data structure

From a programming aspect, our core system is designed to have an atomic-level data model called RepositoryTab. This is the smallest element of data to be stored in the database. Higher than the first citizen one level is the Directory which can be called a parent of RepositoryTab. All these atoms are stored in the Repository which are stored in Workspace.
This data structure is used in most of the part for tab storing in TabHub, but after a while, I realized this is quite similar to the way Chrome structure their bookmark system.

Here is a small piece of code for anyone who is interested with the way tabs are stored in TabHub. To render the tab and directory out, it requires a bit understanding of recursion. I believe every nested folder system like code editor, file system does follow a similar structure like this on the front end side.
const DraggableTreeNodeList = ({
treeNodes,
disableHeader,
nodeOnContextMenu,
draggableDisabled,
itemSelectedCondition,
directoryOnClickedEvent,
itemOnClickedEvent,
onItemDragEvent,
isForceMobile,
plugins = [],
}: Props) => {
return (
<LoadableContainer isLoading={treeNodes.length <= 0} loadComponent={<div>No tabs found</div>}>
{!disableHeader && <TreeNodeListHeader isForceMobile={isForceMobile} />}
<DraggableTreeNodeContainer
disabled={draggableDisabled}
onDragEnd={onItemDragEvent}
renderTreeComponent={(treeItem: TreeNode) => {
return (
<div>
{treeItem.type === 'tab' ? (
<WindowTabItem
plugins={plugins}
id={buildTreeNodeId(treeItem, 'tab')}
actionable
key={treeItem.value.id}
isForceMobile={isForceMobile}
isSelected={itemSelectedCondition(treeItem.value as RepositoryTab)}
windowTab={treeItem.value as RepositoryTab}
onRightClick={nodeOnContextMenu}
onClick={itemOnClickedEvent}
/>
) : (
<DirectoryNodeItem
plugins={plugins}
id={buildTreeNodeId(treeItem, 'directory')}
onClick={directoryOnClickedEvent}
node={treeItem.value as TabTreeDirectory}
isForceMobile={isForceMobile}
onItemDragEvent={onItemDragEvent}
onRightClick={nodeOnContextMenu}
draggableDisabled
isSelected={itemSelectedCondition(treeItem.value as RepositoryTab)}
itemSelectedCondition={itemSelectedCondition}
itemOnClickedEvent={itemOnClickedEvent}
/>
)}
</div>
);
}}
treeNodes={treeNodes}
/>
</LoadableContainer>
);
};
Integrating with Chrome bookmark system
Due to the similarities between two architecture, I decided to add another feature to the app which helps user to synchronize their data from existing browser bookmarks. From the business perspective, this also reduces the context switch cost to the lowest.
