* chore(release): v5.1.0

* feat: implement resume thumbnails

* fix: remove unused mcp tools

* docs: fix formatting of docs
This commit is contained in:
Amruth Pillai
2026-05-07 15:12:33 +02:00
committed by GitHub
parent 51c366310e
commit 50ba37a27f
1015 changed files with 106087 additions and 141872 deletions
@@ -0,0 +1,55 @@
import type { SidebarSection } from "@/libs/resume/section";
import { createJSONStorage, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
import { create } from "zustand/react";
import { leftSidebarSections, rightSidebarSections } from "@/libs/resume/section";
type SectionCollapseState = {
[id in SidebarSection]?: { collapsed: boolean };
};
type SectionStoreState = {
sections: SectionCollapseState;
};
type SectionStoreActions = {
setCollapsed: (id: SidebarSection, collapsed: boolean) => void;
toggleCollapsed: (id: SidebarSection) => void;
toggleAll: () => void;
};
type SectionStore = SectionStoreState & SectionStoreActions;
export const useSectionStore = create<SectionStore>()(
persist(
immer((set) => ({
sections: {},
setCollapsed: (id, collapsed) => {
set((state) => {
state.sections[id] = { collapsed };
});
},
toggleCollapsed: (id) => {
set((state) => {
const current = state.sections[id]?.collapsed ?? false;
state.sections[id] = { collapsed: !current };
});
},
toggleAll: () => {
set((state) => {
[...leftSidebarSections, ...rightSidebarSections].forEach((id) => {
const current = state.sections[id]?.collapsed ?? false;
state.sections[id] = { collapsed: !current };
});
});
},
})),
{
name: "section-store",
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
sections: state.sections,
}),
},
),
);
@@ -0,0 +1,139 @@
import type { Layout, usePanelRef } from "react-resizable-panels";
import { useCallback, useMemo } from "react";
import { useWindowSize } from "usehooks-ts";
import { create } from "zustand/react";
import { useIsMobile } from "@/hooks/use-mobile";
type PanelImperativeHandle = ReturnType<typeof usePanelRef>;
export const BUILDER_LAYOUT_COOKIE_NAME = "builder_layout";
export type BuilderLayout = {
left: number;
artboard: number;
right: number;
};
export const DEFAULT_BUILDER_LAYOUT: BuilderLayout = {
left: 22,
artboard: 56,
right: 22,
};
export const mapPanelLayoutToBuilderLayout = (layout: Layout): BuilderLayout => {
const left = layout.left;
const artboard = layout.artboard;
const right = layout.right;
if (typeof left !== "number" || typeof artboard !== "number" || typeof right !== "number")
return DEFAULT_BUILDER_LAYOUT;
return { left, artboard, right };
};
export const parseBuilderLayoutCookie = (value?: string | null): BuilderLayout => {
if (!value) return DEFAULT_BUILDER_LAYOUT;
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) return DEFAULT_BUILDER_LAYOUT;
if (typeof parsed !== "object" || parsed === null) return DEFAULT_BUILDER_LAYOUT;
const left = (parsed as { left?: unknown }).left;
const artboard = (parsed as { artboard?: unknown }).artboard;
const right = (parsed as { right?: unknown }).right;
if (typeof left !== "number" || typeof artboard !== "number" || typeof right !== "number")
return DEFAULT_BUILDER_LAYOUT;
return { left, artboard, right };
} catch {
return DEFAULT_BUILDER_LAYOUT;
}
};
interface BuilderSidebarState {
layout: BuilderLayout;
leftSidebar: PanelImperativeHandle | null;
rightSidebar: PanelImperativeHandle | null;
}
interface BuilderSidebarActions {
setLayout: (layout: BuilderLayout) => void;
setLeftSidebar: (ref: PanelImperativeHandle | null) => void;
setRightSidebar: (ref: PanelImperativeHandle | null) => void;
}
type BuilderSidebar = BuilderSidebarState & BuilderSidebarActions;
export const useBuilderSidebarStore = create<BuilderSidebar>((set) => ({
layout: DEFAULT_BUILDER_LAYOUT,
leftSidebar: null,
rightSidebar: null,
setLayout: (layout) => set({ layout }),
setLeftSidebar: (ref) => set({ leftSidebar: ref }),
setRightSidebar: (ref) => set({ rightSidebar: ref }),
}));
type UseBuilderSidebarReturn = {
maxSidebarSize: string | number;
collapsedSidebarSize: number;
isCollapsed: (side: "left" | "right") => boolean;
toggleSidebar: (side: "left" | "right", forceState?: boolean) => void;
};
export function useBuilderSidebar<T = UseBuilderSidebarReturn>(selector?: (builder: UseBuilderSidebarReturn) => T): T {
const isMobile = useIsMobile();
const { width } = useWindowSize();
const maxSidebarSize = useMemo((): string | number => {
if (!width) return 0;
return isMobile ? "95%" : "45%";
}, [width, isMobile]);
const collapsedSidebarSize = useMemo((): number => {
if (!width) return 0;
return isMobile ? 0 : 48;
}, [width, isMobile]);
const expandSize = useMemo(() => (isMobile ? "95%" : "30%"), [isMobile]);
const isCollapsed = useCallback((side: "left" | "right") => {
const sidebar =
side === "left"
? useBuilderSidebarStore.getState().leftSidebar?.current
: useBuilderSidebarStore.getState().rightSidebar?.current;
if (!sidebar) return false;
return sidebar.isCollapsed();
}, []);
const toggleSidebar = useCallback(
(side: "left" | "right", forceState?: boolean) => {
const sidebar =
side === "left"
? useBuilderSidebarStore.getState().leftSidebar?.current
: useBuilderSidebarStore.getState().rightSidebar?.current;
if (!sidebar) return;
const shouldExpand = forceState === undefined ? sidebar.isCollapsed() : forceState;
if (shouldExpand) sidebar.resize(expandSize);
else sidebar.collapse();
},
[expandSize],
);
const state = useMemo(() => {
return {
maxSidebarSize,
collapsedSidebarSize,
isCollapsed,
toggleSidebar,
};
}, [maxSidebarSize, collapsedSidebarSize, isCollapsed, toggleSidebar]);
return selector ? selector(state) : (state as T);
}