devfolio-page 0.1.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 (115) hide show
  1. package/README.md +219 -0
  2. package/dist/cli/commands/init.js +282 -0
  3. package/dist/cli/commands/render.js +105 -0
  4. package/dist/cli/commands/themes.js +40 -0
  5. package/dist/cli/commands/validate.js +86 -0
  6. package/dist/cli/helpers/validate.js +99 -0
  7. package/dist/cli/index.js +51 -0
  8. package/dist/cli/postinstall.js +20 -0
  9. package/dist/cli/schemas/portfolio.schema.js +299 -0
  10. package/dist/generator/builder.js +551 -0
  11. package/dist/generator/markdown.js +57 -0
  12. package/dist/generator/themes/dark-academia/partials/education.html +15 -0
  13. package/dist/generator/themes/dark-academia/partials/experience.html +23 -0
  14. package/dist/generator/themes/dark-academia/partials/hero.html +15 -0
  15. package/dist/generator/themes/dark-academia/partials/projects.html +17 -0
  16. package/dist/generator/themes/dark-academia/partials/skills.html +11 -0
  17. package/dist/generator/themes/dark-academia/partials/writing.html +15 -0
  18. package/dist/generator/themes/dark-academia/script.js +91 -0
  19. package/dist/generator/themes/dark-academia/styles.css +913 -0
  20. package/dist/generator/themes/dark-academia/template.html +46 -0
  21. package/dist/generator/themes/dark-academia/templates/experiments-index.html +80 -0
  22. package/dist/generator/themes/dark-academia/templates/homepage.html +125 -0
  23. package/dist/generator/themes/dark-academia/templates/project.html +101 -0
  24. package/dist/generator/themes/dark-academia/templates/projects-index.html +80 -0
  25. package/dist/generator/themes/dark-academia/templates/writing-index.html +75 -0
  26. package/dist/generator/themes/modern/partials/education.html +14 -0
  27. package/dist/generator/themes/modern/partials/experience.html +21 -0
  28. package/dist/generator/themes/modern/partials/hero.html +15 -0
  29. package/dist/generator/themes/modern/partials/projects.html +17 -0
  30. package/dist/generator/themes/modern/partials/skills.html +11 -0
  31. package/dist/generator/themes/modern/partials/writing.html +14 -0
  32. package/dist/generator/themes/modern/script.js +136 -0
  33. package/dist/generator/themes/modern/styles.css +835 -0
  34. package/dist/generator/themes/modern/template.html +59 -0
  35. package/dist/generator/themes/modern/templates/experiments-index.html +78 -0
  36. package/dist/generator/themes/modern/templates/homepage.html +125 -0
  37. package/dist/generator/themes/modern/templates/project.html +98 -0
  38. package/dist/generator/themes/modern/templates/projects-index.html +79 -0
  39. package/dist/generator/themes/modern/templates/writing-index.html +73 -0
  40. package/dist/generator/themes/srcl/partials/education.html +27 -0
  41. package/dist/generator/themes/srcl/partials/experience.html +25 -0
  42. package/dist/generator/themes/srcl/partials/hero.html +22 -0
  43. package/dist/generator/themes/srcl/partials/projects.html +24 -0
  44. package/dist/generator/themes/srcl/partials/sections/code.html +8 -0
  45. package/dist/generator/themes/srcl/partials/sections/demo.html +8 -0
  46. package/dist/generator/themes/srcl/partials/sections/gallery.html +12 -0
  47. package/dist/generator/themes/srcl/partials/sections/image.html +6 -0
  48. package/dist/generator/themes/srcl/partials/sections/interactive.html +8 -0
  49. package/dist/generator/themes/srcl/partials/sections/metrics.html +10 -0
  50. package/dist/generator/themes/srcl/partials/sections/outcomes.html +5 -0
  51. package/dist/generator/themes/srcl/partials/sections/overview.html +5 -0
  52. package/dist/generator/themes/srcl/partials/sections/process.html +5 -0
  53. package/dist/generator/themes/srcl/partials/skills.html +21 -0
  54. package/dist/generator/themes/srcl/partials/writing.html +14 -0
  55. package/dist/generator/themes/srcl/script.js +354 -0
  56. package/dist/generator/themes/srcl/styles.css +1260 -0
  57. package/dist/generator/themes/srcl/template.html +46 -0
  58. package/dist/generator/themes/srcl/templates/experiments-index.html +66 -0
  59. package/dist/generator/themes/srcl/templates/homepage.html +136 -0
  60. package/dist/generator/themes/srcl/templates/project.html +96 -0
  61. package/dist/generator/themes/srcl/templates/projects-index.html +70 -0
  62. package/dist/generator/themes/srcl/templates/writing-index.html +61 -0
  63. package/dist/types/portfolio.js +4 -0
  64. package/package.json +58 -0
  65. package/src/generator/themes/dark-academia/partials/education.html +15 -0
  66. package/src/generator/themes/dark-academia/partials/experience.html +23 -0
  67. package/src/generator/themes/dark-academia/partials/hero.html +15 -0
  68. package/src/generator/themes/dark-academia/partials/projects.html +17 -0
  69. package/src/generator/themes/dark-academia/partials/skills.html +11 -0
  70. package/src/generator/themes/dark-academia/partials/writing.html +15 -0
  71. package/src/generator/themes/dark-academia/script.js +91 -0
  72. package/src/generator/themes/dark-academia/styles.css +913 -0
  73. package/src/generator/themes/dark-academia/template.html +46 -0
  74. package/src/generator/themes/dark-academia/templates/experiments-index.html +80 -0
  75. package/src/generator/themes/dark-academia/templates/homepage.html +125 -0
  76. package/src/generator/themes/dark-academia/templates/project.html +101 -0
  77. package/src/generator/themes/dark-academia/templates/projects-index.html +80 -0
  78. package/src/generator/themes/dark-academia/templates/writing-index.html +75 -0
  79. package/src/generator/themes/modern/partials/education.html +14 -0
  80. package/src/generator/themes/modern/partials/experience.html +21 -0
  81. package/src/generator/themes/modern/partials/hero.html +15 -0
  82. package/src/generator/themes/modern/partials/projects.html +17 -0
  83. package/src/generator/themes/modern/partials/skills.html +11 -0
  84. package/src/generator/themes/modern/partials/writing.html +14 -0
  85. package/src/generator/themes/modern/script.js +136 -0
  86. package/src/generator/themes/modern/styles.css +835 -0
  87. package/src/generator/themes/modern/template.html +59 -0
  88. package/src/generator/themes/modern/templates/experiments-index.html +78 -0
  89. package/src/generator/themes/modern/templates/homepage.html +125 -0
  90. package/src/generator/themes/modern/templates/project.html +98 -0
  91. package/src/generator/themes/modern/templates/projects-index.html +79 -0
  92. package/src/generator/themes/modern/templates/writing-index.html +73 -0
  93. package/src/generator/themes/srcl/partials/education.html +27 -0
  94. package/src/generator/themes/srcl/partials/experience.html +25 -0
  95. package/src/generator/themes/srcl/partials/hero.html +22 -0
  96. package/src/generator/themes/srcl/partials/projects.html +24 -0
  97. package/src/generator/themes/srcl/partials/sections/code.html +8 -0
  98. package/src/generator/themes/srcl/partials/sections/demo.html +8 -0
  99. package/src/generator/themes/srcl/partials/sections/gallery.html +12 -0
  100. package/src/generator/themes/srcl/partials/sections/image.html +6 -0
  101. package/src/generator/themes/srcl/partials/sections/interactive.html +8 -0
  102. package/src/generator/themes/srcl/partials/sections/metrics.html +10 -0
  103. package/src/generator/themes/srcl/partials/sections/outcomes.html +5 -0
  104. package/src/generator/themes/srcl/partials/sections/overview.html +5 -0
  105. package/src/generator/themes/srcl/partials/sections/process.html +5 -0
  106. package/src/generator/themes/srcl/partials/skills.html +21 -0
  107. package/src/generator/themes/srcl/partials/writing.html +14 -0
  108. package/src/generator/themes/srcl/script.js +354 -0
  109. package/src/generator/themes/srcl/styles.css +1260 -0
  110. package/src/generator/themes/srcl/template.html +46 -0
  111. package/src/generator/themes/srcl/templates/experiments-index.html +66 -0
  112. package/src/generator/themes/srcl/templates/homepage.html +136 -0
  113. package/src/generator/themes/srcl/templates/project.html +96 -0
  114. package/src/generator/themes/srcl/templates/projects-index.html +70 -0
  115. package/src/generator/themes/srcl/templates/writing-index.html +61 -0
@@ -0,0 +1,6 @@
1
+ <figure class="content-section section-image">
2
+ <img src="{{src}}" alt="{{alt}}" loading="lazy">
3
+ {{#caption}}
4
+ <figcaption>{{caption}}</figcaption>
5
+ {{/caption}}
6
+ </figure>
@@ -0,0 +1,8 @@
1
+ <section class="content-section section-interactive">
2
+ {{#title}}
3
+ <h3 class="section-subtitle">{{title}}</h3>
4
+ {{/title}}
5
+ <div class="interactive-embed">
6
+ <iframe src="{{url}}" frameborder="0" loading="lazy" allowfullscreen></iframe>
7
+ </div>
8
+ </section>
@@ -0,0 +1,10 @@
1
+ <section class="content-section section-metrics">
2
+ <div class="metrics-grid">
3
+ {{#data}}
4
+ <div class="metric">
5
+ <span class="metric-value">{{value}}</span>
6
+ <span class="metric-label">{{label}}</span>
7
+ </div>
8
+ {{/data}}
9
+ </div>
10
+ </section>
@@ -0,0 +1,5 @@
1
+ <section class="content-section section-outcomes">
2
+ <div class="section-content prose">
3
+ {{{content_html}}}
4
+ </div>
5
+ </section>
@@ -0,0 +1,5 @@
1
+ <section class="content-section section-overview">
2
+ <div class="section-content prose">
3
+ {{{content_html}}}
4
+ </div>
5
+ </section>
@@ -0,0 +1,5 @@
1
+ <section class="content-section section-process">
2
+ <div class="section-content prose">
3
+ {{{content_html}}}
4
+ </div>
5
+ </section>
@@ -0,0 +1,21 @@
1
+ <section id="skills" class="section">
2
+ <h2 class="section-title">SKILLS</h2>
3
+ <div class="srcl-data-table">
4
+ <table>
5
+ <thead>
6
+ <tr>
7
+ <th>CATEGORY</th>
8
+ <th>TECHNOLOGIES</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ {{#skillCategories}}
13
+ <tr>
14
+ <td class="category">{{category}}</td>
15
+ <td class="skills">{{skills}}</td>
16
+ </tr>
17
+ {{/skillCategories}}
18
+ </tbody>
19
+ </table>
20
+ </div>
21
+ </section>
@@ -0,0 +1,14 @@
1
+ <section id="writing" class="section">
2
+ <h2 class="section-title">WRITING</h2>
3
+ <ul class="srcl-list writing-list">
4
+ {{#articles}}
5
+ <li class="writing-item">
6
+ <div class="writing-header">
7
+ <a href="{{url}}" target="_blank" class="writing-title">{{title}}</a>
8
+ <span class="writing-date">{{date}}</span>
9
+ </div>
10
+ {{#description}}<p class="writing-description">{{description}}</p>{{/description}}
11
+ </li>
12
+ {{/articles}}
13
+ </ul>
14
+ </section>
@@ -0,0 +1,354 @@
1
+ /**
2
+ * SRCL Theme Interactive Features
3
+ * Vanilla JavaScript - No dependencies
4
+ */
5
+
6
+ (function() {
7
+ 'use strict';
8
+
9
+ // ============================================
10
+ // Accordion Functionality
11
+ // ============================================
12
+ function initAccordions() {
13
+ const headers = document.querySelectorAll('.accordion-header');
14
+
15
+ headers.forEach(header => {
16
+ header.addEventListener('click', () => {
17
+ const item = header.parentElement;
18
+ const isExpanded = item.dataset.expanded === 'true';
19
+
20
+ // Close all accordions in the same container
21
+ const container = item.parentElement;
22
+ container.querySelectorAll('.accordion-item').forEach(i => {
23
+ i.dataset.expanded = 'false';
24
+ i.querySelector('.accordion-header')?.setAttribute('aria-expanded', 'false');
25
+ });
26
+
27
+ // Open this one if it was closed
28
+ if (!isExpanded) {
29
+ item.dataset.expanded = 'true';
30
+ header.setAttribute('aria-expanded', 'true');
31
+ }
32
+ });
33
+
34
+ // Keyboard support
35
+ header.addEventListener('keydown', (e) => {
36
+ if (e.key === 'Enter' || e.key === ' ') {
37
+ e.preventDefault();
38
+ header.click();
39
+ }
40
+ });
41
+ });
42
+ }
43
+
44
+ // ============================================
45
+ // Smooth Scroll Navigation
46
+ // ============================================
47
+ function initSmoothScroll() {
48
+ document.querySelectorAll('a[href^="#"]').forEach(link => {
49
+ link.addEventListener('click', (e) => {
50
+ const href = link.getAttribute('href');
51
+ if (href === '#') return;
52
+
53
+ const target = document.querySelector(href);
54
+ if (target) {
55
+ e.preventDefault();
56
+
57
+ // Account for fixed action bar height
58
+ const actionBar = document.querySelector('.srcl-action-bar');
59
+ const offset = actionBar ? actionBar.offsetHeight + 16 : 16;
60
+
61
+ const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
62
+
63
+ window.scrollTo({
64
+ top: targetPosition,
65
+ behavior: 'smooth'
66
+ });
67
+
68
+ // Update URL hash without jumping
69
+ history.pushState(null, '', href);
70
+ }
71
+ });
72
+ });
73
+ }
74
+
75
+ // ============================================
76
+ // Theme Toggle (Dark/Light)
77
+ // ============================================
78
+ function toggleTheme() {
79
+ const current = document.body.dataset.theme || document.documentElement.dataset.theme || 'dark';
80
+ const newTheme = current === 'dark' ? 'light' : 'dark';
81
+
82
+ // Set on both html and body for CSS compatibility
83
+ document.documentElement.dataset.theme = newTheme;
84
+ document.body.dataset.theme = newTheme;
85
+
86
+ // Persist preference
87
+ try {
88
+ localStorage.setItem('srcl-theme', newTheme);
89
+ } catch (e) {
90
+ // localStorage not available
91
+ }
92
+
93
+ // Announce change for screen readers
94
+ announceChange(`Theme changed to ${newTheme} mode`);
95
+ }
96
+
97
+ function loadThemePreference() {
98
+ try {
99
+ const saved = localStorage.getItem('srcl-theme');
100
+ if (saved) {
101
+ // Set on both html and body for CSS compatibility
102
+ document.documentElement.dataset.theme = saved;
103
+ document.body.dataset.theme = saved;
104
+ }
105
+ } catch (e) {
106
+ // localStorage not available
107
+ }
108
+ }
109
+
110
+ // ============================================
111
+ // Grid Toggle
112
+ // ============================================
113
+ function toggleGrid() {
114
+ const grid = document.querySelector('.grid-overlay');
115
+ if (grid) {
116
+ const isVisible = grid.style.display !== 'none';
117
+ grid.style.display = isVisible ? 'none' : 'block';
118
+
119
+ // Persist preference
120
+ try {
121
+ localStorage.setItem('srcl-grid', isVisible ? 'hidden' : 'visible');
122
+ } catch (e) {
123
+ // localStorage not available
124
+ }
125
+
126
+ announceChange(`Grid overlay ${isVisible ? 'hidden' : 'visible'}`);
127
+ }
128
+ }
129
+
130
+ function loadGridPreference() {
131
+ try {
132
+ const saved = localStorage.getItem('srcl-grid');
133
+ const grid = document.querySelector('.grid-overlay');
134
+ if (grid && saved === 'hidden') {
135
+ grid.style.display = 'none';
136
+ }
137
+ } catch (e) {
138
+ // localStorage not available
139
+ }
140
+ }
141
+
142
+ // ============================================
143
+ // Keyboard Shortcuts
144
+ // ============================================
145
+ function initKeyboardShortcuts() {
146
+ document.addEventListener('keydown', (e) => {
147
+ // Don't trigger shortcuts when typing in inputs
148
+ if (e.target.matches('input, textarea, select')) return;
149
+
150
+ // Ctrl+T or Cmd+T: Toggle theme
151
+ if ((e.ctrlKey || e.metaKey) && e.key === 't') {
152
+ e.preventDefault();
153
+ toggleTheme();
154
+ }
155
+
156
+ // Ctrl+G or Cmd+G: Toggle grid
157
+ if ((e.ctrlKey || e.metaKey) && e.key === 'g') {
158
+ e.preventDefault();
159
+ toggleGrid();
160
+ }
161
+
162
+ // Number keys 1-9: Jump to sections
163
+ if (!e.ctrlKey && !e.metaKey && !e.altKey) {
164
+ const num = parseInt(e.key);
165
+ if (num >= 1 && num <= 9) {
166
+ const sections = document.querySelectorAll('.section');
167
+ const target = sections[num - 1];
168
+ if (target) {
169
+ e.preventDefault();
170
+ target.scrollIntoView({ behavior: 'smooth' });
171
+ }
172
+ }
173
+ }
174
+
175
+ // Escape: Close any open accordion
176
+ if (e.key === 'Escape') {
177
+ document.querySelectorAll('.accordion-item[data-expanded="true"]').forEach(item => {
178
+ item.dataset.expanded = 'false';
179
+ item.querySelector('.accordion-header')?.setAttribute('aria-expanded', 'false');
180
+ });
181
+ }
182
+ });
183
+
184
+ // Hotkey button click handlers
185
+ document.querySelectorAll('.hotkey[data-hotkey]').forEach(button => {
186
+ button.addEventListener('click', () => {
187
+ const hotkey = button.dataset.hotkey;
188
+ if (hotkey === 'ctrl+t') toggleTheme();
189
+ if (hotkey === 'ctrl+g') toggleGrid();
190
+ });
191
+ });
192
+ }
193
+
194
+ // ============================================
195
+ // Active Section Highlight on Scroll
196
+ // ============================================
197
+ function initScrollSpy() {
198
+ const sections = document.querySelectorAll('.section[id], #hero');
199
+ const navLinks = document.querySelectorAll('.srcl-action-bar .primary a[href^="#"]');
200
+
201
+ if (sections.length === 0 || navLinks.length === 0) return;
202
+
203
+ let ticking = false;
204
+
205
+ function updateActiveSection() {
206
+ const scrollPos = window.scrollY + 100; // Offset for header
207
+
208
+ let currentSection = '';
209
+
210
+ sections.forEach(section => {
211
+ const sectionTop = section.offsetTop;
212
+ const sectionHeight = section.offsetHeight;
213
+
214
+ if (scrollPos >= sectionTop && scrollPos < sectionTop + sectionHeight) {
215
+ currentSection = section.getAttribute('id');
216
+ }
217
+ });
218
+
219
+ navLinks.forEach(link => {
220
+ const href = link.getAttribute('href').substring(1);
221
+ if (href === currentSection) {
222
+ link.classList.add('active');
223
+ } else {
224
+ link.classList.remove('active');
225
+ }
226
+ });
227
+
228
+ ticking = false;
229
+ }
230
+
231
+ window.addEventListener('scroll', () => {
232
+ if (!ticking) {
233
+ requestAnimationFrame(updateActiveSection);
234
+ ticking = true;
235
+ }
236
+ }, { passive: true });
237
+
238
+ // Initial check
239
+ updateActiveSection();
240
+ }
241
+
242
+ // ============================================
243
+ // Cursor Blink Effect
244
+ // ============================================
245
+ function initCursorBlink() {
246
+ // Add blinking cursor to hero name
247
+ const heroName = document.querySelector('.hero-card .name');
248
+ if (heroName) {
249
+ const cursor = document.createElement('span');
250
+ cursor.className = 'blink-cursor';
251
+ cursor.textContent = '_';
252
+ cursor.setAttribute('aria-hidden', 'true');
253
+ heroName.appendChild(cursor);
254
+ }
255
+
256
+ // Add CSS for cursor if not already present
257
+ if (!document.querySelector('#srcl-cursor-style')) {
258
+ const style = document.createElement('style');
259
+ style.id = 'srcl-cursor-style';
260
+ style.textContent = `
261
+ .blink-cursor {
262
+ animation: cursor-blink 1s step-end infinite;
263
+ margin-left: 0.25ch;
264
+ }
265
+ @keyframes cursor-blink {
266
+ 0%, 50% { opacity: 1; }
267
+ 51%, 100% { opacity: 0; }
268
+ }
269
+ `;
270
+ document.head.appendChild(style);
271
+ }
272
+ }
273
+
274
+ // ============================================
275
+ // Active nav link styles
276
+ // ============================================
277
+ function initNavStyles() {
278
+ // Add CSS for active nav links if not present
279
+ if (!document.querySelector('#srcl-nav-style')) {
280
+ const style = document.createElement('style');
281
+ style.id = 'srcl-nav-style';
282
+ style.textContent = `
283
+ .srcl-action-bar .primary a.active {
284
+ background: var(--theme-focused);
285
+ }
286
+ `;
287
+ document.head.appendChild(style);
288
+ }
289
+ }
290
+
291
+ // ============================================
292
+ // Accessibility: Screen Reader Announcements
293
+ // ============================================
294
+ let announcer = null;
295
+
296
+ function announceChange(message) {
297
+ if (!announcer) {
298
+ announcer = document.createElement('div');
299
+ announcer.setAttribute('aria-live', 'polite');
300
+ announcer.setAttribute('aria-atomic', 'true');
301
+ announcer.className = 'sr-only';
302
+ announcer.style.cssText = `
303
+ position: absolute;
304
+ width: 1px;
305
+ height: 1px;
306
+ padding: 0;
307
+ margin: -1px;
308
+ overflow: hidden;
309
+ clip: rect(0, 0, 0, 0);
310
+ white-space: nowrap;
311
+ border: 0;
312
+ `;
313
+ document.body.appendChild(announcer);
314
+ }
315
+
316
+ announcer.textContent = message;
317
+ }
318
+
319
+ // ============================================
320
+ // Initialize Everything
321
+ // ============================================
322
+ function init() {
323
+ loadThemePreference();
324
+ loadGridPreference();
325
+ initAccordions();
326
+ initSmoothScroll();
327
+ initKeyboardShortcuts();
328
+ initScrollSpy();
329
+ initCursorBlink();
330
+ initNavStyles();
331
+
332
+ // Log available shortcuts
333
+ console.log('%c[SRCL] Keyboard shortcuts:', 'color: #ef6300; font-weight: bold');
334
+ console.log(' Ctrl+T: Toggle dark/light theme');
335
+ console.log(' Ctrl+G: Toggle grid overlay');
336
+ console.log(' 1-9: Jump to sections');
337
+ console.log(' Esc: Close accordions');
338
+ }
339
+
340
+ // Run on DOM ready
341
+ if (document.readyState === 'loading') {
342
+ document.addEventListener('DOMContentLoaded', init);
343
+ } else {
344
+ init();
345
+ }
346
+
347
+ // Expose functions globally for debugging
348
+ window.SRCL = {
349
+ toggleTheme,
350
+ toggleGrid,
351
+ version: '1.0.0'
352
+ };
353
+
354
+ })();