create-nativecore 0.1.0 → 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 +10 -18
  2. package/bin/index.mjs +407 -489
  3. package/package.json +4 -3
  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,307 @@
1
+ /**
2
+ * Minimal markdown renderer for the NativeCore docs page.
3
+ *
4
+ * Scope:
5
+ * - headings (#, ##, ###, ####)
6
+ * - paragraphs
7
+ * - unordered / ordered lists
8
+ * - blockquotes
9
+ * - fenced code blocks
10
+ * - horizontal rules
11
+ * - inline code, links, bold, italics
12
+ *
13
+ * This intentionally avoids external runtime dependencies.
14
+ */
15
+
16
+ export interface MarkdownHeading {
17
+ level: number;
18
+ text: string;
19
+ id: string;
20
+ }
21
+
22
+ export interface MarkdownRenderResult {
23
+ html: string;
24
+ headings: MarkdownHeading[];
25
+ }
26
+
27
+ export interface MarkdownRenderOptions {
28
+ headingIdCounts?: Map<string, number>;
29
+ }
30
+
31
+ export interface MarkdownSection {
32
+ id: string;
33
+ title: string;
34
+ html: string;
35
+ headings: MarkdownHeading[];
36
+ isChapter: boolean;
37
+ aliases: string[];
38
+ }
39
+
40
+ function escapeHtml(input: string): string {
41
+ return input
42
+ .replace(/&/g, '&amp;')
43
+ .replace(/</g, '&lt;')
44
+ .replace(/>/g, '&gt;')
45
+ .replace(/"/g, '&quot;');
46
+ }
47
+
48
+ function slugify(text: string): string {
49
+ return text
50
+ .toLowerCase()
51
+ .trim()
52
+ .replace(/[^a-z0-9\s-]/g, '')
53
+ .replace(/\s+/g, '-')
54
+ .replace(/-+/g, '-');
55
+ }
56
+
57
+ function renderInline(text: string): string {
58
+ let out = escapeHtml(text);
59
+
60
+ out = out.replace(/`([^`]+)`/g, '<code>$1</code>');
61
+ out = out.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
62
+ out = out.replace(/\*([^*]+)\*/g, '<em>$1</em>');
63
+ out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
64
+
65
+ return out;
66
+ }
67
+
68
+ export function renderMarkdown(markdown: string, options: MarkdownRenderOptions = {}): MarkdownRenderResult {
69
+ const lines = markdown.replace(/\r\n/g, '\n').split('\n');
70
+ const html: string[] = [];
71
+ const headings: MarkdownHeading[] = [];
72
+ const headingIdCounts = options.headingIdCounts ?? new Map<string, number>();
73
+
74
+ let paragraphBuffer: string[] = [];
75
+ let inCodeBlock = false;
76
+ let codeLang = '';
77
+ let codeLines: string[] = [];
78
+ let inUl = false;
79
+ let inOl = false;
80
+ let inBlockquote = false;
81
+
82
+ const flushParagraph = () => {
83
+ if (paragraphBuffer.length === 0) return;
84
+ const text = paragraphBuffer.join(' ').trim();
85
+ if (text) html.push(`<p>${renderInline(text)}</p>`);
86
+ paragraphBuffer = [];
87
+ };
88
+
89
+ const closeLists = () => {
90
+ if (inUl) {
91
+ html.push('</ul>');
92
+ inUl = false;
93
+ }
94
+ if (inOl) {
95
+ html.push('</ol>');
96
+ inOl = false;
97
+ }
98
+ };
99
+
100
+ const closeBlockquote = () => {
101
+ if (inBlockquote) {
102
+ html.push('</blockquote>');
103
+ inBlockquote = false;
104
+ }
105
+ };
106
+
107
+ for (const line of lines) {
108
+ const trimmed = line.trim();
109
+
110
+ if (trimmed.startsWith('```')) {
111
+ flushParagraph();
112
+ closeLists();
113
+ closeBlockquote();
114
+
115
+ if (!inCodeBlock) {
116
+ inCodeBlock = true;
117
+ codeLang = trimmed.slice(3).trim();
118
+ codeLines = [];
119
+ } else {
120
+ const className = codeLang ? ` class="language-${escapeHtml(codeLang)}"` : '';
121
+ html.push(`<pre><code${className}>${escapeHtml(codeLines.join('\n'))}</code></pre>`);
122
+ inCodeBlock = false;
123
+ codeLang = '';
124
+ codeLines = [];
125
+ }
126
+ continue;
127
+ }
128
+
129
+ if (inCodeBlock) {
130
+ codeLines.push(line);
131
+ continue;
132
+ }
133
+
134
+ if (!trimmed) {
135
+ flushParagraph();
136
+ closeLists();
137
+ closeBlockquote();
138
+ continue;
139
+ }
140
+
141
+ const headingMatch = /^(#{1,4})\s+(.+)$/.exec(trimmed);
142
+ if (headingMatch) {
143
+ flushParagraph();
144
+ closeLists();
145
+ closeBlockquote();
146
+
147
+ const level = headingMatch[1].length;
148
+ const text = headingMatch[2].trim();
149
+ const baseId = slugify(text) || 'section';
150
+ const nextCount = (headingIdCounts.get(baseId) || 0) + 1;
151
+ headingIdCounts.set(baseId, nextCount);
152
+ const id = nextCount === 1 ? baseId : `${baseId}-${nextCount}`;
153
+ headings.push({ level, text, id });
154
+ html.push(`<h${level} id="${id}">${renderInline(text)}</h${level}>`);
155
+ continue;
156
+ }
157
+
158
+ if (/^---+$/.test(trimmed) || /^\*\*\*+$/.test(trimmed)) {
159
+ flushParagraph();
160
+ closeLists();
161
+ closeBlockquote();
162
+ html.push('<hr>');
163
+ continue;
164
+ }
165
+
166
+ const ulMatch = /^[-*]\s+(.+)$/.exec(trimmed);
167
+ if (ulMatch) {
168
+ flushParagraph();
169
+ closeBlockquote();
170
+ if (inOl) {
171
+ html.push('</ol>');
172
+ inOl = false;
173
+ }
174
+ if (!inUl) {
175
+ html.push('<ul>');
176
+ inUl = true;
177
+ }
178
+ html.push(`<li>${renderInline(ulMatch[1])}</li>`);
179
+ continue;
180
+ }
181
+
182
+ const olMatch = /^\d+\.\s+(.+)$/.exec(trimmed);
183
+ if (olMatch) {
184
+ flushParagraph();
185
+ closeBlockquote();
186
+ if (inUl) {
187
+ html.push('</ul>');
188
+ inUl = false;
189
+ }
190
+ if (!inOl) {
191
+ html.push('<ol>');
192
+ inOl = true;
193
+ }
194
+ html.push(`<li>${renderInline(olMatch[1])}</li>`);
195
+ continue;
196
+ }
197
+
198
+ const quoteMatch = /^>\s?(.*)$/.exec(trimmed);
199
+ if (quoteMatch) {
200
+ flushParagraph();
201
+ closeLists();
202
+ if (!inBlockquote) {
203
+ html.push('<blockquote>');
204
+ inBlockquote = true;
205
+ }
206
+ html.push(`<p>${renderInline(quoteMatch[1])}</p>`);
207
+ continue;
208
+ }
209
+
210
+ paragraphBuffer.push(trimmed);
211
+ }
212
+
213
+ flushParagraph();
214
+ closeLists();
215
+ closeBlockquote();
216
+
217
+ if (inCodeBlock) {
218
+ const className = codeLang ? ` class="language-${escapeHtml(codeLang)}"` : '';
219
+ html.push(`<pre><code${className}>${escapeHtml(codeLines.join('\n'))}</code></pre>`);
220
+ }
221
+
222
+ return {
223
+ html: html.join('\n'),
224
+ headings,
225
+ };
226
+ }
227
+
228
+ export function renderMarkdownToc(headings: MarkdownHeading[]): string {
229
+ const items = headings
230
+ .filter(heading => heading.level >= 2 && heading.level <= 4)
231
+ .map(heading => `
232
+ <a class="docs-toc__link docs-toc__link--h${heading.level}" href="#${heading.id}">${escapeHtml(heading.text)}</a>
233
+ `)
234
+ .join('');
235
+
236
+ return items || '<span class="docs-toc__empty">No headings found.</span>';
237
+ }
238
+
239
+ export function splitMarkdownIntoSections(markdown: string): MarkdownSection[] {
240
+ const lines = markdown.replace(/\r\n/g, '\n').split('\n');
241
+ const chapterLinkAliases = new Map<string, string>();
242
+ const chapterStarts = lines.reduce<Array<{ index: number; title: string }>>((sections, line, index) => {
243
+ const headingMatch = /^##\s+(.+)$/.exec(line.trim());
244
+ if (headingMatch && headingMatch[1].trim().startsWith('Chapter ')) {
245
+ sections.push({ index, title: headingMatch[1].trim() });
246
+ }
247
+ return sections;
248
+ }, []);
249
+
250
+ for (const match of markdown.matchAll(/\[[^\]]+\]\(#(chapter-(\d+)[^)]+)\)/g)) {
251
+ const alias = match[1];
252
+ const chapterNumber = match[2];
253
+ if (!chapterLinkAliases.has(chapterNumber)) {
254
+ chapterLinkAliases.set(chapterNumber, alias);
255
+ }
256
+ }
257
+
258
+ if (chapterStarts.length === 0) {
259
+ const rendered = renderMarkdown(markdown);
260
+ return [{
261
+ id: rendered.headings[0]?.id ?? 'overview',
262
+ title: rendered.headings.find(heading => heading.level <= 2)?.text ?? 'Overview',
263
+ html: rendered.html,
264
+ headings: rendered.headings,
265
+ isChapter: false,
266
+ aliases: [],
267
+ }];
268
+ }
269
+
270
+ const sharedHeadingIdCounts = new Map<string, number>();
271
+ const sections: MarkdownSection[] = [];
272
+ const overviewMarkdown = lines.slice(0, chapterStarts[0].index).join('\n').trim();
273
+
274
+ if (overviewMarkdown) {
275
+ const renderedOverview = renderMarkdown(overviewMarkdown, { headingIdCounts: sharedHeadingIdCounts });
276
+ sections.push({
277
+ id: 'overview',
278
+ title: 'Overview',
279
+ html: renderedOverview.html,
280
+ headings: renderedOverview.headings,
281
+ isChapter: false,
282
+ aliases: [],
283
+ });
284
+ }
285
+
286
+ chapterStarts.forEach((chapterStart, index) => {
287
+ const chapterEnd = chapterStarts[index + 1]?.index ?? lines.length;
288
+ const chapterMarkdown = lines.slice(chapterStart.index, chapterEnd).join('\n').trim();
289
+ const renderedChapter = renderMarkdown(chapterMarkdown, { headingIdCounts: sharedHeadingIdCounts });
290
+ const chapterHeading = renderedChapter.headings.find(heading => heading.level === 2) ?? renderedChapter.headings[0];
291
+ const chapterNumber = /^Chapter\s+(\d+)/i.exec(chapterHeading?.text ?? chapterStart.title)?.[1];
292
+ const chapterAlias = chapterNumber ? chapterLinkAliases.get(chapterNumber) : undefined;
293
+ const sectionId = chapterAlias ?? chapterHeading?.id ?? slugify(chapterStart.title);
294
+ const aliases = chapterHeading?.id && chapterHeading.id !== sectionId ? [chapterHeading.id] : [];
295
+
296
+ sections.push({
297
+ id: sectionId,
298
+ title: chapterHeading?.text ?? chapterStart.title,
299
+ html: renderedChapter.html,
300
+ headings: renderedChapter.headings,
301
+ isChapter: true,
302
+ aliases,
303
+ });
304
+ });
305
+
306
+ return sections;
307
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Sidebar Initialization and Management
3
+ */
4
+ import auth from '../services/auth.service.js';
5
+
6
+ /**
7
+ * Initialize sidebar functionality
8
+ */
9
+ export function initSidebar() {
10
+ const sidebar = document.getElementById('appSidebar');
11
+ const appLayout = document.querySelector('.app-layout');
12
+
13
+ // Listen for sidebar toggle events from nc-sidebar component
14
+ sidebar?.addEventListener('toggle', ((e: CustomEvent) => {
15
+ const isCollapsed = e.detail.collapsed;
16
+
17
+ if (appLayout) {
18
+ if (isCollapsed) {
19
+ appLayout.classList.add('sidebar-collapsed');
20
+ } else {
21
+ appLayout.classList.remove('sidebar-collapsed');
22
+ }
23
+ }
24
+
25
+ // Save state
26
+ localStorage.setItem('sidebar-collapsed', isCollapsed.toString());
27
+ }) as EventListener);
28
+
29
+ // Restore saved state on load
30
+ const savedCollapsed = localStorage.getItem('sidebar-collapsed') === 'true';
31
+ if (savedCollapsed && sidebar) {
32
+ sidebar.setAttribute('collapsed', '');
33
+ appLayout?.classList.add('sidebar-collapsed');
34
+ }
35
+
36
+ // Update sidebar visibility based on auth state
37
+ function updateSidebar() {
38
+ const isAuthenticated = auth.isAuthenticated();
39
+
40
+ // Toggle body class for sidebar visibility (no flash)
41
+ if (isAuthenticated) {
42
+ document.body.classList.add('sidebar-enabled');
43
+ if (appLayout) appLayout.classList.remove('no-sidebar');
44
+ } else {
45
+ document.body.classList.remove('sidebar-enabled');
46
+ if (appLayout) appLayout.classList.add('no-sidebar');
47
+ }
48
+
49
+ // Toggle menu items based on auth state
50
+ const homeLink = document.querySelector('.sidebar-item.home-link') as HTMLElement;
51
+ const aboutLink = document.querySelector('.sidebar-item.about-link') as HTMLElement;
52
+ const dashboardLink = document.querySelector('.sidebar-item.dashboard-link') as HTMLElement;
53
+ const componentsLink = document.querySelector('.sidebar-item.components-link') as HTMLElement;
54
+ const underConstructionLink = document.querySelector('.sidebar-item.under-construction-link') as HTMLElement;
55
+ const logoutLink = document.querySelector('.sidebar-item.logout-link') as HTMLElement;
56
+
57
+ if (homeLink) homeLink.style.display = isAuthenticated ? 'none' : 'flex';
58
+ if (aboutLink) aboutLink.style.display = isAuthenticated ? 'none' : 'flex';
59
+ if (dashboardLink) dashboardLink.style.display = isAuthenticated ? 'flex' : 'none';
60
+ if (componentsLink) componentsLink.style.display = isAuthenticated ? 'flex' : 'none';
61
+ if (underConstructionLink) underConstructionLink.style.display = isAuthenticated ? 'flex' : 'none';
62
+ if (logoutLink) logoutLink.style.display = isAuthenticated ? 'flex' : 'none';
63
+
64
+ // Update active link
65
+ updateActiveSidebarLink();
66
+ }
67
+
68
+ // Handle sidebar logout button
69
+ const sidebarLogoutBtn = document.getElementById('sidebarLogoutBtn');
70
+ if (sidebarLogoutBtn) {
71
+ sidebarLogoutBtn.addEventListener('click', () => {
72
+ auth.logout();
73
+ });
74
+ }
75
+
76
+ // Update active link on page load
77
+ function updateActiveSidebarLink() {
78
+ const currentPath = window.location.pathname;
79
+ const sidebarItems = document.querySelectorAll('.sidebar-item');
80
+
81
+ sidebarItems.forEach(item => {
82
+ item.classList.remove('active');
83
+ const href = item.getAttribute('href');
84
+ if (href === currentPath || (currentPath === '/' && href === '/')) {
85
+ item.classList.add('active');
86
+ }
87
+ });
88
+ }
89
+
90
+ // Listen for auth changes
91
+ window.addEventListener('auth-change', updateSidebar);
92
+ window.addEventListener('pageloaded', updateActiveSidebarLink);
93
+
94
+ // Initial update
95
+ updateSidebar();
96
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Smooth Scrolling Enhancement
3
+ * Makes mouse wheel scrolling smoother by intercepting wheel events
4
+ */
5
+
6
+ export function initSmoothScroll() {
7
+ // Only apply on desktop (not mobile)
8
+ if ('ontouchstart' in window) {
9
+ return;
10
+ }
11
+
12
+ let isScrolling = false;
13
+ let targetScrollY = window.scrollY;
14
+ let currentScrollY = window.scrollY;
15
+ const smoothness = 0.15; // Lower = smoother but slower
16
+
17
+ function smoothScrollStep() {
18
+ if (Math.abs(targetScrollY - currentScrollY) < 0.5) {
19
+ currentScrollY = targetScrollY;
20
+ isScrolling = false;
21
+ return;
22
+ }
23
+
24
+ currentScrollY += (targetScrollY - currentScrollY) * smoothness;
25
+ window.scrollTo(0, currentScrollY);
26
+ requestAnimationFrame(smoothScrollStep);
27
+ }
28
+
29
+ window.addEventListener('wheel', (e: WheelEvent) => {
30
+ e.preventDefault();
31
+
32
+ // Calculate new target scroll position
33
+ targetScrollY += e.deltaY;
34
+
35
+ // Clamp to valid range
36
+ const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
37
+ targetScrollY = Math.max(0, Math.min(targetScrollY, maxScroll));
38
+
39
+ // Start smooth scrolling animation if not already running
40
+ if (!isScrolling) {
41
+ isScrolling = true;
42
+ requestAnimationFrame(smoothScrollStep);
43
+ }
44
+ }, { passive: false });
45
+
46
+ // Handle keyboard scrolling
47
+ window.addEventListener('keydown', (e: KeyboardEvent) => {
48
+ const scrollAmount = 100;
49
+ const pageScrollAmount = window.innerHeight * 0.8;
50
+
51
+ switch (e.key) {
52
+ case 'ArrowDown':
53
+ e.preventDefault();
54
+ targetScrollY = Math.min(targetScrollY + scrollAmount, document.documentElement.scrollHeight - window.innerHeight);
55
+ break;
56
+ case 'ArrowUp':
57
+ e.preventDefault();
58
+ targetScrollY = Math.max(targetScrollY - scrollAmount, 0);
59
+ break;
60
+ case 'PageDown':
61
+ e.preventDefault();
62
+ targetScrollY = Math.min(targetScrollY + pageScrollAmount, document.documentElement.scrollHeight - window.innerHeight);
63
+ break;
64
+ case 'PageUp':
65
+ e.preventDefault();
66
+ targetScrollY = Math.max(targetScrollY - pageScrollAmount, 0);
67
+ break;
68
+ case 'Home':
69
+ e.preventDefault();
70
+ targetScrollY = 0;
71
+ break;
72
+ case 'End':
73
+ e.preventDefault();
74
+ targetScrollY = document.documentElement.scrollHeight - window.innerHeight;
75
+ break;
76
+ default:
77
+ return;
78
+ }
79
+
80
+ if (!isScrolling) {
81
+ isScrolling = true;
82
+ requestAnimationFrame(smoothScrollStep);
83
+ }
84
+ });
85
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Template Literal Helpers
3
+ *
4
+ * These are no-op functions that exist purely for syntax highlighting.
5
+ * They have ZERO runtime overhead - just pass through the string unchanged.
6
+ */
7
+
8
+ /**
9
+ * HTML template literal tag for syntax highlighting
10
+ * Usage: html`<div>content</div>`
11
+ *
12
+ * This does nothing at runtime - it's only for VS Code highlighting.
13
+ * Install "lit-html" extension for full HTML/CSS syntax highlighting.
14
+ */
15
+ export const html = (strings: TemplateStringsArray, ...values: any[]): string =>
16
+ String.raw({ raw: strings }, ...values);
17
+
18
+ /**
19
+ * CSS template literal tag for syntax highlighting
20
+ * Usage: css`.class { color: red; }`
21
+ */
22
+ export const css = (strings: TemplateStringsArray, ...values: any[]): string =>
23
+ String.raw({ raw: strings }, ...values);
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Validation Utilities
3
+ */
4
+
5
+ export function isValidEmail(email: string): boolean {
6
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
7
+ return emailRegex.test(email);
8
+ }
9
+
10
+ export function isRequired(value: any): boolean {
11
+ if (value === null || value === undefined) return false;
12
+ if (typeof value === 'string') return value.trim().length > 0;
13
+ return true;
14
+ }
15
+
16
+ export function minLength(min: number): (value: any) => boolean {
17
+ return (value: any) => {
18
+ if (!value) return false;
19
+ return String(value).length >= min;
20
+ };
21
+ }
22
+
23
+ export function maxLength(max: number): (value: any) => boolean {
24
+ return (value: any) => {
25
+ if (!value) return true;
26
+ return String(value).length <= max;
27
+ };
28
+ }
29
+
30
+ export function matchesPattern(pattern: RegExp): (value: any) => boolean {
31
+ return (value: any) => {
32
+ if (!value) return false;
33
+ return pattern.test(String(value));
34
+ };
35
+ }
36
+
37
+ export function isNumber(value: any): boolean {
38
+ return !isNaN(parseFloat(value)) && isFinite(value);
39
+ }
40
+
41
+ export function isInteger(value: any): boolean {
42
+ return Number.isInteger(Number(value));
43
+ }
44
+
45
+ export function isValidURL(url: string): boolean {
46
+ try {
47
+ new URL(url);
48
+ return true;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ export function isValidDate(date: any): boolean {
55
+ return date instanceof Date && !isNaN(date.getTime());
56
+ }
57
+
58
+ type Validator = (value: any) => boolean;
59
+
60
+ export function validateForm(values: Record<string, any>, rules: Record<string, Validator[]>): Record<string, string> {
61
+ const errors: Record<string, string> = {};
62
+
63
+ for (const [field, validators] of Object.entries(rules)) {
64
+ for (const validator of validators) {
65
+ if (!validator(values[field])) {
66
+ errors[field] = `Validation failed for ${field}`;
67
+ break;
68
+ }
69
+ }
70
+ }
71
+
72
+ return errors;
73
+ }