@imksh/create 1.0.30 → 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/src/utils/askTemplate.js +2 -1
  3. package/templates/layers/electron/base/electron/main.js +262 -0
  4. package/templates/layers/electron/base/electron/preload.js +37 -0
  5. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  6. package/templates/layers/electron/javascript/_gitignore +38 -0
  7. package/templates/layers/electron/javascript/package.json +49 -0
  8. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  9. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  10. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  11. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  12. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  13. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  14. package/templates/layers/electron/javascript/vite.config.js +29 -0
  15. package/templates/layers/electron/typescript/_gitignore +38 -0
  16. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  17. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  18. package/templates/layers/electron/typescript/package.json +52 -0
  19. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  20. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  21. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  22. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  23. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  24. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  25. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  26. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  27. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  28. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  29. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  30. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  31. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  32. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  33. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  34. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  35. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  36. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  37. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  38. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  39. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  40. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  41. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  42. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  43. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  44. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  45. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  46. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  47. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  48. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  49. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  50. package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -0
  51. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  52. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  53. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  54. package/templates/layers/next/typescript/src/components/common/Table.tsx +201 -35
  55. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  56. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  57. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  58. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  59. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +1 -0
  60. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  61. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  62. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  63. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  64. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  65. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  66. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  67. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  68. package/templates/layers/node/typescript/package-lock.json +2 -2
  69. package/templates/layers/node/typescript/package.json +1 -1
  70. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  71. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  72. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  73. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  74. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  75. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  76. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  77. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  78. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  79. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  80. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  81. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  82. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  83. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  84. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  85. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  86. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  87. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  88. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  89. package/templates/layers/react/typescript/src/components/common/Table.tsx +197 -34
  90. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  91. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  92. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  93. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  94. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  95. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  96. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  97. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  98. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  99. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  100. package/templates/presets/electron/preset.json +9 -0
  101. package/templates/presets/electron-ts/preset.json +9 -0
  102. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
@@ -0,0 +1,120 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Minus, Square, X, Maximize2, Minimize2 } from "lucide-react";
3
+ import { useUiStore } from "../../store/useUiStore";
4
+ import appConfig from "../../config/appConfig";
5
+
6
+ type Platform = "darwin" | "win32" | "linux" | "web";
7
+
8
+ /**
9
+ * TitleBar — custom frameless window title bar for Electron.
10
+ *
11
+ * Renders a draggable title bar with:
12
+ * - App icon + name centered
13
+ * - Window controls (minimize / maximize / close) per platform:
14
+ * · macOS: right side (native traffic lights hidden via frame: false)
15
+ * · Windows/Linux: right side (Windows style)
16
+ *
17
+ * Only renders when running inside Electron (window.electronAPI present).
18
+ */
19
+ const TitleBar: React.FC = () => {
20
+ const { windowMaximized, setWindowMaximized, minimizeWindow, maximizeWindow, closeWindow } =
21
+ useUiStore();
22
+ const [platform, setPlatform] = useState<Platform>("win32");
23
+
24
+ const isElectron = Boolean(window.electronAPI);
25
+ if (!isElectron) return null;
26
+
27
+ useEffect(() => {
28
+ window.electronAPI.getPlatform().then((p) => setPlatform(p as Platform));
29
+ window.electronAPI.isMaximized().then(setWindowMaximized);
30
+ window.electronAPI.onMaximizeChange((isMax) => setWindowMaximized(isMax));
31
+
32
+ return () => {
33
+ window.electronAPI.removeAllListeners("window:maximized-change");
34
+ };
35
+ }, []);
36
+
37
+ const isMac = platform === "darwin";
38
+
39
+ const windowControls = (
40
+ <div className={`flex items-center gap-1.5 ${isMac ? "mr-3" : "ml-auto"}`}>
41
+ {/* Minimize */}
42
+ <button
43
+ id="titlebar-minimize"
44
+ onClick={minimizeWindow}
45
+ className="w-7 h-7 flex items-center justify-center rounded-full
46
+ text-base-content/50 hover:bg-warning/20 hover:text-warning
47
+ transition-all duration-150 group"
48
+ title="Minimize"
49
+ >
50
+ <Minus size={12} className="group-hover:scale-110 transition-transform" />
51
+ </button>
52
+
53
+ {/* Maximize / Restore */}
54
+ <button
55
+ id="titlebar-maximize"
56
+ onClick={maximizeWindow}
57
+ className="w-7 h-7 flex items-center justify-center rounded-full
58
+ text-base-content/50 hover:bg-success/20 hover:text-success
59
+ transition-all duration-150 group"
60
+ title={windowMaximized ? "Restore" : "Maximize"}
61
+ >
62
+ {windowMaximized ? (
63
+ <Minimize2 size={11} className="group-hover:scale-110 transition-transform" />
64
+ ) : (
65
+ <Maximize2 size={11} className="group-hover:scale-110 transition-transform" />
66
+ )}
67
+ </button>
68
+
69
+ {/* Close */}
70
+ <button
71
+ id="titlebar-close"
72
+ onClick={closeWindow}
73
+ className="w-7 h-7 flex items-center justify-center rounded-full
74
+ text-base-content/50 hover:bg-error/20 hover:text-error
75
+ transition-all duration-150 group"
76
+ title="Close"
77
+ >
78
+ <X size={12} className="group-hover:scale-110 transition-transform" />
79
+ </button>
80
+ </div>
81
+ );
82
+
83
+ return (
84
+ <div
85
+ id="electron-titlebar"
86
+ className="h-10 shrink-0 flex items-center px-3
87
+ bg-base-200 border-b border-base-300
88
+ select-none"
89
+ style={{ WebkitAppRegion: "drag" } as React.CSSProperties}
90
+ >
91
+ {isMac && (
92
+ <div style={{ WebkitAppRegion: "no-drag" } as React.CSSProperties}>
93
+ {windowControls}
94
+ </div>
95
+ )}
96
+
97
+ {/* App name — centered */}
98
+ <div className="flex items-center gap-2 mx-auto">
99
+ {appConfig.app.logo ? (
100
+ <img src={appConfig.app.logo} alt="" className="w-4 h-4 rounded" />
101
+ ) : (
102
+ <div className="w-4 h-4 rounded bg-primary flex items-center justify-center text-primary-content text-[8px] font-bold">
103
+ {appConfig.app.name.charAt(0)}
104
+ </div>
105
+ )}
106
+ <span className="text-xs font-semibold text-base-content/70">
107
+ {appConfig.app.name}
108
+ </span>
109
+ </div>
110
+
111
+ {!isMac && (
112
+ <div style={{ WebkitAppRegion: "no-drag" } as React.CSSProperties}>
113
+ {windowControls}
114
+ </div>
115
+ )}
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export default TitleBar;
@@ -0,0 +1,52 @@
1
+ const appConfig = {
2
+ app: {
3
+ name: "Electron App",
4
+ logo: "./logo.png",
5
+ shortName: "App",
6
+ description: "An Electron application built with @imksh/create",
7
+ version: "1.0.0",
8
+ author: "imksh",
9
+ },
10
+
11
+ api: {
12
+ baseURL: import.meta.env.VITE_API_URL as string,
13
+ timeout: 30000,
14
+ },
15
+
16
+ routes: {
17
+ home: "/",
18
+ login: "/login",
19
+ profile: "/profile",
20
+ },
21
+
22
+ pagination: {
23
+ pageSize: 10,
24
+ },
25
+
26
+ github: {
27
+ repository: "",
28
+ },
29
+
30
+ social: {
31
+ website: "",
32
+ linkedin: "",
33
+ github: "",
34
+ twitter: "",
35
+ },
36
+
37
+ features: {
38
+ darkMode: true,
39
+ notifications: true,
40
+ sidebar: true,
41
+ electron: true,
42
+ customTitleBar: true,
43
+ },
44
+
45
+ electron: {
46
+ tray: true,
47
+ autoUpdater: false,
48
+ },
49
+ } as const;
50
+
51
+ export type AppConfig = typeof appConfig;
52
+ export default appConfig;
@@ -0,0 +1,68 @@
1
+ import { useEffect } from "react";
2
+
3
+ type Platform = "darwin" | "win32" | "linux" | "web";
4
+
5
+ interface UseElectronReturn {
6
+ isElectron: boolean;
7
+ minimize: () => void;
8
+ maximize: () => void;
9
+ close: () => void;
10
+ isMaximized: () => Promise<boolean>;
11
+ getVersion: () => Promise<string>;
12
+ getPlatform: () => Promise<Platform>;
13
+ openExternal: (url: string) => Promise<void>;
14
+ onMaximizeChange: (cb: (isMax: boolean) => void) => void;
15
+ onMenuAction: (cb: (data: { label: string }) => void) => void;
16
+ removeAllListeners: (channel: string) => void;
17
+ }
18
+
19
+ /**
20
+ * useElectron — safely access window.electronAPI in the renderer.
21
+ *
22
+ * Returns typed wrappers for all IPC calls. Every method is a no-op
23
+ * (or returns a resolved promise) when running outside of Electron.
24
+ */
25
+ export function useElectron(): UseElectronReturn {
26
+ const isElectron = typeof window !== "undefined" && Boolean(window.electronAPI);
27
+
28
+ return {
29
+ isElectron,
30
+ minimize: () => window.electronAPI?.minimize(),
31
+ maximize: () => window.electronAPI?.maximize(),
32
+ close: () => window.electronAPI?.close(),
33
+ isMaximized: () => window.electronAPI?.isMaximized() ?? Promise.resolve(false),
34
+ getVersion: () => window.electronAPI?.getVersion() ?? Promise.resolve("web"),
35
+ getPlatform: () => (window.electronAPI?.getPlatform() ?? Promise.resolve("web")) as Promise<Platform>,
36
+ openExternal: (url) => window.electronAPI?.openExternal(url) ?? Promise.resolve(),
37
+ onMaximizeChange: (cb) => window.electronAPI?.onMaximizeChange(cb),
38
+ onMenuAction: (cb) => window.electronAPI?.onMenuAction(cb),
39
+ removeAllListeners: (channel) => window.electronAPI?.removeAllListeners(channel),
40
+ };
41
+ }
42
+
43
+ type ElectronEvent = "maximize" | "menu";
44
+
45
+ /**
46
+ * useElectronEvent — subscribe to an Electron IPC event in a React component.
47
+ * Automatically cleans up the listener on unmount.
48
+ */
49
+ export function useElectronEvent(
50
+ event: ElectronEvent,
51
+ callback: (value: boolean | { label: string }) => void,
52
+ ): void {
53
+ const electron = useElectron();
54
+
55
+ useEffect(() => {
56
+ if (!electron.isElectron) return;
57
+
58
+ if (event === "maximize") {
59
+ electron.onMaximizeChange(callback as (v: boolean) => void);
60
+ return () => electron.removeAllListeners("window:maximized-change");
61
+ }
62
+
63
+ if (event === "menu") {
64
+ electron.onMenuAction(callback as (d: { label: string }) => void);
65
+ return () => electron.removeAllListeners("menu:preferences");
66
+ }
67
+ }, [event, electron.isElectron]);
68
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import { HashRouter } from "react-router-dom";
4
+ import App from "./App.js";
5
+ import "./index.css";
6
+
7
+ // HashRouter is required for Electron because the renderer is served via
8
+ // the file:// protocol in production, which breaks BrowserRouter's history API.
9
+ // Hash-based URLs (#/path) work correctly in both dev and packaged builds.
10
+
11
+ ReactDOM.createRoot(document.getElementById("root")!).render(
12
+ <React.StrictMode>
13
+ <HashRouter>
14
+ <App />
15
+ </HashRouter>
16
+ </React.StrictMode>,
17
+ );
@@ -0,0 +1,97 @@
1
+ import { create } from "zustand";
2
+
3
+ interface UiState {
4
+ // Sidebar
5
+ open: boolean;
6
+ collapsed: boolean;
7
+ isMobileMenuOpened: boolean;
8
+
9
+ // Modal
10
+ isModalOpen: boolean;
11
+
12
+ // Header
13
+ headerTitle: string;
14
+ headerActions: React.ReactNode | null;
15
+ breadcrumbs: React.ReactNode | null;
16
+ layoutClass: string;
17
+
18
+ // Theme
19
+ theme: string;
20
+
21
+ // Electron window
22
+ windowMaximized: boolean;
23
+
24
+ // Actions
25
+ setLayoutClass: (layoutClass: string) => void;
26
+ setHeaderTitle: (title: string) => void;
27
+ setHeaderActions: (actions: React.ReactNode | null) => void;
28
+ setBreadcrumbs: (breadcrumbs: React.ReactNode | null) => void;
29
+ toggleCollapsed: () => void;
30
+ setMobileMenuOpened: (open: boolean) => void;
31
+ toggleMobileMenuOpened: () => void;
32
+ toggleSidebar: () => void;
33
+ openSidebar: () => void;
34
+ close: () => void;
35
+ setModalOpen: (open: boolean) => void;
36
+ setTheme: (theme: string) => void;
37
+ toggleTheme: () => void;
38
+
39
+ // Electron window controls
40
+ setWindowMaximized: (value: boolean) => void;
41
+ minimizeWindow: () => void;
42
+ maximizeWindow: () => void;
43
+ closeWindow: () => void;
44
+ }
45
+
46
+ const useUiStore = create<UiState>((set, get) => ({
47
+ open: false,
48
+ isModalOpen: false,
49
+ collapsed: false,
50
+ isMobileMenuOpened: false,
51
+ headerTitle: "Dashboard",
52
+ headerActions: null,
53
+ breadcrumbs: null,
54
+ layoutClass: "",
55
+
56
+ // ── Layout helpers ──────────────────────────────────────────────────────────
57
+ setLayoutClass: (layoutClass) => set({ layoutClass }),
58
+ setHeaderTitle: (title) => set({ headerTitle: title }),
59
+ setHeaderActions: (actions) => set({ headerActions: actions }),
60
+ setBreadcrumbs: (breadcrumbs) => set({ breadcrumbs }),
61
+
62
+ // ── Sidebar ─────────────────────────────────────────────────────────────────
63
+ toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
64
+ setMobileMenuOpened: (open) => set({ isMobileMenuOpened: open }),
65
+ toggleMobileMenuOpened: () =>
66
+ set((state) => ({ isMobileMenuOpened: !state.isMobileMenuOpened })),
67
+ toggleSidebar: () => set((state) => ({ open: !state.open })),
68
+ openSidebar: () => set({ open: true }),
69
+ close: () => set({ open: false }),
70
+
71
+ // ── Modal ───────────────────────────────────────────────────────────────────
72
+ setModalOpen: (open) => set({ isModalOpen: open }),
73
+
74
+ // ── Theme ───────────────────────────────────────────────────────────────────
75
+ theme: localStorage.getItem("theme") || "light",
76
+ setTheme: (theme) => {
77
+ document.documentElement.setAttribute("data-theme", theme);
78
+ localStorage.setItem("theme", theme);
79
+ set({ theme });
80
+ },
81
+ toggleTheme: () => {
82
+ const nextTheme = get().theme === "light" ? "dark" : "light";
83
+ document.documentElement.setAttribute("data-theme", nextTheme);
84
+ localStorage.setItem("theme", nextTheme);
85
+ set({ theme: nextTheme });
86
+ },
87
+
88
+ // ── Electron window state ───────────────────────────────────────────────────
89
+ windowMaximized: false,
90
+ setWindowMaximized: (value) => set({ windowMaximized: value }),
91
+ minimizeWindow: () => window.electronAPI?.minimize(),
92
+ maximizeWindow: () => window.electronAPI?.maximize(),
93
+ closeWindow: () => window.electronAPI?.close(),
94
+ }));
95
+
96
+ export { useUiStore };
97
+ export type { UiState };
@@ -0,0 +1,13 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ import type { ElectronAPI } from '../electron/preload';
4
+
5
+ /**
6
+ * Augment the Window interface to include the Electron context bridge API.
7
+ * This gives full type safety when accessing window.electronAPI in the renderer.
8
+ */
9
+ declare global {
10
+ interface Window {
11
+ electronAPI: ElectronAPI;
12
+ }
13
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src", "electron"],
24
+ "references": [{ "path": "./tsconfig.node.json" }]
25
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true,
8
+ "strict": true
9
+ },
10
+ "include": ["vite.config.ts"]
11
+ }
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import tailwindcss from '@tailwindcss/vite';
4
+ import electron from 'vite-plugin-electron';
5
+ import renderer from 'vite-plugin-electron-renderer';
6
+
7
+ // https://vitejs.dev/config/
8
+ export default defineConfig({
9
+ plugins: [
10
+ react(),
11
+ tailwindcss(),
12
+ electron([
13
+ {
14
+ // Main process entry
15
+ entry: 'electron/main.ts',
16
+ },
17
+ {
18
+ // Preload script — reload renderer on change
19
+ entry: 'electron/preload.ts',
20
+ onstart(options) {
21
+ options.reload();
22
+ },
23
+ },
24
+ ]),
25
+ renderer(),
26
+ ],
27
+ // Critical: ensures built assets resolve correctly via file:// in production
28
+ base: './',
29
+ });
@@ -1,25 +1,63 @@
1
1
  "use client";
2
2
  import React from "react";
3
3
  import Link from "next/link";
4
- import { ChevronRight } from "lucide-react";
4
+ import { useRouter } from "next/navigation";
5
+ import { ArrowLeft, ChevronRight } from "lucide-react";
5
6
 
6
7
  export const Breadcrumbs = ({ items }) => {
8
+ const router = useRouter();
7
9
  return (
8
10
  <div className="flex items-center gap-1.5 text-sm font-bold text-base-content/50 flex-wrap">
11
+ <button
12
+ className="btn btn-primary btn-soft btn-square btn-sm mr-1 hidden md:flex"
13
+ onClick={() => router.back()}
14
+ >
15
+ <ArrowLeft size={18} />
16
+ </button>
9
17
  {items.map((item, idx) => {
10
18
  const isLast = idx === items.length - 1;
11
19
  return (
12
20
  <React.Fragment key={idx}>
13
21
  {item.path && !isLast ? (
14
- <Link href={item.path} className="hover:text-primary transition-colors">
22
+ <Link
23
+ href={item.path}
24
+ onClick={item.onClick}
25
+ className="hover:text-primary transition-colors hidden sm:flex items-center gap-1"
26
+ >
15
27
  {item.label}
16
28
  </Link>
17
- ) : (
18
- <span className={isLast ? "text-base font-black text-base-content tracking-tight" : ""}>
29
+ ) : item.onClick && !isLast ? (
30
+ <button
31
+ type="button"
32
+ onClick={item.onClick}
33
+ className="hover:text-primary transition-colors hidden sm:flex items-center gap-1"
34
+ >
19
35
  {item.label}
20
- </span>
36
+ </button>
37
+ ) : (
38
+ <div className="flex flex-col">
39
+ <span
40
+ className={
41
+ isLast
42
+ ? "text-lg md:text-xl font-bold leading-tight text-base-content tracking-tight truncate max-w-[200px] md:max-w-none"
43
+ : ""
44
+ }
45
+ >
46
+ {item.label}
47
+ </span>
48
+ {item.subtitle && (
49
+ <span className="text-xs text-base-content/60 font-normal truncate">
50
+ {item.subtitle}
51
+ </span>
52
+ )}
53
+ </div>
54
+ )}
55
+ {!isLast && (
56
+ <ChevronRight
57
+ size={14}
58
+ className="text-base-content/30 shrink-0 hidden sm:block"
59
+ />
21
60
  )}
22
- {!isLast && <ChevronRight size={14} className="text-base-content/30 shrink-0" />}
23
61
  </React.Fragment>
24
62
  );
25
63
  })}
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import React from "react";
2
3
  import LottieAnimation from "./LottieIcon";
3
4
  import loader from "../../assets/animations/loading.json";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { memo, useEffect, useRef } from "react";
2
3
  import { useInView } from "framer-motion";
3
4
  import lottie from "lottie-web";