@commonpub/layer 0.4.11 → 0.4.12

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -50,16 +50,16 @@
50
50
  "vue": "^3.4.0",
51
51
  "vue-router": "^4.3.0",
52
52
  "zod": "^4.3.6",
53
+ "@commonpub/docs": "0.5.2",
54
+ "@commonpub/config": "0.8.0",
55
+ "@commonpub/explainer": "0.5.3",
53
56
  "@commonpub/auth": "0.5.0",
54
57
  "@commonpub/editor": "0.5.0",
55
58
  "@commonpub/learning": "0.5.0",
56
- "@commonpub/schema": "0.8.14",
57
- "@commonpub/config": "0.8.0",
58
- "@commonpub/explainer": "0.5.3",
59
- "@commonpub/docs": "0.5.2",
60
59
  "@commonpub/ui": "0.8.4",
61
60
  "@commonpub/server": "2.23.0",
62
- "@commonpub/protocol": "0.9.5"
61
+ "@commonpub/protocol": "0.9.5",
62
+ "@commonpub/schema": "0.8.14"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@testing-library/jest-dom": "^6.9.1",
@@ -30,14 +30,14 @@ const form = ref({
30
30
  bannerUrl: '',
31
31
  });
32
32
 
33
- const skills = ref<Array<{ name: string; proficiency: number }>>([]);
33
+ const skills = ref<string[]>([]);
34
34
  const socialLinks = ref({
35
35
  github: '',
36
36
  twitter: '',
37
37
  linkedin: '',
38
- website: '',
39
38
  });
40
- const experience = ref<Array<{ id: string; title: string; company: string; startDate: string; endDate: string; description: string }>>([]);
39
+ const pronouns = ref('');
40
+ const experience = ref<Array<{ title: string; company: string; startDate: string; endDate: string; description: string }>>([]);
41
41
 
42
42
  const emailNotifications = ref<{
43
43
  digest: 'daily' | 'weekly' | 'none';
@@ -75,19 +75,23 @@ if (profile.value) {
75
75
  form.value.bannerUrl = p.bannerUrl || '';
76
76
 
77
77
  if (Array.isArray(p.skills)) {
78
- skills.value = p.skills.map((s) =>
79
- typeof s === 'string' ? { name: s, proficiency: 3 } : s,
80
- );
78
+ skills.value = p.skills.filter((s): s is string => typeof s === 'string');
81
79
  }
80
+ pronouns.value = p.pronouns || '';
82
81
  if (p.socialLinks) {
83
82
  socialLinks.value.github = p.socialLinks.github || '';
84
83
  socialLinks.value.twitter = p.socialLinks.twitter || '';
85
84
  socialLinks.value.linkedin = p.socialLinks.linkedin || '';
86
- socialLinks.value.website = (p.socialLinks as Record<string, string | undefined>).website || '';
87
85
  }
88
86
  const profileRecord = p as Record<string, unknown>;
89
87
  if (Array.isArray(profileRecord.experience)) {
90
- experience.value = (profileRecord.experience as Array<Record<string, unknown>>).map((e) => ({ ...e }) as typeof experience.value[number]);
88
+ experience.value = (profileRecord.experience as Array<Record<string, unknown>>).map((e) => ({
89
+ title: String(e.title || ''),
90
+ company: String(e.company || ''),
91
+ startDate: String(e.startDate || ''),
92
+ endDate: String(e.endDate || ''),
93
+ description: String(e.description || ''),
94
+ }));
91
95
  }
92
96
  if (profileRecord.emailNotifications && typeof profileRecord.emailNotifications === 'object') {
93
97
  const en = profileRecord.emailNotifications as Record<string, unknown>;
@@ -104,25 +108,20 @@ if (profile.value) {
104
108
  // Watch for form changes AFTER initial data is loaded (nextTick avoids false positive)
105
109
  onMounted(() => {
106
110
  nextTick(() => {
107
- watch([form, skills, socialLinks, experience, emailNotifications], () => { isDirty.value = true; }, { deep: true });
111
+ watch([form, skills, pronouns, socialLinks, experience, emailNotifications], () => { isDirty.value = true; }, { deep: true });
108
112
  });
109
113
  });
110
114
 
111
115
  function addSkill(): void {
112
- skills.value.push({ name: '', proficiency: 50 });
116
+ skills.value.push('');
113
117
  }
114
118
 
115
119
  function removeSkill(index: number): void {
116
120
  skills.value.splice(index, 1);
117
121
  }
118
122
 
119
- function generateId(): string {
120
- return `exp-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
121
- }
122
-
123
123
  function addExperience(): void {
124
124
  experience.value.push({
125
- id: generateId(),
126
125
  title: '',
127
126
  company: '',
128
127
  startDate: '',
@@ -170,8 +169,9 @@ async function handleSave(): Promise<void> {
170
169
  method: 'PUT',
171
170
  body: {
172
171
  ...form.value,
173
- skills: skills.value.filter((s) => s.name.trim()).map((s) => s.name),
172
+ skills: skills.value.filter((s) => s.trim()),
174
173
  experience: experience.value.filter((e) => e.title.trim()),
174
+ pronouns: pronouns.value || undefined,
175
175
  socialLinks: socialLinks.value,
176
176
  ...(emailNotificationsEnabled.value ? { emailNotifications: emailNotifications.value } : {}),
177
177
  },
@@ -330,39 +330,42 @@ async function handleSave(): Promise<void> {
330
330
  </div>
331
331
  </div>
332
332
 
333
+ <!-- Pronouns -->
334
+ <div class="cpub-form-section">
335
+ <span class="cpub-form-section-label">Identity</span>
336
+
337
+ <div class="cpub-form-group">
338
+ <label for="pronouns" class="cpub-form-label">Pronouns</label>
339
+ <input
340
+ id="pronouns"
341
+ v-model="pronouns"
342
+ type="text"
343
+ class="cpub-input"
344
+ placeholder="e.g., they/them, she/her, he/him"
345
+ />
346
+ </div>
347
+ </div>
348
+
333
349
  <!-- Skills -->
334
350
  <div class="cpub-form-section">
335
351
  <span class="cpub-form-section-label">Skills</span>
336
352
 
337
353
  <div
338
- v-for="(skill, index) in skills"
354
+ v-for="(_skill, index) in skills"
339
355
  :key="index"
340
356
  class="cpub-skill-row"
341
357
  >
342
- <div class="cpub-skill-name">
343
- <input
344
- v-model="skill.name"
345
- type="text"
346
- class="cpub-input"
347
- placeholder="Skill name"
348
- :aria-label="`Skill ${index + 1} name`"
349
- />
350
- </div>
351
- <div class="cpub-skill-slider">
352
- <input
353
- v-model.number="skill.proficiency"
354
- type="range"
355
- min="0"
356
- max="100"
357
- class="cpub-range"
358
- :aria-label="`Skill ${index + 1} proficiency`"
359
- />
360
- <span class="cpub-skill-value">{{ skill.proficiency }}%</span>
361
- </div>
358
+ <input
359
+ v-model="skills[index]"
360
+ type="text"
361
+ class="cpub-input"
362
+ placeholder="Skill name"
363
+ :aria-label="`Skill ${index + 1}`"
364
+ />
362
365
  <button
363
366
  type="button"
364
367
  class="cpub-btn-icon cpub-btn-danger"
365
- :aria-label="`Remove skill ${skill.name || index + 1}`"
368
+ :aria-label="`Remove skill ${skills[index] || index + 1}`"
366
369
  @click="removeSkill(index)"
367
370
  >
368
371
  <i class="fa-solid fa-xmark" aria-hidden="true"></i>
@@ -416,16 +419,6 @@ async function handleSave(): Promise<void> {
416
419
  />
417
420
  </div>
418
421
 
419
- <div class="cpub-form-group">
420
- <label for="social-website" class="cpub-form-label">Website URL</label>
421
- <input
422
- id="social-website"
423
- v-model="socialLinks.website"
424
- type="url"
425
- class="cpub-input"
426
- placeholder="https://..."
427
- />
428
- </div>
429
422
  </div>
430
423
 
431
424
  <!-- Experience -->
@@ -434,7 +427,7 @@ async function handleSave(): Promise<void> {
434
427
 
435
428
  <div
436
429
  v-for="(entry, index) in experience"
437
- :key="entry.id"
430
+ :key="index"
438
431
  class="cpub-experience-card"
439
432
  >
440
433
  <div class="cpub-experience-header">
@@ -451,9 +444,9 @@ async function handleSave(): Promise<void> {
451
444
 
452
445
  <div class="cpub-experience-fields">
453
446
  <div class="cpub-form-group">
454
- <label :for="`exp-title-${entry.id}`" class="cpub-form-label">Title</label>
447
+ <label :for="`exp-title-${index}`" class="cpub-form-label">Title</label>
455
448
  <input
456
- :id="`exp-title-${entry.id}`"
449
+ :id="`exp-title-${index}`"
457
450
  v-model="entry.title"
458
451
  type="text"
459
452
  class="cpub-input"
@@ -462,9 +455,9 @@ async function handleSave(): Promise<void> {
462
455
  </div>
463
456
 
464
457
  <div class="cpub-form-group">
465
- <label :for="`exp-company-${entry.id}`" class="cpub-form-label">Company</label>
458
+ <label :for="`exp-company-${index}`" class="cpub-form-label">Company</label>
466
459
  <input
467
- :id="`exp-company-${entry.id}`"
460
+ :id="`exp-company-${index}`"
468
461
  v-model="entry.company"
469
462
  type="text"
470
463
  class="cpub-input"
@@ -474,18 +467,18 @@ async function handleSave(): Promise<void> {
474
467
 
475
468
  <div class="cpub-experience-dates">
476
469
  <div class="cpub-form-group">
477
- <label :for="`exp-start-${entry.id}`" class="cpub-form-label">Start Date</label>
470
+ <label :for="`exp-start-${index}`" class="cpub-form-label">Start Date</label>
478
471
  <input
479
- :id="`exp-start-${entry.id}`"
472
+ :id="`exp-start-${index}`"
480
473
  v-model="entry.startDate"
481
474
  type="month"
482
475
  class="cpub-input"
483
476
  />
484
477
  </div>
485
478
  <div class="cpub-form-group">
486
- <label :for="`exp-end-${entry.id}`" class="cpub-form-label">End Date</label>
479
+ <label :for="`exp-end-${index}`" class="cpub-form-label">End Date</label>
487
480
  <input
488
- :id="`exp-end-${entry.id}`"
481
+ :id="`exp-end-${index}`"
489
482
  v-model="entry.endDate"
490
483
  type="month"
491
484
  class="cpub-input"
@@ -495,9 +488,9 @@ async function handleSave(): Promise<void> {
495
488
  </div>
496
489
 
497
490
  <div class="cpub-form-group">
498
- <label :for="`exp-desc-${entry.id}`" class="cpub-form-label">Description</label>
491
+ <label :for="`exp-desc-${index}`" class="cpub-form-label">Description</label>
499
492
  <textarea
500
- :id="`exp-desc-${entry.id}`"
493
+ :id="`exp-desc-${index}`"
501
494
  v-model="entry.description"
502
495
  class="cpub-textarea"
503
496
  rows="3"
@@ -370,6 +370,25 @@ async function handleReport(): Promise<void> {
370
370
  <p class="cpub-empty-state-title">No skills listed yet</p>
371
371
  </div>
372
372
  </div>
373
+
374
+ <!-- Experience -->
375
+ <div v-if="(p as Record<string, unknown>).experience && ((p as Record<string, unknown>).experience as Array<Record<string, string>>).length > 0" class="cpub-experience-section">
376
+ <div class="cpub-sec-head">
377
+ <h2><i class="fa-solid fa-briefcase" style="color: var(--purple); margin-right: 6px"></i>Experience</h2>
378
+ </div>
379
+ <div class="cpub-experience-list">
380
+ <div v-for="(exp, idx) in ((p as Record<string, unknown>).experience as Array<Record<string, string>>)" :key="idx" class="cpub-experience-item">
381
+ <div class="cpub-exp-header">
382
+ <strong>{{ exp.title }}</strong>
383
+ <span v-if="exp.company" class="cpub-exp-company">{{ exp.company }}</span>
384
+ </div>
385
+ <div v-if="exp.startDate" class="cpub-exp-dates">
386
+ {{ exp.startDate }}{{ exp.endDate ? ` — ${exp.endDate}` : ' — Present' }}
387
+ </div>
388
+ <p v-if="exp.description" class="cpub-exp-desc">{{ exp.description }}</p>
389
+ </div>
390
+ </div>
391
+ </div>
373
392
  </div>
374
393
 
375
394
  <!-- Sidebar -->
@@ -709,6 +728,20 @@ async function handleReport(): Promise<void> {
709
728
  margin-bottom: 8px;
710
729
  }
711
730
 
731
+ /* Experience */
732
+ .cpub-experience-section { margin-top: 24px; }
733
+ .cpub-experience-list { display: flex; flex-direction: column; gap: 16px; }
734
+ .cpub-experience-item {
735
+ padding: 12px 16px;
736
+ border: var(--border-width-default) solid var(--border2);
737
+ background: var(--surface);
738
+ }
739
+ .cpub-exp-header { display: flex; flex-direction: column; gap: 2px; }
740
+ .cpub-exp-header strong { font-size: var(--text-sm); }
741
+ .cpub-exp-company { font-size: var(--text-xs); color: var(--text-dim); }
742
+ .cpub-exp-dates { font-size: var(--text-xs); color: var(--text-faint); font-family: var(--font-mono); margin-top: 4px; }
743
+ .cpub-exp-desc { font-size: var(--text-sm); color: var(--text-dim); margin-top: 8px; line-height: var(--leading-normal); }
744
+
712
745
  /* About grid */
713
746
  .cpub-about-grid {
714
747
  display: grid;