create-nativecore 0.1.1 → 0.2.0

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 (175) hide show
  1. package/README.md +6 -14
  2. package/bin/index.mjs +402 -431
  3. package/package.json +3 -2
  4. package/template/.env.example +28 -0
  5. package/template/.htmlhintrc +14 -0
  6. package/template/api/data/dashboard.json +11 -0
  7. package/template/api/data/users.json +18 -0
  8. package/template/api/mockApi.js +161 -0
  9. package/template/assets/icon.svg +13 -0
  10. package/template/assets/logo.svg +25 -0
  11. package/template/eslint.config.js +94 -0
  12. package/template/index.html +137 -0
  13. package/template/manifest.json +19 -0
  14. package/template/public/.well-known/security.txt +9 -0
  15. package/template/public/_headers +24 -0
  16. package/template/public/_redirects +14 -0
  17. package/template/public/assets/icon.svg +13 -0
  18. package/template/public/assets/logo.svg +25 -0
  19. package/template/public/manifest.json +19 -0
  20. package/template/public/robots.txt +13 -0
  21. package/template/public/sitemap.xml +27 -0
  22. package/template/scripts/build-for-bots.mjs +121 -0
  23. package/template/scripts/convert-to-ts.mjs +106 -0
  24. package/template/scripts/fix-encoding.mjs +38 -0
  25. package/template/scripts/fix-svg-paths.mjs +32 -0
  26. package/template/scripts/generate-cf-router.mjs +52 -0
  27. package/template/scripts/inject-dev-tools.mjs +41 -0
  28. package/template/scripts/inject-version.mjs +65 -0
  29. package/template/scripts/make-component.mjs +445 -0
  30. package/template/scripts/make-component.mjs.backup +432 -0
  31. package/template/scripts/make-controller.mjs +119 -0
  32. package/template/scripts/make-core-component.mjs +303 -0
  33. package/template/scripts/make-view.mjs +346 -0
  34. package/template/scripts/minify.mjs +71 -0
  35. package/template/scripts/prepare-static-assets.mjs +141 -0
  36. package/template/scripts/prompt-bot-build.mjs +223 -0
  37. package/template/scripts/remove-component.mjs +170 -0
  38. package/template/scripts/remove-core-component.mjs +156 -0
  39. package/template/scripts/remove-dev.mjs +13 -0
  40. package/template/scripts/remove-view.mjs +200 -0
  41. package/template/scripts/strip-dev-blocks.mjs +30 -0
  42. package/template/scripts/watch-compile.mjs +69 -0
  43. package/template/server.js +1066 -0
  44. package/template/src/app.ts +115 -0
  45. package/template/src/components/appRegistry.ts +8 -0
  46. package/template/src/components/core/app-footer.ts +27 -0
  47. package/template/src/components/core/app-header.ts +175 -0
  48. package/template/src/components/core/app-sidebar.ts +238 -0
  49. package/template/src/components/core/loading-spinner.ts +25 -0
  50. package/template/src/components/core/nc-a.ts +313 -0
  51. package/template/src/components/core/nc-accordion.ts +186 -0
  52. package/template/src/components/core/nc-alert.ts +153 -0
  53. package/template/src/components/core/nc-animation.ts +1150 -0
  54. package/template/src/components/core/nc-autocomplete.ts +271 -0
  55. package/template/src/components/core/nc-avatar-group.ts +113 -0
  56. package/template/src/components/core/nc-avatar.ts +148 -0
  57. package/template/src/components/core/nc-badge.ts +86 -0
  58. package/template/src/components/core/nc-bottom-nav.ts +214 -0
  59. package/template/src/components/core/nc-breadcrumb.ts +96 -0
  60. package/template/src/components/core/nc-button.ts +307 -0
  61. package/template/src/components/core/nc-card.ts +160 -0
  62. package/template/src/components/core/nc-checkbox.ts +282 -0
  63. package/template/src/components/core/nc-chip.ts +115 -0
  64. package/template/src/components/core/nc-code.ts +314 -0
  65. package/template/src/components/core/nc-collapsible.ts +154 -0
  66. package/template/src/components/core/nc-color-picker.ts +268 -0
  67. package/template/src/components/core/nc-copy-button.ts +119 -0
  68. package/template/src/components/core/nc-date-picker.ts +443 -0
  69. package/template/src/components/core/nc-div.ts +280 -0
  70. package/template/src/components/core/nc-divider.ts +81 -0
  71. package/template/src/components/core/nc-drawer.ts +230 -0
  72. package/template/src/components/core/nc-dropdown.ts +178 -0
  73. package/template/src/components/core/nc-empty-state.ts +134 -0
  74. package/template/src/components/core/nc-file-upload.ts +354 -0
  75. package/template/src/components/core/nc-form.ts +312 -0
  76. package/template/src/components/core/nc-image.ts +184 -0
  77. package/template/src/components/core/nc-input.ts +383 -0
  78. package/template/src/components/core/nc-kbd.ts +48 -0
  79. package/template/src/components/core/nc-menu-item.ts +193 -0
  80. package/template/src/components/core/nc-menu.ts +376 -0
  81. package/template/src/components/core/nc-modal.ts +238 -0
  82. package/template/src/components/core/nc-nav-item.ts +151 -0
  83. package/template/src/components/core/nc-number-input.ts +350 -0
  84. package/template/src/components/core/nc-otp-input.ts +235 -0
  85. package/template/src/components/core/nc-pagination.ts +178 -0
  86. package/template/src/components/core/nc-popover.ts +260 -0
  87. package/template/src/components/core/nc-progress-circular.ts +119 -0
  88. package/template/src/components/core/nc-progress.ts +134 -0
  89. package/template/src/components/core/nc-radio.ts +235 -0
  90. package/template/src/components/core/nc-rating.ts +266 -0
  91. package/template/src/components/core/nc-rich-text.ts +283 -0
  92. package/template/src/components/core/nc-scroll-top.ts +116 -0
  93. package/template/src/components/core/nc-select.ts +452 -0
  94. package/template/src/components/core/nc-skeleton.ts +107 -0
  95. package/template/src/components/core/nc-slider.ts +285 -0
  96. package/template/src/components/core/nc-snackbar.ts +230 -0
  97. package/template/src/components/core/nc-splash.ts +343 -0
  98. package/template/src/components/core/nc-stepper.ts +247 -0
  99. package/template/src/components/core/nc-switch.ts +281 -0
  100. package/template/src/components/core/nc-tab-item.ts +138 -0
  101. package/template/src/components/core/nc-table.ts +279 -0
  102. package/template/src/components/core/nc-tabs.ts +554 -0
  103. package/template/src/components/core/nc-tag-input.ts +279 -0
  104. package/template/src/components/core/nc-textarea.ts +216 -0
  105. package/template/src/components/core/nc-time-picker.ts +438 -0
  106. package/template/src/components/core/nc-timeline.ts +186 -0
  107. package/template/src/components/core/nc-tooltip.ts +143 -0
  108. package/template/src/components/frameworkRegistry.ts +68 -0
  109. package/template/src/components/preloadRegistry.ts +28 -0
  110. package/template/src/components/registry.ts +8 -0
  111. package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
  112. package/template/src/constants/apiEndpoints.ts +27 -0
  113. package/template/src/constants/errorMessages.ts +23 -0
  114. package/template/src/constants/index.ts +8 -0
  115. package/template/src/constants/routePaths.ts +15 -0
  116. package/template/src/constants/storageKeys.ts +18 -0
  117. package/template/src/controllers/dashboard.controller.ts +200 -0
  118. package/template/src/controllers/home.controller.ts +21 -0
  119. package/template/src/controllers/index.ts +11 -0
  120. package/template/src/controllers/login.controller.ts +131 -0
  121. package/template/src/core/component.ts +354 -0
  122. package/template/src/core/errorHandler.ts +85 -0
  123. package/template/src/core/gpu-animation.ts +604 -0
  124. package/template/src/core/http.ts +173 -0
  125. package/template/src/core/lazyComponents.ts +90 -0
  126. package/template/src/core/router.ts +642 -0
  127. package/template/src/core/signals.ts +146 -0
  128. package/template/src/core/state.ts +248 -0
  129. package/template/src/dev/component-editor.ts +1363 -0
  130. package/template/src/dev/component-overlay.ts +278 -0
  131. package/template/src/dev/context-menu.ts +223 -0
  132. package/template/src/dev/denc-tools.ts +250 -0
  133. package/template/src/dev/hmr.ts +189 -0
  134. package/template/src/dev/nfbs.code-workspace +27 -0
  135. package/template/src/dev/outline-panel.ts +1247 -0
  136. package/template/src/middleware/auth.middleware.ts +23 -0
  137. package/template/src/routes/routes.ts +38 -0
  138. package/template/src/services/api.service.ts +394 -0
  139. package/template/src/services/auth.service.ts +176 -0
  140. package/template/src/services/index.ts +8 -0
  141. package/template/src/services/logger.service.ts +74 -0
  142. package/template/src/services/storage.service.ts +88 -0
  143. package/template/src/stores/appStore.ts +57 -0
  144. package/template/src/stores/uiStore.ts +36 -0
  145. package/template/src/styles/core-variables.css +219 -0
  146. package/template/src/styles/core.css +710 -0
  147. package/template/src/styles/main.css +3164 -0
  148. package/template/src/styles/variables.css +152 -0
  149. package/template/src/types/global.d.ts +47 -0
  150. package/template/src/utils/cacheBuster.ts +20 -0
  151. package/template/src/utils/dom.ts +149 -0
  152. package/template/src/utils/events.ts +203 -0
  153. package/template/src/utils/form.ts +176 -0
  154. package/template/src/utils/formatters.ts +169 -0
  155. package/template/src/utils/helpers.ts +195 -0
  156. package/template/src/utils/markdown.ts +307 -0
  157. package/template/src/utils/sidebar.ts +96 -0
  158. package/template/src/utils/smoothScroll.ts +85 -0
  159. package/template/src/utils/templates.ts +23 -0
  160. package/template/src/utils/validation.ts +73 -0
  161. package/template/src/views/protected/dashboard.html +293 -0
  162. package/template/src/views/public/home.html +150 -0
  163. package/template/src/views/public/login.html +102 -0
  164. package/template/tests/unit/component.test.ts +87 -0
  165. package/template/tests/unit/computed.test.ts +79 -0
  166. package/template/tests/unit/form.test.ts +68 -0
  167. package/template/tests/unit/formatters.test.ts +49 -0
  168. package/template/tests/unit/lazy-components.test.ts +59 -0
  169. package/template/tests/unit/markdown.test.ts +62 -0
  170. package/template/tests/unit/router.test.ts +112 -0
  171. package/template/tests/unit/signals.test.ts +54 -0
  172. package/template/tests/unit/validation.test.ts +50 -0
  173. package/template/tsconfig.build.json +21 -0
  174. package/template/tsconfig.json +51 -0
  175. package/template/vitest.config.ts +36 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * NativeCore Dev Tools
3
+ *
4
+ * SECURITY: This module is ONLY loaded in development mode (localhost).
5
+ * It is completely excluded from production builds via tsconfig.build.json
6
+ *
7
+ * Features:
8
+ * - Component overlay with gear icons on hover
9
+ * - Live edit modal for component properties
10
+ * - Direct file modification via dev server API
11
+ * - Real-time updates via HMR
12
+ */
13
+
14
+ import { ComponentOverlay } from './component-overlay.js';
15
+ import { ComponentEditor } from './component-editor.js';
16
+ import { OutlinePanel } from './outline-panel.js';
17
+
18
+ class DevTools {
19
+ private static readonly TOGGLE_STORAGE_KEY = 'nativecore-devtools-visible';
20
+ private overlay: ComponentOverlay | null = null;
21
+ private editor: ComponentEditor | null = null;
22
+ private outlinePanel: OutlinePanel | null = null;
23
+ private indicator: HTMLButtonElement | null = null;
24
+ private enabled: boolean = false;
25
+ private overlayVisible: boolean = true;
26
+
27
+ /**
28
+ * Initialize dev tools - only works on localhost
29
+ */
30
+ init(): void {
31
+ // SECURITY: Triple-check we're in dev mode
32
+ if (!this.isDevEnvironment()) {
33
+ console.warn('[DevTools] Not in dev environment, refusing to initialize');
34
+ return;
35
+ }
36
+
37
+ // console.log('[DevTools] Initializing NativeCore Dev Tools...');
38
+
39
+ this.overlay = new ComponentOverlay(this.onEditComponent.bind(this));
40
+ this.editor = new ComponentEditor();
41
+ this.outlinePanel = new OutlinePanel(this.onEditComponent.bind(this));
42
+ this.enabled = true;
43
+ this.overlayVisible = this.loadOverlayVisibilityPreference();
44
+ this.overlay.setVisible(this.overlayVisible);
45
+
46
+ // Add dev mode indicator
47
+ this.addDevIndicator();
48
+
49
+ // console.log('[DevTools] Ready - hover over components to see edit options');
50
+ }
51
+
52
+ /**
53
+ * Check if we're in a development environment
54
+ */
55
+ private isDevEnvironment(): boolean {
56
+ const hostname = window.location.hostname;
57
+ const isLocalhost = hostname === 'localhost' ||
58
+ hostname === '127.0.0.1' ||
59
+ hostname.startsWith('192.168.') ||
60
+ hostname.endsWith('.local');
61
+
62
+ // Also check for explicit dev flag
63
+ const hasDevFlag = (window as any).__NATIVECORE_DEV__ === true;
64
+
65
+ return isLocalhost || hasDevFlag;
66
+ }
67
+
68
+ /**
69
+ * Handle edit request from overlay
70
+ */
71
+ private async onEditComponent(element: HTMLElement, fromOutlineTree: boolean = false): Promise<void> {
72
+ if (!this.editor) return;
73
+
74
+ const tagName = element.tagName.toLowerCase();
75
+ // console.log(`[DevTools] Opening editor for <${tagName}>`);
76
+
77
+ try {
78
+ // Fetch component metadata from server
79
+ const metadata = await this.fetchComponentMetadata(tagName);
80
+
81
+ // Open editor modal
82
+ this.editor.open(element, metadata);
83
+
84
+ // Only expand outline panel if clicked from page (not from tree)
85
+ if (this.outlinePanel && !fromOutlineTree) {
86
+ this.outlinePanel.showAndExpandTo(element);
87
+ }
88
+ } catch (error) {
89
+ console.error('[DevTools] Failed to open editor:', error);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Fetch component file and parse metadata
95
+ */
96
+ private async fetchComponentMetadata(tagName: string): Promise<ComponentMetadata> {
97
+ const response = await fetch(`/api/dev/component/${tagName}`);
98
+
99
+ if (!response.ok) {
100
+ throw new Error(`Failed to fetch metadata for ${tagName}`);
101
+ }
102
+
103
+ return response.json();
104
+ }
105
+
106
+ /**
107
+ * Add visual indicator that dev mode is active
108
+ */
109
+ private addDevIndicator(): void {
110
+ this.indicator?.remove();
111
+
112
+ const indicator = document.createElement('button');
113
+ indicator.id = 'nativecore-denc-indicator';
114
+ indicator.type = 'button';
115
+ indicator.setAttribute('aria-pressed', String(this.overlayVisible));
116
+ indicator.innerHTML = `
117
+ <style>
118
+ #nativecore-denc-indicator {
119
+ position: fixed;
120
+ bottom: 20px;
121
+ left: 50%;
122
+ transform: translateX(calc(-50% + 120px));
123
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
124
+ color: white;
125
+ padding: 6px 12px;
126
+ border-radius: 20px;
127
+ font-size: 11px;
128
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
129
+ font-weight: 600;
130
+ z-index: 100;
131
+ box-shadow: 0 2px 10px rgba(0,0,0,0.2);
132
+ transition: opacity 0.2s, transform 0.2s, box-shadow 0.2s;
133
+ opacity: 0.95;
134
+ border: none;
135
+ cursor: pointer;
136
+ }
137
+
138
+ #nativecore-denc-indicator:hover {
139
+ transform: translateX(calc(-50% + 120px)) translateY(-1px);
140
+ box-shadow: 0 6px 18px rgba(0,0,0,0.22);
141
+ }
142
+
143
+ #nativecore-denc-indicator.is-off {
144
+ background: linear-gradient(135deg, #475569 0%, #334155 100%);
145
+ }
146
+ </style>
147
+ DEV MODE: ${this.overlayVisible ? 'ON' : 'OFF'}
148
+ `;
149
+ indicator.classList.toggle('is-off', !this.overlayVisible);
150
+ indicator.addEventListener('click', () => this.toggleOverlayVisibility());
151
+
152
+ document.body.appendChild(indicator);
153
+ this.indicator = indicator;
154
+ }
155
+
156
+ private loadOverlayVisibilityPreference(): boolean {
157
+ try {
158
+ return localStorage.getItem(DevTools.TOGGLE_STORAGE_KEY) !== 'false';
159
+ } catch {
160
+ return true;
161
+ }
162
+ }
163
+
164
+ private saveOverlayVisibilityPreference(): void {
165
+ try {
166
+ localStorage.setItem(DevTools.TOGGLE_STORAGE_KEY, String(this.overlayVisible));
167
+ } catch {
168
+ // Ignore storage failures in dev mode.
169
+ }
170
+ }
171
+
172
+ private toggleOverlayVisibility(): void {
173
+ this.overlayVisible = !this.overlayVisible;
174
+
175
+ if (!this.overlayVisible) {
176
+ document.dispatchEvent(new CustomEvent('nc-close-editor'));
177
+ }
178
+
179
+ this.overlay?.setVisible(this.overlayVisible);
180
+ this.outlinePanel?.setVisible(this.overlayVisible);
181
+ this.saveOverlayVisibilityPreference();
182
+ this.addDevIndicator();
183
+ }
184
+
185
+ /**
186
+ * Disable dev tools
187
+ */
188
+ destroy(): void {
189
+ if (this.overlay) {
190
+ this.overlay.destroy();
191
+ }
192
+ if (this.editor) {
193
+ this.editor.destroy();
194
+ }
195
+ if (this.outlinePanel) {
196
+ this.outlinePanel.destroy();
197
+ }
198
+ // Use dom.query for consistency
199
+ const indicator = (window.dom?.query?.('#nativecore-denc-indicator') || document.getElementById('nativecore-denc-indicator')) as HTMLElement;
200
+ if (indicator) {
201
+ indicator.remove();
202
+ }
203
+ this.indicator = null;
204
+ this.enabled = false;
205
+ }
206
+ }
207
+
208
+ export interface ComponentMetadata {
209
+ tagName: string;
210
+ filePath: string;
211
+ absoluteFilePath?: string;
212
+ className: string;
213
+ attributes: AttributeInfo[];
214
+ cssVariables: CssVariableInfo[];
215
+ slots: SlotInfo[];
216
+ sourceCode: string;
217
+ }
218
+
219
+ export interface AttributeInfo {
220
+ name: string;
221
+ type: 'string' | 'number' | 'boolean';
222
+ defaultValue: string;
223
+ currentValue: string;
224
+ line: number;
225
+ }
226
+
227
+ export interface CssVariableInfo {
228
+ name: string;
229
+ defaultValue: string;
230
+ currentValue: string;
231
+ line: number;
232
+ }
233
+
234
+ export interface SlotInfo {
235
+ name: string;
236
+ content: string;
237
+ }
238
+
239
+ // Export singleton
240
+ export const devTools = new DevTools();
241
+
242
+ // Auto-initialize if in dev mode
243
+ if (typeof window !== 'undefined') {
244
+ // Wait for DOM ready
245
+ if (document.readyState === 'loading') {
246
+ document.addEventListener('DOMContentLoaded', () => devTools.init());
247
+ } else {
248
+ devTools.init();
249
+ }
250
+ }
@@ -0,0 +1,189 @@
1
+ // src/dev/hmr.ts
2
+ // NativeCore HMR client (development only)
3
+
4
+ // Extend Window interface for HMR
5
+ declare global {
6
+ interface Window {
7
+ __hmrWebSocket?: WebSocket;
8
+ }
9
+ }
10
+
11
+ if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
12
+ // Restore scroll position after HMR reload
13
+ const savedScroll = sessionStorage.getItem('hmr_scroll_pos');
14
+ if (savedScroll !== null) {
15
+ // Wait for router to load the page, then restore scroll
16
+ window.addEventListener('pageloaded', () => {
17
+ setTimeout(() => {
18
+ window.scrollTo(0, parseInt(savedScroll, 10));
19
+ sessionStorage.removeItem('hmr_scroll_pos');
20
+ console.warn('🔥 Scroll position restored:', savedScroll);
21
+ }, 100);
22
+ }, { once: true });
23
+
24
+ // Fallback if pageloaded doesn't fire
25
+ setTimeout(() => {
26
+ const scrollPos = sessionStorage.getItem('hmr_scroll_pos');
27
+ if (scrollPos) {
28
+ window.scrollTo(0, parseInt(scrollPos, 10));
29
+ sessionStorage.removeItem('hmr_scroll_pos');
30
+ }
31
+ }, 500);
32
+ }
33
+
34
+ // Prevent multiple HMR connections
35
+ if (window.__hmrWebSocket) {
36
+ console.warn('[HMR] Connection already exists, skipping...');
37
+ } else {
38
+ const ws = new WebSocket('ws://localhost:8001');
39
+ window.__hmrWebSocket = ws;
40
+ let isConnected = false;
41
+
42
+ // Show HMR status indicator
43
+ function showHMRIndicator(status: 'connected' | 'disconnected' | 'updating', message?: string): void {
44
+ let indicator = document.getElementById('hmr-indicator');
45
+
46
+ if (!indicator) {
47
+ indicator = document.createElement('div');
48
+ indicator.id = 'hmr-indicator';
49
+ indicator.style.cssText = `
50
+ position: fixed;
51
+ bottom: 20px;
52
+ left: 50%;
53
+ transform: translateX(calc(-50% - 96px));
54
+ padding: 6px 12px;
55
+ background: #000;
56
+ color: #fff;
57
+ border-radius: 20px;
58
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
59
+ font-size: 11px;
60
+ font-weight: 600;
61
+ z-index: 999999;
62
+ opacity: 1;
63
+ transition: all 0.3s;
64
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
65
+ pointer-events: none;
66
+ line-height: 1.2;
67
+ `;
68
+ document.body.appendChild(indicator);
69
+ }
70
+
71
+ const colors: Record<string, string> = {
72
+ connected: '#10b981',
73
+ disconnected: '#ef4444',
74
+ updating: '#f59e0b'
75
+ };
76
+
77
+ const icons: Record<string, string> = {
78
+ connected: '[HMR]',
79
+ disconnected: '[DISCONNECTED]',
80
+ updating: '[UPDATING]'
81
+ };
82
+
83
+ indicator.textContent = `${icons[status]} ${message || status.toUpperCase()}`;
84
+ indicator.style.background = colors[status] || '#000';
85
+ }
86
+
87
+ function flashHMRIndicator(): void {
88
+ const indicator = document.getElementById('hmr-indicator');
89
+ if (indicator) {
90
+ indicator.style.transform = 'translateX(calc(-50% - 96px)) scale(1.08)';
91
+ setTimeout(() => {
92
+ indicator.style.transform = 'translateX(calc(-50% - 96px))';
93
+ }, 200);
94
+ }
95
+ }
96
+
97
+ // Hot reload CSS
98
+ async function hotReloadCSS(file: string): Promise<void> {
99
+ const links = document.querySelectorAll('link[rel="stylesheet"]');
100
+ let reloaded = false;
101
+
102
+ links.forEach(link => {
103
+ const href = link.getAttribute('href');
104
+ if (href && href.includes(file)) {
105
+ const newHref = href.split('?')[0] + '?' + Date.now();
106
+ (link as HTMLLinkElement).href = newHref;
107
+ reloaded = true;
108
+ }
109
+ });
110
+
111
+ if (reloaded) {
112
+ console.warn(`CSS hot reloaded: ${file}`);
113
+ flashHMRIndicator();
114
+ }
115
+ }
116
+
117
+ // Hot reload JavaScript (full reload for now, can optimize later)
118
+ async function hotReloadJS(file: string): Promise<void> {
119
+ console.warn(`Reloading JS: ${file}`);
120
+ showHMRIndicator('updating', 'Reloading...');
121
+
122
+ // For now, do a full reload
123
+ // Future: implement module-level HMR
124
+ setTimeout(() => {
125
+ location.reload();
126
+ }, 100);
127
+ }
128
+
129
+ // Connection established
130
+ ws.onopen = () => {
131
+ isConnected = true;
132
+ console.warn('[HMR] enabled - Live reload active');
133
+ showHMRIndicator('connected', 'HMR Ready');
134
+ };
135
+
136
+ // Receive updates from server
137
+ ws.onmessage = async (event) => {
138
+ try {
139
+ const { type, file } = JSON.parse(event.data);
140
+
141
+ if (type === 'file-changed') {
142
+
143
+ // Handle different file types
144
+ if (file.endsWith('.css')) {
145
+ await hotReloadCSS(file);
146
+ } else if (file.endsWith('.js')) {
147
+ await hotReloadJS(file);
148
+ } else if (file.endsWith('.ts')) {
149
+ // TypeScript changed - just reload the page (compilation already done by server)
150
+ console.warn(`♻️ TS changed: ${file}`);
151
+ showHMRIndicator('updating', 'Reloading...');
152
+ sessionStorage.setItem('hmr_scroll_pos', window.scrollY.toString());
153
+ setTimeout(() => location.reload(), 100);
154
+ } else if (file.endsWith('.html')) {
155
+ // For HTML changes - just reload the page (simple and reliable)
156
+ console.warn(`♻️ HTML changed: ${file}`);
157
+ showHMRIndicator('updating', 'Reloading...');
158
+
159
+ // Save scroll position before reload
160
+ sessionStorage.setItem('hmr_scroll_pos', window.scrollY.toString());
161
+
162
+ setTimeout(() => location.reload(), 100);
163
+ }
164
+ }
165
+ } catch (error) {
166
+ console.error('🔥 HMR error:', error);
167
+ }
168
+ };
169
+
170
+ // Connection lost
171
+ ws.onclose = () => {
172
+ if (isConnected) {
173
+ console.warn('🔥 HMR disconnected - Server may have restarted');
174
+ showHMRIndicator('disconnected', 'Reconnecting...');
175
+
176
+ // Try to reconnect after 1 second
177
+ setTimeout(() => {
178
+ location.reload();
179
+ }, 1000);
180
+ }
181
+ };
182
+
183
+ ws.onerror = (error) => {
184
+ console.error('[HMR] connection error:', error);
185
+ };
186
+ } // end of else block for single HMR connection
187
+ }
188
+
189
+ export {};
@@ -0,0 +1,27 @@
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": "../.."
5
+ }
6
+ ],
7
+ "settings": {
8
+ "liveServer.settings.file": "index.html",
9
+ "liveServer.settings.mount": [
10
+ [
11
+ "/",
12
+ "./"
13
+ ]
14
+ ],
15
+ "liveServer.settings.useWebExt": false,
16
+ "liveServer.settings.ignoreFiles": [
17
+ ".vscode/**",
18
+ "**/*.scss",
19
+ "**/*.sass"
20
+ ],
21
+ "liveServer.settings.proxy": {
22
+ "enable": false
23
+ },
24
+ "liveServer.settings.CustomBrowser": null,
25
+ "liveServer.settings.ChromeDebuggingAttachment": false
26
+ }
27
+ }