@imksh/create 1.0.30 → 1.0.32

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 (247) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/commands/create.js +2 -0
  4. package/src/index.js +1 -1
  5. package/src/utils/askTemplate.js +2 -1
  6. package/templates/layers/electron/base/electron/main.js +262 -0
  7. package/templates/layers/electron/base/electron/preload.js +37 -0
  8. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  9. package/templates/layers/electron/javascript/_gitignore +38 -0
  10. package/templates/layers/electron/javascript/package.json +49 -0
  11. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  12. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  13. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  14. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  15. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  16. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  17. package/templates/layers/electron/javascript/vite.config.js +29 -0
  18. package/templates/layers/electron/typescript/_gitignore +38 -0
  19. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  20. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  21. package/templates/layers/electron/typescript/package.json +52 -0
  22. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  23. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  24. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  25. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  26. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  27. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  28. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  29. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  30. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  31. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  32. package/templates/layers/next/javascript/package.json +8 -2
  33. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  34. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  35. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  36. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  37. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  38. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  39. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  40. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  41. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  42. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  43. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  44. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  45. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  46. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  47. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  48. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  49. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  50. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  51. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  52. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  53. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  54. package/templates/layers/next/typescript/package.json +14 -3
  55. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  56. package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
  57. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  58. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  59. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  60. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  61. package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
  62. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  63. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  64. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  65. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  66. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
  67. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  68. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  69. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  70. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  71. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  72. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  73. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  74. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  75. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  76. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  77. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  78. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  79. package/templates/layers/node/javascript/.env.example +3 -1
  80. package/templates/layers/node/javascript/package.json +12 -3
  81. package/templates/layers/node/javascript/src/app.js +2 -1
  82. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  83. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  84. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  85. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  86. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  87. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  88. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  89. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  90. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  91. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  92. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  93. package/templates/layers/node/typescript/.env.example +3 -1
  94. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  95. package/templates/layers/node/typescript/package-lock.json +2 -2
  96. package/templates/layers/node/typescript/package.json +17 -3
  97. package/templates/layers/node/typescript/src/app.ts +3 -2
  98. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  99. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  100. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  101. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  102. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  103. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  104. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  105. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  106. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  107. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  108. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  109. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  110. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  111. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  112. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  113. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  114. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  115. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  116. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  117. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  118. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  119. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  120. package/templates/layers/node-prisma/javascript/package.json +11 -2
  121. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  122. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  123. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  124. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  125. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  126. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  127. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  128. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  129. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  130. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  131. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  132. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  133. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  134. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  135. package/templates/layers/node-prisma/typescript/package.json +19 -5
  136. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  137. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  138. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  139. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  140. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  141. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  142. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  143. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  144. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  145. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  146. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  147. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  148. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  149. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  150. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  151. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  152. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  153. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  154. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  155. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  156. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  157. package/templates/layers/prisma/javascript/package.json +8 -0
  158. package/templates/layers/prisma/typescript/package.json +14 -1
  159. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  160. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  161. package/templates/layers/react/javascript/package.json +6 -0
  162. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  163. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  164. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  165. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  166. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  167. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  168. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  169. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  170. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  171. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  172. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  173. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  174. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  175. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  176. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  177. package/templates/layers/react/typescript/package.json +11 -2
  178. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  179. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  180. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  181. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  182. package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
  183. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  184. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  185. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  186. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  187. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  188. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  189. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  190. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  191. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  192. package/templates/layers/react/typescript/src/main.tsx +11 -11
  193. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  194. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  195. package/templates/layers/react-native/base/app.json +9 -22
  196. package/templates/layers/react-native/base/eslint.config.js +5 -1
  197. package/templates/layers/react-native/base/package.json +11 -1
  198. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  199. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  200. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  201. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  202. package/templates/layers/react-native/base/src/app/_layout.tsx +3 -3
  203. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
  204. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  205. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -0
  206. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  207. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  208. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  209. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
  210. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  211. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  212. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  213. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  214. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  215. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  216. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  217. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  218. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  219. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  220. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  221. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  222. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  223. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  224. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  225. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  226. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  227. package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
  228. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  229. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  230. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  231. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  232. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  233. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  234. package/templates/presets/electron/preset.json +9 -0
  235. package/templates/presets/electron-ts/preset.json +9 -0
  236. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  237. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  238. package/templates/presets/production-fullstack/overrides/mobile/package.json +11 -1
  239. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  240. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  241. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  242. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  243. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  244. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
  245. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  246. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  247. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -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
+ });
@@ -6,7 +6,11 @@
6
6
  "dev": "next dev",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
- "lint": "next lint"
9
+ "lint": "next lint",
10
+ "lint:fix": "next lint --fix",
11
+ "format": "prettier --write \"src/**/*.{js,jsx,css,json}\"",
12
+ "format:check": "prettier --check \"src/**/*.{js,jsx,css,json}\"",
13
+ "circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.{js,jsx}\""
10
14
  },
11
15
  "dependencies": {
12
16
  "@ducanh2912/next-pwa": "latest",
@@ -27,7 +31,9 @@
27
31
  "zustand": "^4.5.2"
28
32
  },
29
33
  "devDependencies": {
34
+ "dpdm": "^3.14.0",
30
35
  "eslint": "^10.8.1",
31
- "eslint-config-next": "^16.3.0"
36
+ "eslint-config-next": "^16.3.0",
37
+ "prettier": "^3.4.2"
32
38
  }
33
39
  }
@@ -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";