@takuhon/core 1.3.0 → 1.4.1

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  var $schema = "https://json-schema.org/draft/2020-12/schema";
2
- var $id = "https://takuhon.example/schemas/1.3.0/takuhon.schema.json";
2
+ var $id = "https://takuhon.example/schemas/1.4.0/takuhon.schema.json";
3
3
  var title = "Takuhon Profile";
4
4
  var description = "Portable profile data format consumed by @takuhon/core. The canonical contract for profile content authored as takuhon.json.";
5
5
  var type = "object";
@@ -125,6 +125,13 @@ var properties = {
125
125
  $ref: "#/$defs/Recommendation"
126
126
  }
127
127
  },
128
+ highlights: {
129
+ type: "array",
130
+ maxItems: 50,
131
+ items: {
132
+ $ref: "#/$defs/Highlight"
133
+ }
134
+ },
128
135
  contact: {
129
136
  $ref: "#/$defs/Contact"
130
137
  },
@@ -425,6 +432,10 @@ var $defs = {
425
432
  title: {
426
433
  $ref: "#/$defs/LocalizedTitle"
427
434
  },
435
+ role: {
436
+ $ref: "#/$defs/LocalizedTitle",
437
+ description: "The owner's role/relationship on this project (added in 1.4.0), e.g. \"Author & lead maintainer\", \"Contributor\", \"Product owner\". Rendered as a short line under the project title. Localized, like the section-entry roles in `Career`/`Membership`/`Volunteering`."
438
+ },
428
439
  description: {
429
440
  $ref: "#/$defs/LocalizedBody"
430
441
  },
@@ -480,9 +491,17 @@ var $defs = {
480
491
  $ref: "#/$defs/Visibility"
481
492
  },
482
493
  label: {
483
- type: "string",
484
- minLength: 1,
485
- maxLength: 100
494
+ anyOf: [
495
+ {
496
+ type: "string",
497
+ minLength: 1,
498
+ maxLength: 100
499
+ },
500
+ {
501
+ $ref: "#/$defs/LocalizedTitle"
502
+ }
503
+ ],
504
+ description: "Skill name. A plain string is locale-independent; a localized map (added in 1.4.0) resolves per request locale. Existing single-string labels stay valid — the map form is purely additive."
486
505
  },
487
506
  category: {
488
507
  type: "string",
@@ -600,6 +619,117 @@ var $defs = {
600
619
  $ref: "#/$defs/SkillCategory"
601
620
  },
602
621
  description: "Ordered skill-category display groups (added in 1.3.0). When present, the rendered profile groups skills by their `category` under these localized headings, in array order; absent = the flat skill list. A skill whose `category` is not listed here (or which has none) renders in a trailing group so nothing is dropped."
622
+ },
623
+ sectionOrder: {
624
+ type: "array",
625
+ uniqueItems: true,
626
+ maxItems: 18,
627
+ items: {
628
+ "enum": [
629
+ "about",
630
+ "careers",
631
+ "projects",
632
+ "volunteering",
633
+ "skills",
634
+ "activity",
635
+ "education",
636
+ "certifications",
637
+ "publications",
638
+ "honors",
639
+ "memberships",
640
+ "courses",
641
+ "patents",
642
+ "testScores",
643
+ "languages",
644
+ "recommendations",
645
+ "highlights",
646
+ "contact"
647
+ ]
648
+ },
649
+ description: "Explicit section display order (added in 1.4.0). A partial list is allowed — any renderable section not named here follows, in the default order. Absent = the default order."
650
+ },
651
+ sectionLabels: {
652
+ $ref: "#/$defs/SectionLabelOverrides"
653
+ },
654
+ highlightsIntro: {
655
+ $ref: "#/$defs/LocalizedTitle",
656
+ description: "Optional localized intro line shown under the highlights section heading (added in 1.4.0). Absent = no intro line."
657
+ }
658
+ }
659
+ },
660
+ SectionLabelOverrides: {
661
+ type: "object",
662
+ additionalProperties: false,
663
+ description: "Localized overrides for section headings and chrome labels (added in 1.4.0). Each present key retitles that label; the renderer merges these over its built-in locale pack. Absent keys keep the built-in label.",
664
+ properties: {
665
+ about: {
666
+ $ref: "#/$defs/LocalizedTitle"
667
+ },
668
+ careers: {
669
+ $ref: "#/$defs/LocalizedTitle"
670
+ },
671
+ projects: {
672
+ $ref: "#/$defs/LocalizedTitle"
673
+ },
674
+ volunteering: {
675
+ $ref: "#/$defs/LocalizedTitle"
676
+ },
677
+ skills: {
678
+ $ref: "#/$defs/LocalizedTitle"
679
+ },
680
+ activity: {
681
+ $ref: "#/$defs/LocalizedTitle"
682
+ },
683
+ education: {
684
+ $ref: "#/$defs/LocalizedTitle"
685
+ },
686
+ certifications: {
687
+ $ref: "#/$defs/LocalizedTitle"
688
+ },
689
+ publications: {
690
+ $ref: "#/$defs/LocalizedTitle"
691
+ },
692
+ honors: {
693
+ $ref: "#/$defs/LocalizedTitle"
694
+ },
695
+ memberships: {
696
+ $ref: "#/$defs/LocalizedTitle"
697
+ },
698
+ courses: {
699
+ $ref: "#/$defs/LocalizedTitle"
700
+ },
701
+ patents: {
702
+ $ref: "#/$defs/LocalizedTitle"
703
+ },
704
+ testScores: {
705
+ $ref: "#/$defs/LocalizedTitle"
706
+ },
707
+ languages: {
708
+ $ref: "#/$defs/LocalizedTitle"
709
+ },
710
+ recommendations: {
711
+ $ref: "#/$defs/LocalizedTitle"
712
+ },
713
+ highlights: {
714
+ $ref: "#/$defs/LocalizedTitle"
715
+ },
716
+ contact: {
717
+ $ref: "#/$defs/LocalizedTitle"
718
+ },
719
+ skipLink: {
720
+ $ref: "#/$defs/LocalizedTitle"
721
+ },
722
+ localeNav: {
723
+ $ref: "#/$defs/LocalizedTitle"
724
+ },
725
+ featuredLinks: {
726
+ $ref: "#/$defs/LocalizedTitle"
727
+ },
728
+ otherLinks: {
729
+ $ref: "#/$defs/LocalizedTitle"
730
+ },
731
+ poweredBy: {
732
+ $ref: "#/$defs/LocalizedTitle"
603
733
  }
604
734
  }
605
735
  },
@@ -661,6 +791,10 @@ var $defs = {
661
791
  primaryContrast: {
662
792
  $ref: "#/$defs/CssColor",
663
793
  description: "Foreground color used on top of the primary color."
794
+ },
795
+ heading: {
796
+ $ref: "#/$defs/CssColor",
797
+ description: "Heading color for the \"About\" section's Markdown sub-headings (##/###)."
664
798
  }
665
799
  }
666
800
  },
@@ -766,6 +900,10 @@ var $defs = {
766
900
  type: "boolean",
767
901
  description: "Default true. When false, recommendations[] is emitted empty on public surfaces."
768
902
  },
903
+ highlights: {
904
+ type: "boolean",
905
+ description: "Default true. When false, highlights[] is emitted empty on public surfaces."
906
+ },
769
907
  contact: {
770
908
  type: "boolean",
771
909
  description: "Default true. When false, the contact section (email, formUrl) is emitted empty on public surfaces."
@@ -1067,12 +1205,34 @@ var $defs = {
1067
1205
  url: {
1068
1206
  $ref: "#/$defs/Url"
1069
1207
  },
1208
+ secondaryLink: {
1209
+ $ref: "#/$defs/SecondaryLink",
1210
+ description: "An optional second link for this entry (added in 1.4.0), rendered as a small pill beside the description — e.g. the organization's GitHub org page next to its main site. The brand glyph is resolved from the URL host; no vendor name is hard-coded."
1211
+ },
1070
1212
  order: {
1071
1213
  type: "integer",
1072
1214
  minimum: 0
1073
1215
  }
1074
1216
  }
1075
1217
  },
1218
+ SecondaryLink: {
1219
+ type: "object",
1220
+ additionalProperties: false,
1221
+ required: [
1222
+ "url"
1223
+ ],
1224
+ description: "A supplementary link with an optional localized label (added in 1.4.0). When the label is absent the renderer shows the URL host. The brand glyph is resolved from the URL host.",
1225
+ properties: {
1226
+ url: {
1227
+ $ref: "#/$defs/Url",
1228
+ description: "The link target."
1229
+ },
1230
+ label: {
1231
+ $ref: "#/$defs/LocalizedTitle",
1232
+ description: "Localized display label. When absent, the renderer derives a label from the URL host."
1233
+ }
1234
+ }
1235
+ },
1076
1236
  Honor: {
1077
1237
  type: "object",
1078
1238
  additionalProperties: false,
@@ -1476,6 +1636,74 @@ var $defs = {
1476
1636
  description: "Link to the recommender's profile, for external verification by the reader."
1477
1637
  }
1478
1638
  }
1639
+ },
1640
+ Highlight: {
1641
+ type: "object",
1642
+ additionalProperties: false,
1643
+ required: [
1644
+ "id",
1645
+ "platform",
1646
+ "url",
1647
+ "image",
1648
+ "alt",
1649
+ "title"
1650
+ ],
1651
+ description: "One curated \"selected post\" (added in 1.4.0), rendered as a card in the highlights carousel. A hand-picked link to an off-site post (Instagram, X, a blog, a talk, …) with a self-hosted thumbnail — deliberately not an automated social feed. Appears on the public page, /api/profile, /takuhon.json, and MCP; it is intentionally omitted from JSON-LD and the derived CV (no natural vocabulary/résumé slot).",
1652
+ properties: {
1653
+ id: {
1654
+ $ref: "#/$defs/Slug"
1655
+ },
1656
+ visibility: {
1657
+ $ref: "#/$defs/Visibility"
1658
+ },
1659
+ platform: {
1660
+ type: "string",
1661
+ minLength: 1,
1662
+ maxLength: 64,
1663
+ description: "Free-form source platform (e.g. 'instagram', 'x', 'github', 'blog', 'event'). A value with a matching brand glyph renders a badge icon; others fall back to a text badge. Not an enum, so new platforms need no schema change."
1664
+ },
1665
+ url: {
1666
+ $ref: "#/$defs/Url",
1667
+ description: "Permalink to the original post; the card links here."
1668
+ },
1669
+ image: {
1670
+ type: "string",
1671
+ format: "uri-reference",
1672
+ maxLength: 2048,
1673
+ description: "Self-hosted thumbnail, as an absolute URL or a root-relative path (like avatar). Serving the asset is the deployment's responsibility (a static build does not copy it; a Cloudflare deploy serves it via its assets binding / /assets/*). Do not point at a third-party CDN URL — those can be signed/expiring and leak visitor data."
1674
+ },
1675
+ alt: {
1676
+ $ref: "#/$defs/LocalizedTitle",
1677
+ description: "Required alt text for the thumbnail — the card must not depend on the image alone."
1678
+ },
1679
+ title: {
1680
+ $ref: "#/$defs/LocalizedTitle",
1681
+ description: "Localized card title / caption."
1682
+ },
1683
+ description: {
1684
+ $ref: "#/$defs/LocalizedBody",
1685
+ description: "Optional localized supporting text shown under the title."
1686
+ },
1687
+ postedAt: {
1688
+ type: "string",
1689
+ format: "date",
1690
+ pattern: "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
1691
+ description: "Optional post date, ISO 'YYYY-MM-DD' (full date, unlike the YYYY-MM dates used elsewhere — a post has a specific day)."
1692
+ },
1693
+ tags: {
1694
+ type: "array",
1695
+ maxItems: 30,
1696
+ items: {
1697
+ type: "string",
1698
+ minLength: 1,
1699
+ maxLength: 50
1700
+ }
1701
+ },
1702
+ order: {
1703
+ type: "integer",
1704
+ minimum: 0
1705
+ }
1706
+ }
1479
1707
  }
1480
1708
  };
1481
1709
  var schemaJson = {
@@ -1621,6 +1849,13 @@ declare const schema: {
1621
1849
  $ref: string;
1622
1850
  };
1623
1851
  };
1852
+ highlights: {
1853
+ type: string;
1854
+ maxItems: number;
1855
+ items: {
1856
+ $ref: string;
1857
+ };
1858
+ };
1624
1859
  contact: {
1625
1860
  $ref: string;
1626
1861
  };
@@ -1879,6 +2114,10 @@ declare const schema: {
1879
2114
  title: {
1880
2115
  $ref: string;
1881
2116
  };
2117
+ role: {
2118
+ $ref: string;
2119
+ description: string;
2120
+ };
1882
2121
  description: {
1883
2122
  $ref: string;
1884
2123
  };
@@ -1930,9 +2169,18 @@ declare const schema: {
1930
2169
  $ref: string;
1931
2170
  };
1932
2171
  label: {
1933
- type: string;
1934
- minLength: number;
1935
- maxLength: number;
2172
+ anyOf: ({
2173
+ type: string;
2174
+ minLength: number;
2175
+ maxLength: number;
2176
+ $ref?: undefined;
2177
+ } | {
2178
+ $ref: string;
2179
+ type?: undefined;
2180
+ minLength?: undefined;
2181
+ maxLength?: undefined;
2182
+ })[];
2183
+ description: string;
1936
2184
  };
1937
2185
  category: {
1938
2186
  type: string;
@@ -2045,6 +2293,98 @@ declare const schema: {
2045
2293
  };
2046
2294
  description: string;
2047
2295
  };
2296
+ sectionOrder: {
2297
+ type: string;
2298
+ uniqueItems: boolean;
2299
+ maxItems: number;
2300
+ items: {
2301
+ enum: string[];
2302
+ };
2303
+ description: string;
2304
+ };
2305
+ sectionLabels: {
2306
+ $ref: string;
2307
+ };
2308
+ highlightsIntro: {
2309
+ $ref: string;
2310
+ description: string;
2311
+ };
2312
+ };
2313
+ };
2314
+ SectionLabelOverrides: {
2315
+ type: string;
2316
+ additionalProperties: boolean;
2317
+ description: string;
2318
+ properties: {
2319
+ about: {
2320
+ $ref: string;
2321
+ };
2322
+ careers: {
2323
+ $ref: string;
2324
+ };
2325
+ projects: {
2326
+ $ref: string;
2327
+ };
2328
+ volunteering: {
2329
+ $ref: string;
2330
+ };
2331
+ skills: {
2332
+ $ref: string;
2333
+ };
2334
+ activity: {
2335
+ $ref: string;
2336
+ };
2337
+ education: {
2338
+ $ref: string;
2339
+ };
2340
+ certifications: {
2341
+ $ref: string;
2342
+ };
2343
+ publications: {
2344
+ $ref: string;
2345
+ };
2346
+ honors: {
2347
+ $ref: string;
2348
+ };
2349
+ memberships: {
2350
+ $ref: string;
2351
+ };
2352
+ courses: {
2353
+ $ref: string;
2354
+ };
2355
+ patents: {
2356
+ $ref: string;
2357
+ };
2358
+ testScores: {
2359
+ $ref: string;
2360
+ };
2361
+ languages: {
2362
+ $ref: string;
2363
+ };
2364
+ recommendations: {
2365
+ $ref: string;
2366
+ };
2367
+ highlights: {
2368
+ $ref: string;
2369
+ };
2370
+ contact: {
2371
+ $ref: string;
2372
+ };
2373
+ skipLink: {
2374
+ $ref: string;
2375
+ };
2376
+ localeNav: {
2377
+ $ref: string;
2378
+ };
2379
+ featuredLinks: {
2380
+ $ref: string;
2381
+ };
2382
+ otherLinks: {
2383
+ $ref: string;
2384
+ };
2385
+ poweredBy: {
2386
+ $ref: string;
2387
+ };
2048
2388
  };
2049
2389
  };
2050
2390
  AppearanceSettings: {
@@ -2106,6 +2446,10 @@ declare const schema: {
2106
2446
  $ref: string;
2107
2447
  description: string;
2108
2448
  };
2449
+ heading: {
2450
+ $ref: string;
2451
+ description: string;
2452
+ };
2109
2453
  };
2110
2454
  };
2111
2455
  CssColor: {
@@ -2210,6 +2554,10 @@ declare const schema: {
2210
2554
  type: string;
2211
2555
  description: string;
2212
2556
  };
2557
+ highlights: {
2558
+ type: string;
2559
+ description: string;
2560
+ };
2213
2561
  contact: {
2214
2562
  type: string;
2215
2563
  description: string;
@@ -2486,12 +2834,32 @@ declare const schema: {
2486
2834
  url: {
2487
2835
  $ref: string;
2488
2836
  };
2837
+ secondaryLink: {
2838
+ $ref: string;
2839
+ description: string;
2840
+ };
2489
2841
  order: {
2490
2842
  type: string;
2491
2843
  minimum: number;
2492
2844
  };
2493
2845
  };
2494
2846
  };
2847
+ SecondaryLink: {
2848
+ type: string;
2849
+ additionalProperties: boolean;
2850
+ required: string[];
2851
+ description: string;
2852
+ properties: {
2853
+ url: {
2854
+ $ref: string;
2855
+ description: string;
2856
+ };
2857
+ label: {
2858
+ $ref: string;
2859
+ description: string;
2860
+ };
2861
+ };
2862
+ };
2495
2863
  Honor: {
2496
2864
  type: string;
2497
2865
  additionalProperties: boolean;
@@ -2848,10 +3216,115 @@ declare const schema: {
2848
3216
  };
2849
3217
  };
2850
3218
  };
3219
+ Highlight: {
3220
+ type: string;
3221
+ additionalProperties: boolean;
3222
+ required: string[];
3223
+ description: string;
3224
+ properties: {
3225
+ id: {
3226
+ $ref: string;
3227
+ };
3228
+ visibility: {
3229
+ $ref: string;
3230
+ };
3231
+ platform: {
3232
+ type: string;
3233
+ minLength: number;
3234
+ maxLength: number;
3235
+ description: string;
3236
+ };
3237
+ url: {
3238
+ $ref: string;
3239
+ description: string;
3240
+ };
3241
+ image: {
3242
+ type: string;
3243
+ format: string;
3244
+ maxLength: number;
3245
+ description: string;
3246
+ };
3247
+ alt: {
3248
+ $ref: string;
3249
+ description: string;
3250
+ };
3251
+ title: {
3252
+ $ref: string;
3253
+ description: string;
3254
+ };
3255
+ description: {
3256
+ $ref: string;
3257
+ description: string;
3258
+ };
3259
+ postedAt: {
3260
+ type: string;
3261
+ format: string;
3262
+ pattern: string;
3263
+ description: string;
3264
+ };
3265
+ tags: {
3266
+ type: string;
3267
+ maxItems: number;
3268
+ items: {
3269
+ type: string;
3270
+ minLength: number;
3271
+ maxLength: number;
3272
+ };
3273
+ };
3274
+ order: {
3275
+ type: string;
3276
+ minimum: number;
3277
+ };
3278
+ };
3279
+ };
2851
3280
  };
2852
3281
  };
2853
3282
  type Schema = typeof schemaJson;
2854
3283
 
3284
+ /**
3285
+ * The canonical set — and default order — of the profile sections the standard
3286
+ * renderer emits. One key per orderable `<section>`.
3287
+ *
3288
+ * This is the single source of truth for section identity and default ordering,
3289
+ * shared so the renderer, `settings.sectionOrder`, and the section-level privacy
3290
+ * controls cannot drift apart. Two kinds of keys appear:
3291
+ *
3292
+ * - **Data sections** — the array/object sections of the document, named exactly
3293
+ * as in {@link PublicVisibility} (`careers`, `projects`, `highlights`, …). A
3294
+ * parity test guarantees every visibility-controlled data section is
3295
+ * represented here. `highlights` (the curated "selected posts" carousel, its
3296
+ * own top-level array added in 1.4.0) is one of these.
3297
+ * - **Page-only sections** — `about` (rendered from `profile.bio`) and
3298
+ * `activity` (rendered from a synced snapshot, not stored document data).
3299
+ *
3300
+ * `links` is deliberately absent: featured links render at the top and the
3301
+ * remaining links render as a fixed bottom section, so link placement is not
3302
+ * part of the reorderable section flow.
3303
+ *
3304
+ * The array order is the renderer's default section order; a caller may render a
3305
+ * subset in a different order, but unlisted sections keep this ordering.
3306
+ */
3307
+ declare const SECTION_KEYS: readonly ["about", "careers", "projects", "volunteering", "skills", "activity", "education", "certifications", "publications", "honors", "memberships", "courses", "patents", "testScores", "languages", "recommendations", "highlights", "contact"];
3308
+ /** One of the canonical {@link SECTION_KEYS}. */
3309
+ type SectionKey = (typeof SECTION_KEYS)[number];
3310
+ /**
3311
+ * Non-section "chrome" labels the renderer emits (skip link, nav aria-labels,
3312
+ * the other-links heading, the footer credit lead-in). Together with the
3313
+ * section headings these form the full set of localizable labels a caller can
3314
+ * override.
3315
+ */
3316
+ declare const CHROME_LABEL_KEYS: readonly ["skipLink", "localeNav", "featuredLinks", "otherLinks", "poweredBy"];
3317
+ /**
3318
+ * Every overridable label key: one per {@link SECTION_KEYS} heading plus the
3319
+ * {@link CHROME_LABEL_KEYS}. This is the single source of truth for the label
3320
+ * set shared by the renderer's `SectionLabels`, the `settings.sectionLabels`
3321
+ * data override, and the per-request label override — so all three stay in
3322
+ * lock-step.
3323
+ */
3324
+ declare const LABEL_KEYS: readonly ["about", "careers", "projects", "volunteering", "skills", "activity", "education", "certifications", "publications", "honors", "memberships", "courses", "patents", "testScores", "languages", "recommendations", "highlights", "contact", "skipLink", "localeNav", "featuredLinks", "otherLinks", "poweredBy"];
3325
+ /** One of the {@link LABEL_KEYS}. */
3326
+ type LabelKey = (typeof LABEL_KEYS)[number];
3327
+
2855
3328
  /**
2856
3329
  * TypeScript types for takuhon profile data.
2857
3330
  *
@@ -2894,6 +3367,7 @@ type Schema = typeof schemaJson;
2894
3367
  * alongside a profile should keep it in a sibling structure rather than
2895
3368
  * smuggling undeclared keys into the contract — schema 1.0.0 now rejects them.
2896
3369
  */
3370
+
2897
3371
  /** BCP-47 language tag, e.g. 'en', 'ja', 'zh-Hant', 'pt-BR'. */
2898
3372
  type LocaleTag = string;
2899
3373
  /** ISO 3166-1 alpha-2 country code, uppercase, two letters (e.g. 'JP', 'PT'). */
@@ -2985,6 +3459,12 @@ interface Career extends VisibilityControlled {
2985
3459
  interface Project extends VisibilityControlled {
2986
3460
  id: Slug;
2987
3461
  title: LocalizedTitle;
3462
+ /**
3463
+ * The owner's role/relationship on this project (added in 1.4.0), e.g.
3464
+ * "Author & lead maintainer". Rendered as a short line under the title.
3465
+ * Localized, like {@link Career.role}.
3466
+ */
3467
+ role?: LocalizedTitle;
2988
3468
  description?: LocalizedBody;
2989
3469
  url?: string;
2990
3470
  tags?: string[];
@@ -2996,7 +3476,12 @@ interface Project extends VisibilityControlled {
2996
3476
  }
2997
3477
  interface Skill extends VisibilityControlled {
2998
3478
  id: Slug;
2999
- label: string;
3479
+ /**
3480
+ * Skill name. A plain `string` is locale-independent; a {@link LocalizedTitle}
3481
+ * map (added in 1.4.0) resolves per request locale. The map form is purely
3482
+ * additive — existing single-string labels stay valid.
3483
+ */
3484
+ label: string | LocalizedTitle;
3000
3485
  /**
3001
3486
  * Recommended values (extensible): programming, design, business, communication,
3002
3487
  * language, music, art, sports, other.
@@ -3041,8 +3526,20 @@ interface Volunteering extends VisibilityControlled {
3041
3526
  endDate?: YearMonth | null;
3042
3527
  isCurrent?: boolean;
3043
3528
  url?: string;
3529
+ /** An optional second link (added in 1.4.0), e.g. the organization's GitHub org page. */
3530
+ secondaryLink?: SecondaryLink;
3044
3531
  order?: number;
3045
3532
  }
3533
+ /**
3534
+ * A supplementary link with an optional localized label (added in 1.4.0). Used by
3535
+ * {@link Volunteering.secondaryLink}. The renderer resolves the brand glyph from
3536
+ * the URL host and, when {@link SecondaryLink.label} is absent, derives a display
3537
+ * label from the host too.
3538
+ */
3539
+ interface SecondaryLink {
3540
+ url: string;
3541
+ label?: LocalizedTitle;
3542
+ }
3046
3543
  interface Honor extends VisibilityControlled {
3047
3544
  id: Slug;
3048
3545
  title: LocalizedTitle;
@@ -3159,6 +3656,41 @@ interface Recommendation extends VisibilityControlled {
3159
3656
  relatedEducationId?: Slug;
3160
3657
  order?: number;
3161
3658
  }
3659
+ /**
3660
+ * One curated "selected post" (added in 1.4.0), rendered as a card in the
3661
+ * highlights carousel: a hand-picked link to an off-site post (Instagram, X, a
3662
+ * blog, a talk, …) with a self-hosted thumbnail — deliberately not an automated
3663
+ * social feed. It appears on the public page, `/api/profile`, `/takuhon.json`,
3664
+ * and MCP, but is intentionally omitted from JSON-LD and the derived CV (no
3665
+ * natural vocabulary / résumé slot for it).
3666
+ */
3667
+ interface Highlight extends VisibilityControlled {
3668
+ id: Slug;
3669
+ /**
3670
+ * Free-form source platform (e.g. `'instagram'`, `'x'`, `'github'`, `'blog'`,
3671
+ * `'event'`). A value with a matching brand glyph renders a badge icon; others
3672
+ * fall back to a text badge. Not an enum, so new platforms need no schema
3673
+ * change.
3674
+ */
3675
+ platform: string;
3676
+ /** Permalink to the original post; the card links here. */
3677
+ url: string;
3678
+ /**
3679
+ * Self-hosted thumbnail, as an absolute URL or a root-relative path (like
3680
+ * {@link Avatar.url}). Serving the asset is the deployment's responsibility.
3681
+ */
3682
+ image: string;
3683
+ /** Required alt text for the thumbnail — the card must not rely on the image alone. */
3684
+ alt: LocalizedTitle;
3685
+ /** Localized card title / caption. */
3686
+ title: LocalizedTitle;
3687
+ /** Optional localized supporting text shown under the title. */
3688
+ description?: LocalizedBody;
3689
+ /** Optional post date, ISO `YYYY-MM-DD` (a full date, unlike the YYYY-MM used elsewhere). */
3690
+ postedAt?: string;
3691
+ tags?: string[];
3692
+ order?: number;
3693
+ }
3162
3694
  interface Contact {
3163
3695
  email?: string;
3164
3696
  showEmail?: boolean;
@@ -3193,7 +3725,28 @@ interface Settings {
3193
3725
  * `category` is unlisted (or absent) renders in a trailing group.
3194
3726
  */
3195
3727
  skillCategories?: SkillCategory[];
3728
+ /**
3729
+ * Explicit section display order (added in 1.4.0). A partial list is allowed:
3730
+ * any renderable section not named here follows, in the default order. Absent
3731
+ * = the default order. Keys are the canonical {@link SectionKey}s.
3732
+ */
3733
+ sectionOrder?: SectionKey[];
3734
+ /**
3735
+ * Localized overrides for section headings and chrome labels (added in 1.4.0).
3736
+ * A partial map keyed by {@link LabelKey}; the renderer merges it over the
3737
+ * built-in locale pack and under a per-request override, so an owner can
3738
+ * retitle any section or chrome label straight from `takuhon.json`. Absent =
3739
+ * the built-in labels.
3740
+ */
3741
+ sectionLabels?: SectionLabelOverrides;
3742
+ /**
3743
+ * Localized intro line shown under the highlights section heading (added in
3744
+ * 1.4.0). Absent = no intro line.
3745
+ */
3746
+ highlightsIntro?: LocalizedTitle;
3196
3747
  }
3748
+ /** Localized label overrides for {@link Settings.sectionLabels}. */
3749
+ type SectionLabelOverrides = Partial<Record<LabelKey, LocalizedTitle>>;
3197
3750
  /**
3198
3751
  * One skill-category display group (added in 1.3.0). Maps a `Skill.category`
3199
3752
  * value to a localized heading; the array position in {@link
@@ -3249,6 +3802,12 @@ interface AppearanceColors {
3249
3802
  primary?: string;
3250
3803
  /** Foreground color used on top of the primary color. */
3251
3804
  primaryContrast?: string;
3805
+ /**
3806
+ * Heading color for the "About" section's Markdown sub-headings (`##`/`###`).
3807
+ * Defaults to a deep indigo (light) / soft indigo (dark) so sub-headings stand
3808
+ * out from body text; override to re-tint them.
3809
+ */
3810
+ heading?: string;
3252
3811
  }
3253
3812
  /**
3254
3813
  * Owner-curated configuration for the opt-in contact form (added in 1.1.0).
@@ -3300,6 +3859,7 @@ interface PublicVisibility {
3300
3859
  patents?: boolean;
3301
3860
  testScores?: boolean;
3302
3861
  recommendations?: boolean;
3862
+ highlights?: boolean;
3303
3863
  contact?: boolean;
3304
3864
  }
3305
3865
  /**
@@ -3385,6 +3945,7 @@ interface Takuhon {
3385
3945
  patents: Patent[];
3386
3946
  testScores: TestScore[];
3387
3947
  recommendations: Recommendation[];
3948
+ highlights: Highlight[];
3388
3949
  contact: Contact;
3389
3950
  settings: Settings;
3390
3951
  meta: Meta;
@@ -3456,6 +4017,8 @@ interface LocalizedCareer extends VisibilityControlled {
3456
4017
  interface LocalizedProject extends VisibilityControlled {
3457
4018
  id: Slug;
3458
4019
  title: string;
4020
+ /** Resolved {@link Project.role} for the active locale (added in 1.4.0). */
4021
+ role?: string;
3459
4022
  description?: string;
3460
4023
  url?: string;
3461
4024
  tags?: string[];
@@ -3488,6 +4051,11 @@ interface LocalizedMembership extends VisibilityControlled {
3488
4051
  url?: string;
3489
4052
  order?: number;
3490
4053
  }
4054
+ /** {@link SecondaryLink} with its localized label collapsed to a single string. */
4055
+ interface LocalizedSecondaryLink {
4056
+ url: string;
4057
+ label?: string;
4058
+ }
3491
4059
  /** Volunteering with localized fields collapsed to single strings. */
3492
4060
  interface LocalizedVolunteering extends VisibilityControlled {
3493
4061
  id: Slug;
@@ -3499,6 +4067,7 @@ interface LocalizedVolunteering extends VisibilityControlled {
3499
4067
  endDate?: YearMonth | null;
3500
4068
  isCurrent?: boolean;
3501
4069
  url?: string;
4070
+ secondaryLink?: LocalizedSecondaryLink;
3502
4071
  order?: number;
3503
4072
  }
3504
4073
  /** Honor with localized fields collapsed to single strings. */
@@ -3599,6 +4168,30 @@ interface LocalizedRecommendation extends VisibilityControlled {
3599
4168
  relatedEducationId?: Slug;
3600
4169
  order?: number;
3601
4170
  }
4171
+ /** Highlight with `alt`, `title`, `description` collapsed to single strings. */
4172
+ interface LocalizedHighlight extends VisibilityControlled {
4173
+ id: Slug;
4174
+ platform: string;
4175
+ url: string;
4176
+ image: string;
4177
+ alt: string;
4178
+ title: string;
4179
+ description?: string;
4180
+ postedAt?: string;
4181
+ tags?: string[];
4182
+ order?: number;
4183
+ }
4184
+ /**
4185
+ * {@link Skill} after locale resolution: its `label` — a plain string or a
4186
+ * localized map — collapsed to the single resolved string. Every other field
4187
+ * passes through unchanged.
4188
+ */
4189
+ interface LocalizedSkill extends VisibilityControlled {
4190
+ id: Slug;
4191
+ label: string;
4192
+ category?: string;
4193
+ order?: number;
4194
+ }
3602
4195
  /**
3603
4196
  * One skill-category group after locale resolution: the localized `label` map
3604
4197
  * collapsed to the single resolved string. See {@link SkillCategory}.
@@ -3613,17 +4206,22 @@ interface LocalizedSkillCategory {
3613
4206
  * ({@link LocalizedSkillCategory}); every other settings field is passed
3614
4207
  * through unchanged.
3615
4208
  */
3616
- type LocalizedSettings = Omit<Settings, 'skillCategories'> & {
4209
+ type LocalizedSettings = Omit<Settings, 'skillCategories' | 'sectionLabels' | 'highlightsIntro'> & {
3617
4210
  skillCategories?: LocalizedSkillCategory[];
4211
+ /** {@link Settings.sectionLabels} with each value collapsed to the resolved string. */
4212
+ sectionLabels?: Partial<Record<LabelKey, string>>;
4213
+ /** {@link Settings.highlightsIntro} collapsed to the resolved string. */
4214
+ highlightsIntro?: string;
3618
4215
  };
3619
4216
  /**
3620
4217
  * A takuhon document with every localized map flattened to a single string,
3621
4218
  * plus a `resolvedLocale` field recording which tag was actually used as the
3622
4219
  * head of the fallback chain. `resolveLocale()` returns this shape.
3623
4220
  *
3624
- * `Skill`, `Contact`, and `Meta` carry no localized fields and pass through
3625
- * unchanged; `Settings` passes through too, except its `skillCategories`
3626
- * labels are resolved to the single locale string (see {@link LocalizedSettings}).
4221
+ * `Contact` and `Meta` carry no localized fields and pass through unchanged;
4222
+ * `Settings` passes through too, except its `skillCategories` labels are
4223
+ * resolved to the single locale string (see {@link LocalizedSettings}). `Skill`
4224
+ * becomes {@link LocalizedSkill} once its `label` can be a localized map (1.4.0).
3627
4225
  */
3628
4226
  interface LocalizedTakuhon {
3629
4227
  schemaVersion: string;
@@ -3631,7 +4229,7 @@ interface LocalizedTakuhon {
3631
4229
  links: LocalizedLink[];
3632
4230
  careers: LocalizedCareer[];
3633
4231
  projects: LocalizedProject[];
3634
- skills: Skill[];
4232
+ skills: LocalizedSkill[];
3635
4233
  certifications: LocalizedCertification[];
3636
4234
  memberships: LocalizedMembership[];
3637
4235
  volunteering: LocalizedVolunteering[];
@@ -3643,6 +4241,7 @@ interface LocalizedTakuhon {
3643
4241
  patents: LocalizedPatent[];
3644
4242
  testScores: LocalizedTestScore[];
3645
4243
  recommendations: LocalizedRecommendation[];
4244
+ highlights: LocalizedHighlight[];
3646
4245
  contact: Contact;
3647
4246
  settings: LocalizedSettings;
3648
4247
  meta: Meta;
@@ -3690,7 +4289,7 @@ interface LocalizedTakuhon {
3690
4289
  * versions whose JSON Schema this package literally bundles, not the full
3691
4290
  * support window seen by end users.
3692
4291
  */
3693
- declare const SUPPORTED_SCHEMA_VERSIONS: readonly ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.7.0", "1.0.0", "1.1.0", "1.2.0", "1.3.0"];
4292
+ declare const SUPPORTED_SCHEMA_VERSIONS: readonly ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.7.0", "1.0.0", "1.1.0", "1.2.0", "1.3.0", "1.4.0"];
3694
4293
  /**
3695
4294
  * A single validation failure.
3696
4295
  *
@@ -3831,6 +4430,10 @@ declare function resolveLocale(data: Takuhon, locale?: string, fallbackLocale?:
3831
4430
  * no placeholder is fabricated.
3832
4431
  * - `profile.tagline` is intentionally not surfaced. `description` carries
3833
4432
  * `profile.bio` only, matching the spec exemplar. Phase 2 may revisit.
4433
+ * - The curated `highlights` (selected posts) are intentionally NOT surfaced:
4434
+ * Schema.org has no natural vocabulary for an owner-curated set of off-site
4435
+ * posts, so they stay a page/API/MCP feature and are absent from JSON-LD (a
4436
+ * deliberate omission, consistent with the derived CV).
3834
4437
  * - `contact.email` is surfaced only when `contact.showEmail === true`
3835
4438
  * (privacy by default).
3836
4439
  * - URLs pass through verbatim. Relative paths in the input remain relative
@@ -4463,8 +5066,9 @@ declare function renderActivitySvg(snapshot: ActivitySnapshot, options?: RenderA
4463
5066
  * same input.
4464
5067
  *
4465
5068
  * A CV is a curated *subset* of the profile: the web-page-specific sections
4466
- * (links, recommendations, the activity dashboard, test scores) are omitted,
4467
- * and the rest appear in the order a résumé conventionally uses. Entry order
5069
+ * (links, recommendations, the activity dashboard, test scores, and the
5070
+ * curated `highlights` carousel) are omitted, and the rest appear in the order
5071
+ * a résumé conventionally uses. Entry order
4468
5072
  * within a section is preserved from the input (already normalized by `order`
4469
5073
  * upstream), so the owner's `order` field controls it — `deriveCv` never
4470
5074
  * re-sorts. Empty sections are dropped so the renderer can iterate without
@@ -4498,7 +5102,7 @@ type CvSection = {
4498
5102
  entries: LocalizedEducation[];
4499
5103
  } | {
4500
5104
  kind: 'skills';
4501
- entries: Skill[];
5105
+ entries: LocalizedSkill[];
4502
5106
  } | {
4503
5107
  kind: 'certifications';
4504
5108
  entries: LocalizedCertification[];
@@ -4645,7 +5249,7 @@ declare function getPresentLabel(locale: LocaleTag): string;
4645
5249
  * `meta`, `schemaVersion`, and `resolvedLocale` are intentionally excluded —
4646
5250
  * they are configuration / bookkeeping, not profile content an agent reads.
4647
5251
  */
4648
- declare const MCP_PROFILE_SECTIONS: readonly ["profile", "links", "careers", "projects", "skills", "certifications", "memberships", "volunteering", "honors", "education", "publications", "languages", "courses", "patents", "testScores", "recommendations", "contact"];
5252
+ declare const MCP_PROFILE_SECTIONS: readonly ["profile", "links", "careers", "projects", "skills", "certifications", "memberships", "volunteering", "honors", "education", "publications", "languages", "courses", "patents", "testScores", "recommendations", "highlights", "contact"];
4649
5253
  /** One of the {@link MCP_PROFILE_SECTIONS} a `get_section` call accepts. */
4650
5254
  type McpProfileSection = (typeof MCP_PROFILE_SECTIONS)[number];
4651
5255
  /** A JSON Schema (draft 2020-12) object describing a tool's input arguments. */
@@ -4820,6 +5424,6 @@ declare function stripImageMetadata(bytes: Uint8Array, mime: AcceptedImageMime):
4820
5424
  * A takuhon profile document's `schemaVersion` field must be migrate-compatible
4821
5425
  * with this version. See operational-lifecycle docs for the migration policy.
4822
5426
  */
4823
- declare const SCHEMA_VERSION = "1.3.0";
5427
+ declare const SCHEMA_VERSION = "1.4.0";
4824
5428
 
4825
- export { ACCEPTED_IMAGE_MIME_TYPES, type AcceptedImageMime, type ActivitySettings, type ActivitySnapshot, type ActivityStorage, type Address, type AppearanceColors, type AppearanceSettings, type AssetOptions, type AssetRecord, type Avatar, type Career, type Certification, type CodingTime, ConflictError, type Contact, type ContentLicense, type ContentLicenseAttribution, type ContributionCalendar, type ContributionDay, type Course, type CvDocument, type CvHeader, type CvSection, type CvSectionKind, DARK_PALETTE, type Education, type ExportOptions, type ExportedTakuhon, type GravatarOptions, type Honor, IMAGE_EXTENSIONS, type ImageInfo, ImportError, type Iso3166Alpha2, type IsoDateTime, LIGHT_PALETTE, type Language, type LanguageBreakdown, type LanguageProficiency, type Link, type LinkBuiltin, type LinkCustom, type LinkType, type LocaleTag, type LocalizedAddress, type LocalizedAvatar, type LocalizedBody, type LocalizedCareer, type LocalizedCertification, type LocalizedCourse, type LocalizedEducation, type LocalizedHonor, type LocalizedLanguage, type LocalizedLink, type LocalizedLinkBuiltin, type LocalizedLinkCustom, type LocalizedMembership, type LocalizedPatent, type LocalizedProfile, type LocalizedProject, type LocalizedPublication, type LocalizedRecommendation, type LocalizedRecommendationAuthor, type LocalizedTakuhon, type LocalizedTestScore, type LocalizedTitle, type LocalizedVolunteering, MAX_IMAGE_BYTES, MAX_IMAGE_DIMENSION, MAX_IMAGE_FRAMES, MCP_PROFILE_SECTIONS, MCP_RESOURCES, MCP_TOOLS, type McpInputSchema, type McpProfileSection, McpRequestError, type McpResourceDefinition, type McpResourceResult, type McpToolDefinition, type McpToolResult, type Membership, type Meta, type MetaPrivacy, type Migration, MigrationError, type NormalizedTakuhon, NotFoundError, type Palette, type Patent, type PatentStatus, type Profile, type Project, type PublicVisibility, type Publication, RANK_FULL_CODING_HOURS, RANK_FULL_CONTRIBUTIONS, RANK_TIER_THRESHOLDS, type RankInput, type RankTier, type RankTierLabel, type Recommendation, type RecommendationAuthor, type RenderActivitySvgOptions, SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS, type Schema, type Settings, type Skill, type Slug, StorageError, type Takuhon, type TakuhonAssetStorage, type TakuhonStorage, type TestScore, type ValidationError, type ValidationResult, type Volunteering, type YearMonth, applyPublicPrivacyFilter, computeLanguagePercentages, deriveCv, deriveRankTier, detectImageMime, executeMcpTool, exportTakuhon, formatCodingTime, formatDate, generateJsonLd, generatePersonJsonLd, generateProfilePageJsonLd, getPresentLabel, gravatarUrl, importTakuhon, isActivitySnapshot, migrateTakuhon, migrations, normalize, readImageInfo, readMcpResource, renderActivitySvg, resolveLocale, schema, stripImageMetadata, validate };
5429
+ export { ACCEPTED_IMAGE_MIME_TYPES, type AcceptedImageMime, type ActivitySettings, type ActivitySnapshot, type ActivityStorage, type Address, type AppearanceColors, type AppearanceSettings, type AssetOptions, type AssetRecord, type Avatar, CHROME_LABEL_KEYS, type Career, type Certification, type CodingTime, ConflictError, type Contact, type ContentLicense, type ContentLicenseAttribution, type ContributionCalendar, type ContributionDay, type Course, type CvDocument, type CvHeader, type CvSection, type CvSectionKind, DARK_PALETTE, type Education, type ExportOptions, type ExportedTakuhon, type GravatarOptions, type Highlight, type Honor, IMAGE_EXTENSIONS, type ImageInfo, ImportError, type Iso3166Alpha2, type IsoDateTime, LABEL_KEYS, LIGHT_PALETTE, type LabelKey, type Language, type LanguageBreakdown, type LanguageProficiency, type Link, type LinkBuiltin, type LinkCustom, type LinkType, type LocaleTag, type LocalizedAddress, type LocalizedAvatar, type LocalizedBody, type LocalizedCareer, type LocalizedCertification, type LocalizedCourse, type LocalizedEducation, type LocalizedHighlight, type LocalizedHonor, type LocalizedLanguage, type LocalizedLink, type LocalizedLinkBuiltin, type LocalizedLinkCustom, type LocalizedMembership, type LocalizedPatent, type LocalizedProfile, type LocalizedProject, type LocalizedPublication, type LocalizedRecommendation, type LocalizedRecommendationAuthor, type LocalizedSecondaryLink, type LocalizedSkill, type LocalizedTakuhon, type LocalizedTestScore, type LocalizedTitle, type LocalizedVolunteering, MAX_IMAGE_BYTES, MAX_IMAGE_DIMENSION, MAX_IMAGE_FRAMES, MCP_PROFILE_SECTIONS, MCP_RESOURCES, MCP_TOOLS, type McpInputSchema, type McpProfileSection, McpRequestError, type McpResourceDefinition, type McpResourceResult, type McpToolDefinition, type McpToolResult, type Membership, type Meta, type MetaPrivacy, type Migration, MigrationError, type NormalizedTakuhon, NotFoundError, type Palette, type Patent, type PatentStatus, type Profile, type Project, type PublicVisibility, type Publication, RANK_FULL_CODING_HOURS, RANK_FULL_CONTRIBUTIONS, RANK_TIER_THRESHOLDS, type RankInput, type RankTier, type RankTierLabel, type Recommendation, type RecommendationAuthor, type RenderActivitySvgOptions, SCHEMA_VERSION, SECTION_KEYS, SUPPORTED_SCHEMA_VERSIONS, type Schema, type SecondaryLink, type SectionKey, type Settings, type Skill, type Slug, StorageError, type Takuhon, type TakuhonAssetStorage, type TakuhonStorage, type TestScore, type ValidationError, type ValidationResult, type Volunteering, type YearMonth, applyPublicPrivacyFilter, computeLanguagePercentages, deriveCv, deriveRankTier, detectImageMime, executeMcpTool, exportTakuhon, formatCodingTime, formatDate, generateJsonLd, generatePersonJsonLd, generateProfilePageJsonLd, getPresentLabel, gravatarUrl, importTakuhon, isActivitySnapshot, migrateTakuhon, migrations, normalize, readImageInfo, readMcpResource, renderActivitySvg, resolveLocale, schema, stripImageMetadata, validate };