@takuhon/ui 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.
@@ -0,0 +1,51 @@
1
+ .section {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--takuhon-space-3);
5
+ margin-top: var(--takuhon-space-5);
6
+ }
7
+
8
+ .heading {
9
+ margin: 0;
10
+ font-size: var(--takuhon-font-size-xl);
11
+ color: var(--takuhon-color-text);
12
+ }
13
+
14
+ .groups {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: var(--takuhon-space-3);
18
+ }
19
+
20
+ .group {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: var(--takuhon-space-2);
24
+ }
25
+
26
+ .category {
27
+ margin: 0;
28
+ font-size: var(--takuhon-font-size-sm);
29
+ color: var(--takuhon-color-text-muted);
30
+ text-transform: capitalize;
31
+ font-weight: 600;
32
+ }
33
+
34
+ .list {
35
+ list-style: none;
36
+ margin: 0;
37
+ padding: 0;
38
+ display: flex;
39
+ flex-wrap: wrap;
40
+ gap: var(--takuhon-space-2);
41
+ }
42
+
43
+ .item {
44
+ display: inline-block;
45
+ padding: var(--takuhon-space-1) var(--takuhon-space-3);
46
+ border-radius: var(--takuhon-radius-full);
47
+ background-color: var(--takuhon-color-surface);
48
+ border: 1px solid var(--takuhon-color-border);
49
+ font-size: var(--takuhon-font-size-sm);
50
+ color: var(--takuhon-color-text);
51
+ }
@@ -0,0 +1,13 @@
1
+ .root {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--takuhon-space-4);
5
+ max-width: var(--takuhon-max-content-width);
6
+ margin-inline: auto;
7
+ padding: var(--takuhon-space-4);
8
+ background-color: var(--takuhon-color-bg);
9
+ color: var(--takuhon-color-text);
10
+ font-family: var(--takuhon-font-family);
11
+ font-size: var(--takuhon-font-size-base);
12
+ line-height: var(--takuhon-line-height);
13
+ }
@@ -0,0 +1,56 @@
1
+ import { LocalizedTakuhon, LocalizedProfile, LocalizedLink, LocalizedCareer, LocalizedProject, Skill, Contact, LocaleTag } from '@takuhon/core';
2
+
3
+ interface TakuhonProfileProps {
4
+ data: LocalizedTakuhon;
5
+ }
6
+ declare function TakuhonProfile({ data }: TakuhonProfileProps): React.JSX.Element;
7
+
8
+ interface ProfileHeaderProps {
9
+ profile: LocalizedProfile;
10
+ }
11
+ declare function ProfileHeader({ profile }: ProfileHeaderProps): React.JSX.Element;
12
+
13
+ interface LinksListProps {
14
+ links: LocalizedLink[];
15
+ }
16
+ declare function LinksList({ links }: LinksListProps): React.JSX.Element | null;
17
+
18
+ interface CareerTimelineProps {
19
+ careers: LocalizedCareer[];
20
+ }
21
+ declare function CareerTimeline({ careers }: CareerTimelineProps): React.JSX.Element | null;
22
+
23
+ interface ProjectsListProps {
24
+ projects: LocalizedProject[];
25
+ }
26
+ declare function ProjectsList({ projects }: ProjectsListProps): React.JSX.Element | null;
27
+
28
+ interface SkillsListProps {
29
+ skills: Skill[];
30
+ }
31
+ declare function SkillsList({ skills }: SkillsListProps): React.JSX.Element | null;
32
+
33
+ interface ContactInfoProps {
34
+ contact: Contact;
35
+ }
36
+ declare function ContactInfo({ contact }: ContactInfoProps): React.JSX.Element | null;
37
+
38
+ declare function Footer(): React.JSX.Element;
39
+
40
+ interface LocaleSwitcherProps {
41
+ availableLocales: LocaleTag[];
42
+ currentLocale: LocaleTag;
43
+ onSelect: (locale: LocaleTag) => void;
44
+ ariaLabel?: string;
45
+ formatLocale?: (locale: LocaleTag) => string;
46
+ }
47
+ declare function LocaleSwitcher({ availableLocales, currentLocale, onSelect, ariaLabel, formatLocale, }: LocaleSwitcherProps): React.JSX.Element;
48
+
49
+ interface TakuhonHeadProps {
50
+ data: LocalizedTakuhon;
51
+ siteUrl?: string;
52
+ pageUrl?: string;
53
+ }
54
+ declare function TakuhonHead({ data, siteUrl, pageUrl }: TakuhonHeadProps): React.JSX.Element;
55
+
56
+ export { CareerTimeline, type CareerTimelineProps, ContactInfo, type ContactInfoProps, Footer, LinksList, type LinksListProps, LocaleSwitcher, type LocaleSwitcherProps, ProfileHeader, type ProfileHeaderProps, ProjectsList, type ProjectsListProps, SkillsList, type SkillsListProps, TakuhonHead, type TakuhonHeadProps, TakuhonProfile, type TakuhonProfileProps };
package/dist/index.js ADDED
@@ -0,0 +1,384 @@
1
+ // src/components/TakuhonProfile.tsx
2
+ import "./tokens-TEMWJS5E.css";
3
+
4
+ // src/components/CareerTimeline.tsx
5
+ import styles from "./CareerTimeline.module-BGFX3LGA.module.css";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ function sortCareers(careers) {
8
+ return [...careers].sort((a, b) => {
9
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
10
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
11
+ if (aOrder !== bOrder) return aOrder - bOrder;
12
+ return b.startDate.localeCompare(a.startDate);
13
+ });
14
+ }
15
+ function isOngoing(career) {
16
+ return career.isCurrent === true || career.endDate === null || career.endDate === void 0;
17
+ }
18
+ function CareerTimeline({ careers }) {
19
+ if (careers.length === 0) return null;
20
+ const ordered = sortCareers(careers);
21
+ return /* @__PURE__ */ jsxs("section", { className: styles.section, "aria-labelledby": "takuhon-career-heading", children: [
22
+ /* @__PURE__ */ jsx("h2", { id: "takuhon-career-heading", className: styles.heading, children: "Career" }),
23
+ /* @__PURE__ */ jsx("ol", { className: styles.list, children: ordered.map((career) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
24
+ /* @__PURE__ */ jsx("div", { className: styles.timelineMarker, "aria-hidden": "true" }),
25
+ /* @__PURE__ */ jsxs("div", { className: styles.content, children: [
26
+ /* @__PURE__ */ jsx("p", { className: styles.role, children: career.url ? /* @__PURE__ */ jsxs(
27
+ "a",
28
+ {
29
+ className: styles.organizationLink,
30
+ href: career.url,
31
+ target: "_blank",
32
+ rel: "noopener noreferrer",
33
+ children: [
34
+ career.role,
35
+ " \xB7 ",
36
+ career.organization
37
+ ]
38
+ }
39
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
40
+ career.role,
41
+ " \xB7 ",
42
+ career.organization
43
+ ] }) }),
44
+ /* @__PURE__ */ jsxs("p", { className: styles.range, children: [
45
+ /* @__PURE__ */ jsx("time", { dateTime: career.startDate, children: career.startDate }),
46
+ " \u2013 ",
47
+ isOngoing(career) ? "Present" : /* @__PURE__ */ jsx("time", { dateTime: career.endDate, children: career.endDate })
48
+ ] }),
49
+ career.location?.display ? /* @__PURE__ */ jsx("p", { className: styles.location, children: career.location.display }) : null,
50
+ career.description ? /* @__PURE__ */ jsx("p", { className: styles.description, children: career.description }) : null
51
+ ] })
52
+ ] }, career.id)) })
53
+ ] });
54
+ }
55
+
56
+ // src/components/ContactInfo.tsx
57
+ import styles2 from "./ContactInfo.module-7SO24KHC.module.css";
58
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
59
+ function ContactInfo({ contact }) {
60
+ const showEmail = contact.showEmail === true && contact.email !== void 0;
61
+ if (!showEmail && contact.formUrl === void 0) return null;
62
+ return /* @__PURE__ */ jsxs2("section", { className: styles2.section, "aria-labelledby": "takuhon-contact-heading", children: [
63
+ /* @__PURE__ */ jsx2("h2", { id: "takuhon-contact-heading", className: styles2.heading, children: "Contact" }),
64
+ /* @__PURE__ */ jsxs2("ul", { className: styles2.list, children: [
65
+ showEmail && contact.email !== void 0 ? /* @__PURE__ */ jsx2("li", { className: styles2.item, children: /* @__PURE__ */ jsx2("a", { className: styles2.link, href: `mailto:${contact.email}`, children: contact.email }) }) : null,
66
+ contact.formUrl !== void 0 ? /* @__PURE__ */ jsx2("li", { className: styles2.item, children: /* @__PURE__ */ jsx2(
67
+ "a",
68
+ {
69
+ className: styles2.link,
70
+ href: contact.formUrl,
71
+ target: "_blank",
72
+ rel: "noopener noreferrer",
73
+ children: "Contact form"
74
+ }
75
+ ) }) : null
76
+ ] })
77
+ ] });
78
+ }
79
+
80
+ // src/components/Footer.tsx
81
+ import styles3 from "./Footer.module-HLC5GXVI.module.css";
82
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
83
+ function Footer() {
84
+ return /* @__PURE__ */ jsx3("footer", { className: styles3.footer, children: /* @__PURE__ */ jsxs3("p", { className: styles3.line, children: [
85
+ "Powered by",
86
+ " ",
87
+ /* @__PURE__ */ jsx3(
88
+ "a",
89
+ {
90
+ className: styles3.link,
91
+ href: "https://github.com/takuhon-dev/takuhon",
92
+ target: "_blank",
93
+ rel: "noopener noreferrer",
94
+ children: "takuhon"
95
+ }
96
+ )
97
+ ] }) });
98
+ }
99
+
100
+ // src/components/LinksList.tsx
101
+ import styles4 from "./LinksList.module-QPSFPOF3.module.css";
102
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
103
+ function sortLinks(links) {
104
+ return [...links].sort((a, b) => {
105
+ const aFeatured = a.featured ? 1 : 0;
106
+ const bFeatured = b.featured ? 1 : 0;
107
+ if (aFeatured !== bFeatured) return bFeatured - aFeatured;
108
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
109
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
110
+ return aOrder - bOrder;
111
+ });
112
+ }
113
+ function formatLinkLabel(link) {
114
+ if (link.label) return link.label;
115
+ if (link.type === "custom") return link.id;
116
+ return link.type;
117
+ }
118
+ function LinksList({ links }) {
119
+ if (links.length === 0) return null;
120
+ const ordered = sortLinks(links);
121
+ return /* @__PURE__ */ jsx4("nav", { "aria-label": "Profile links", className: styles4.nav, children: /* @__PURE__ */ jsx4("ul", { className: styles4.list, children: ordered.map((link) => /* @__PURE__ */ jsx4("li", { className: styles4.item, children: /* @__PURE__ */ jsxs4(
122
+ "a",
123
+ {
124
+ className: styles4.link,
125
+ href: link.url,
126
+ target: "_blank",
127
+ rel: "noopener noreferrer",
128
+ "data-featured": link.featured ? "true" : void 0,
129
+ children: [
130
+ link.iconUrl ? /* @__PURE__ */ jsx4("img", { className: styles4.icon, src: link.iconUrl, alt: "" }) : null,
131
+ /* @__PURE__ */ jsx4("span", { className: styles4.label, children: formatLinkLabel(link) })
132
+ ]
133
+ }
134
+ ) }, link.id)) }) });
135
+ }
136
+
137
+ // src/components/ProfileHeader.tsx
138
+ import styles5 from "./ProfileHeader.module-EK27UTZL.module.css";
139
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
140
+ function ProfileHeader({ profile }) {
141
+ const locationLabel = profile.location?.display ?? profile.location?.locality;
142
+ return /* @__PURE__ */ jsxs5("header", { className: styles5.header, children: [
143
+ profile.avatar ? /* @__PURE__ */ jsx5(
144
+ "img",
145
+ {
146
+ className: styles5.avatar,
147
+ src: profile.avatar.url,
148
+ alt: profile.avatar.alt ?? "",
149
+ width: 128,
150
+ height: 128,
151
+ loading: "eager",
152
+ decoding: "async"
153
+ }
154
+ ) : null,
155
+ /* @__PURE__ */ jsx5("h1", { className: styles5.displayName, children: profile.displayName }),
156
+ profile.tagline ? /* @__PURE__ */ jsx5("p", { className: styles5.tagline, children: profile.tagline }) : null,
157
+ locationLabel ? /* @__PURE__ */ jsx5("p", { className: styles5.location, children: locationLabel }) : null,
158
+ profile.bio ? /* @__PURE__ */ jsx5("p", { className: styles5.bio, children: profile.bio }) : null
159
+ ] });
160
+ }
161
+
162
+ // src/components/ProjectsList.tsx
163
+ import styles6 from "./ProjectsList.module-JKGGZRKO.module.css";
164
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
165
+ function sortProjects(projects) {
166
+ return [...projects].sort((a, b) => {
167
+ const aHighlighted = a.highlighted ? 1 : 0;
168
+ const bHighlighted = b.highlighted ? 1 : 0;
169
+ if (aHighlighted !== bHighlighted) return bHighlighted - aHighlighted;
170
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
171
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
172
+ return aOrder - bOrder;
173
+ });
174
+ }
175
+ function ProjectsList({ projects }) {
176
+ if (projects.length === 0) return null;
177
+ const ordered = sortProjects(projects);
178
+ return /* @__PURE__ */ jsxs6("section", { className: styles6.section, "aria-labelledby": "takuhon-projects-heading", children: [
179
+ /* @__PURE__ */ jsx6("h2", { id: "takuhon-projects-heading", className: styles6.heading, children: "Projects" }),
180
+ /* @__PURE__ */ jsx6("ul", { className: styles6.list, children: ordered.map((project) => /* @__PURE__ */ jsxs6(
181
+ "li",
182
+ {
183
+ className: styles6.item,
184
+ "data-highlighted": project.highlighted ? "true" : void 0,
185
+ children: [
186
+ /* @__PURE__ */ jsx6("p", { className: styles6.title, children: project.url ? /* @__PURE__ */ jsx6(
187
+ "a",
188
+ {
189
+ className: styles6.titleLink,
190
+ href: project.url,
191
+ target: "_blank",
192
+ rel: "noopener noreferrer",
193
+ children: project.title
194
+ }
195
+ ) : project.title }),
196
+ project.startDate !== void 0 ? /* @__PURE__ */ jsxs6("p", { className: styles6.range, children: [
197
+ /* @__PURE__ */ jsx6("time", { dateTime: project.startDate, children: project.startDate }),
198
+ " \u2013 ",
199
+ project.endDate ? /* @__PURE__ */ jsx6("time", { dateTime: project.endDate, children: project.endDate }) : "Present"
200
+ ] }) : null,
201
+ project.description ? /* @__PURE__ */ jsx6("p", { className: styles6.description, children: project.description }) : null,
202
+ project.tags && project.tags.length > 0 ? /* @__PURE__ */ jsx6("ul", { className: styles6.tags, "aria-label": "Tags", children: project.tags.map((tag) => /* @__PURE__ */ jsx6("li", { className: styles6.tag, children: tag }, tag)) }) : null
203
+ ]
204
+ },
205
+ project.id
206
+ )) })
207
+ ] });
208
+ }
209
+
210
+ // src/components/SkillsList.tsx
211
+ import styles7 from "./SkillsList.module-XIAWE55H.module.css";
212
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
213
+ function groupSkills(skills) {
214
+ const sorted = [...skills].sort((a, b) => {
215
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
216
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
217
+ return aOrder - bOrder;
218
+ });
219
+ const groups = /* @__PURE__ */ new Map();
220
+ for (const skill of sorted) {
221
+ const key = skill.category ?? "other";
222
+ const bucket = groups.get(key);
223
+ if (bucket) {
224
+ bucket.push(skill);
225
+ } else {
226
+ groups.set(key, [skill]);
227
+ }
228
+ }
229
+ return [...groups.entries()].map(([category, items]) => ({ category, skills: items }));
230
+ }
231
+ function SkillsList({ skills }) {
232
+ if (skills.length === 0) return null;
233
+ const groups = groupSkills(skills);
234
+ return /* @__PURE__ */ jsxs7("section", { className: styles7.section, "aria-labelledby": "takuhon-skills-heading", children: [
235
+ /* @__PURE__ */ jsx7("h2", { id: "takuhon-skills-heading", className: styles7.heading, children: "Skills" }),
236
+ /* @__PURE__ */ jsx7("div", { className: styles7.groups, children: groups.map((group) => /* @__PURE__ */ jsxs7("div", { className: styles7.group, children: [
237
+ /* @__PURE__ */ jsx7("h3", { className: styles7.category, children: group.category }),
238
+ /* @__PURE__ */ jsx7("ul", { className: styles7.list, "aria-label": `${group.category} skills`, children: group.skills.map((skill) => /* @__PURE__ */ jsx7("li", { className: styles7.item, children: skill.label }, skill.id)) })
239
+ ] }, group.category)) })
240
+ ] });
241
+ }
242
+
243
+ // src/components/TakuhonProfile.tsx
244
+ import styles8 from "./TakuhonProfile.module-QUEORIHA.module.css";
245
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
246
+ function TakuhonProfile({ data }) {
247
+ const showFooter = data.settings.showPoweredBy !== false;
248
+ return /* @__PURE__ */ jsxs8("article", { className: styles8.root, children: [
249
+ /* @__PURE__ */ jsx8(ProfileHeader, { profile: data.profile }),
250
+ /* @__PURE__ */ jsx8(LinksList, { links: data.links }),
251
+ /* @__PURE__ */ jsx8(CareerTimeline, { careers: data.careers }),
252
+ /* @__PURE__ */ jsx8(ProjectsList, { projects: data.projects }),
253
+ /* @__PURE__ */ jsx8(SkillsList, { skills: data.skills }),
254
+ /* @__PURE__ */ jsx8(ContactInfo, { contact: data.contact }),
255
+ showFooter ? /* @__PURE__ */ jsx8(Footer, {}) : null
256
+ ] });
257
+ }
258
+
259
+ // src/components/LocaleSwitcher.tsx
260
+ import styles9 from "./LocaleSwitcher.module-QVLPVGLG.module.css";
261
+ import { jsx as jsx9 } from "react/jsx-runtime";
262
+ var DEFAULT_ARIA_LABEL = "Select language";
263
+ function LocaleSwitcher({
264
+ availableLocales,
265
+ currentLocale,
266
+ onSelect,
267
+ ariaLabel,
268
+ formatLocale
269
+ }) {
270
+ const label = ariaLabel ?? DEFAULT_ARIA_LABEL;
271
+ const format = formatLocale ?? ((locale) => locale);
272
+ return /* @__PURE__ */ jsx9("div", { className: styles9.wrapper, children: /* @__PURE__ */ jsx9(
273
+ "select",
274
+ {
275
+ className: styles9.select,
276
+ "aria-label": label,
277
+ value: currentLocale,
278
+ onChange: (event) => {
279
+ onSelect(event.target.value);
280
+ },
281
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx9("option", { value: locale, children: format(locale) }, locale))
282
+ }
283
+ ) });
284
+ }
285
+
286
+ // src/components/TakuhonHead.tsx
287
+ import { generateJsonLd } from "@takuhon/core";
288
+ import { useEffect } from "react";
289
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
290
+ function buildLocaleUrl(pageUrl, locale) {
291
+ try {
292
+ const url = new URL(pageUrl);
293
+ url.searchParams.set("lang", locale);
294
+ return url.toString();
295
+ } catch {
296
+ return pageUrl;
297
+ }
298
+ }
299
+ function stripLangParam(pageUrl) {
300
+ try {
301
+ const url = new URL(pageUrl);
302
+ url.searchParams.delete("lang");
303
+ return url.toString();
304
+ } catch {
305
+ return pageUrl;
306
+ }
307
+ }
308
+ function absolutizeUrl(url, base) {
309
+ if (!url) return void 0;
310
+ if (!base) return url;
311
+ try {
312
+ return new URL(url, base).toString();
313
+ } catch {
314
+ return url;
315
+ }
316
+ }
317
+ function detectSiteUrl(explicit) {
318
+ if (explicit) return explicit;
319
+ if (typeof window !== "undefined") return window.location.origin;
320
+ return void 0;
321
+ }
322
+ function detectPageUrl(explicit, siteUrl) {
323
+ if (explicit) return stripLangParam(explicit);
324
+ if (typeof window !== "undefined") {
325
+ return window.location.origin + window.location.pathname;
326
+ }
327
+ return siteUrl;
328
+ }
329
+ function TakuhonHead({ data, siteUrl, pageUrl }) {
330
+ const resolvedSiteUrl = detectSiteUrl(siteUrl);
331
+ const resolvedPageUrl = detectPageUrl(pageUrl, resolvedSiteUrl);
332
+ const { profile, settings, resolvedLocale } = data;
333
+ const description = profile.bio ?? profile.tagline;
334
+ const ogDescription = profile.tagline ?? profile.bio;
335
+ const ogImage = absolutizeUrl(profile.avatar?.url, resolvedSiteUrl);
336
+ const twitterCard = profile.avatar ? "summary_large_image" : "summary";
337
+ const canonical = resolvedPageUrl ? buildLocaleUrl(resolvedPageUrl, resolvedLocale) : void 0;
338
+ const alternates = resolvedPageUrl ? settings.availableLocales.map((loc) => ({
339
+ locale: loc,
340
+ href: buildLocaleUrl(resolvedPageUrl, loc)
341
+ })) : [];
342
+ const xDefaultHref = resolvedPageUrl ? buildLocaleUrl(resolvedPageUrl, settings.defaultLocale) : void 0;
343
+ const ogAlternates = settings.availableLocales.filter((loc) => loc !== resolvedLocale);
344
+ const emitJsonLd = settings.enableJsonLd !== false;
345
+ const jsonLdPayload = emitJsonLd ? JSON.stringify(generateJsonLd(data)) : null;
346
+ useEffect(() => {
347
+ if (!jsonLdPayload) return;
348
+ const script = document.createElement("script");
349
+ script.type = "application/ld+json";
350
+ script.textContent = jsonLdPayload;
351
+ document.head.appendChild(script);
352
+ return () => {
353
+ script.remove();
354
+ };
355
+ }, [jsonLdPayload]);
356
+ return /* @__PURE__ */ jsxs9(Fragment2, { children: [
357
+ /* @__PURE__ */ jsx10("title", { children: profile.displayName }),
358
+ description ? /* @__PURE__ */ jsx10("meta", { name: "description", content: description }) : null,
359
+ canonical ? /* @__PURE__ */ jsx10("link", { rel: "canonical", href: canonical }) : null,
360
+ /* @__PURE__ */ jsx10("meta", { property: "og:title", content: profile.displayName }),
361
+ ogDescription ? /* @__PURE__ */ jsx10("meta", { property: "og:description", content: ogDescription }) : null,
362
+ canonical ? /* @__PURE__ */ jsx10("meta", { property: "og:url", content: canonical }) : null,
363
+ ogImage ? /* @__PURE__ */ jsx10("meta", { property: "og:image", content: ogImage }) : null,
364
+ /* @__PURE__ */ jsx10("meta", { property: "og:type", content: "profile" }),
365
+ /* @__PURE__ */ jsx10("meta", { property: "og:locale", content: resolvedLocale }),
366
+ ogAlternates.map((loc) => /* @__PURE__ */ jsx10("meta", { property: "og:locale:alternate", content: loc }, loc)),
367
+ /* @__PURE__ */ jsx10("meta", { name: "twitter:card", content: twitterCard }),
368
+ alternates.map(({ locale, href }) => /* @__PURE__ */ jsx10("link", { rel: "alternate", hrefLang: locale, href }, locale)),
369
+ xDefaultHref ? /* @__PURE__ */ jsx10("link", { rel: "alternate", hrefLang: "x-default", href: xDefaultHref }) : null
370
+ ] });
371
+ }
372
+ export {
373
+ CareerTimeline,
374
+ ContactInfo,
375
+ Footer,
376
+ LinksList,
377
+ LocaleSwitcher,
378
+ ProfileHeader,
379
+ ProjectsList,
380
+ SkillsList,
381
+ TakuhonHead,
382
+ TakuhonProfile
383
+ };
384
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TakuhonProfile.tsx","../src/components/CareerTimeline.tsx","../src/components/ContactInfo.tsx","../src/components/Footer.tsx","../src/components/LinksList.tsx","../src/components/ProfileHeader.tsx","../src/components/ProjectsList.tsx","../src/components/SkillsList.tsx","../src/components/LocaleSwitcher.tsx","../src/components/TakuhonHead.tsx"],"sourcesContent":["import type { LocalizedTakuhon } from '@takuhon/core';\n\nimport '../styles/tokens.css';\n\nimport { CareerTimeline } from './CareerTimeline.js';\nimport { ContactInfo } from './ContactInfo.js';\nimport { Footer } from './Footer.js';\nimport { LinksList } from './LinksList.js';\nimport { ProfileHeader } from './ProfileHeader.js';\nimport { ProjectsList } from './ProjectsList.js';\nimport { SkillsList } from './SkillsList.js';\nimport styles from './TakuhonProfile.module.css';\n\nexport interface TakuhonProfileProps {\n data: LocalizedTakuhon;\n}\n\nexport function TakuhonProfile({ data }: TakuhonProfileProps): React.JSX.Element {\n const showFooter = data.settings.showPoweredBy !== false;\n\n return (\n <article className={styles.root}>\n <ProfileHeader profile={data.profile} />\n <LinksList links={data.links} />\n <CareerTimeline careers={data.careers} />\n <ProjectsList projects={data.projects} />\n <SkillsList skills={data.skills} />\n <ContactInfo contact={data.contact} />\n {showFooter ? <Footer /> : null}\n </article>\n );\n}\n","import type { LocalizedCareer } from '@takuhon/core';\n\nimport styles from './CareerTimeline.module.css';\n\nexport interface CareerTimelineProps {\n careers: LocalizedCareer[];\n}\n\nfunction sortCareers(careers: LocalizedCareer[]): LocalizedCareer[] {\n return [...careers].sort((a, b) => {\n const aOrder = a.order ?? Number.POSITIVE_INFINITY;\n const bOrder = b.order ?? Number.POSITIVE_INFINITY;\n if (aOrder !== bOrder) return aOrder - bOrder;\n return b.startDate.localeCompare(a.startDate);\n });\n}\n\nfunction isOngoing(career: LocalizedCareer): boolean {\n return career.isCurrent === true || career.endDate === null || career.endDate === undefined;\n}\n\nexport function CareerTimeline({ careers }: CareerTimelineProps): React.JSX.Element | null {\n if (careers.length === 0) return null;\n const ordered = sortCareers(careers);\n\n return (\n <section className={styles.section} aria-labelledby=\"takuhon-career-heading\">\n <h2 id=\"takuhon-career-heading\" className={styles.heading}>\n Career\n </h2>\n <ol className={styles.list}>\n {ordered.map((career) => (\n <li key={career.id} className={styles.item}>\n <div className={styles.timelineMarker} aria-hidden=\"true\" />\n <div className={styles.content}>\n <p className={styles.role}>\n {career.url ? (\n <a\n className={styles.organizationLink}\n href={career.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {career.role} · {career.organization}\n </a>\n ) : (\n <>\n {career.role} · {career.organization}\n </>\n )}\n </p>\n <p className={styles.range}>\n <time dateTime={career.startDate}>{career.startDate}</time>\n {' – '}\n {isOngoing(career) ? (\n 'Present'\n ) : (\n <time dateTime={career.endDate!}>{career.endDate}</time>\n )}\n </p>\n {career.location?.display ? (\n <p className={styles.location}>{career.location.display}</p>\n ) : null}\n {career.description ? (\n <p className={styles.description}>{career.description}</p>\n ) : null}\n </div>\n </li>\n ))}\n </ol>\n </section>\n );\n}\n","import type { Contact } from '@takuhon/core';\n\nimport styles from './ContactInfo.module.css';\n\nexport interface ContactInfoProps {\n contact: Contact;\n}\n\nexport function ContactInfo({ contact }: ContactInfoProps): React.JSX.Element | null {\n const showEmail = contact.showEmail === true && contact.email !== undefined;\n if (!showEmail && contact.formUrl === undefined) return null;\n\n return (\n <section className={styles.section} aria-labelledby=\"takuhon-contact-heading\">\n <h2 id=\"takuhon-contact-heading\" className={styles.heading}>\n Contact\n </h2>\n <ul className={styles.list}>\n {showEmail && contact.email !== undefined ? (\n <li className={styles.item}>\n <a className={styles.link} href={`mailto:${contact.email}`}>\n {contact.email}\n </a>\n </li>\n ) : null}\n {contact.formUrl !== undefined ? (\n <li className={styles.item}>\n <a\n className={styles.link}\n href={contact.formUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Contact form\n </a>\n </li>\n ) : null}\n </ul>\n </section>\n );\n}\n","import styles from './Footer.module.css';\n\nexport function Footer(): React.JSX.Element {\n return (\n <footer className={styles.footer}>\n <p className={styles.line}>\n Powered by{' '}\n <a\n className={styles.link}\n href=\"https://github.com/takuhon-dev/takuhon\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n takuhon\n </a>\n </p>\n </footer>\n );\n}\n","import type { LocalizedLink } from '@takuhon/core';\n\nimport styles from './LinksList.module.css';\n\nexport interface LinksListProps {\n links: LocalizedLink[];\n}\n\nfunction sortLinks(links: LocalizedLink[]): LocalizedLink[] {\n return [...links].sort((a, b) => {\n const aFeatured = a.featured ? 1 : 0;\n const bFeatured = b.featured ? 1 : 0;\n if (aFeatured !== bFeatured) return bFeatured - aFeatured;\n const aOrder = a.order ?? Number.POSITIVE_INFINITY;\n const bOrder = b.order ?? Number.POSITIVE_INFINITY;\n return aOrder - bOrder;\n });\n}\n\nfunction formatLinkLabel(link: LocalizedLink): string {\n if (link.label) return link.label;\n if (link.type === 'custom') return link.id;\n return link.type;\n}\n\nexport function LinksList({ links }: LinksListProps): React.JSX.Element | null {\n if (links.length === 0) return null;\n\n const ordered = sortLinks(links);\n\n return (\n <nav aria-label=\"Profile links\" className={styles.nav}>\n <ul className={styles.list}>\n {ordered.map((link) => (\n <li key={link.id} className={styles.item}>\n <a\n className={styles.link}\n href={link.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-featured={link.featured ? 'true' : undefined}\n >\n {link.iconUrl ? <img className={styles.icon} src={link.iconUrl} alt=\"\" /> : null}\n <span className={styles.label}>{formatLinkLabel(link)}</span>\n </a>\n </li>\n ))}\n </ul>\n </nav>\n );\n}\n","import type { LocalizedProfile } from '@takuhon/core';\n\nimport styles from './ProfileHeader.module.css';\n\nexport interface ProfileHeaderProps {\n profile: LocalizedProfile;\n}\n\nexport function ProfileHeader({ profile }: ProfileHeaderProps): React.JSX.Element {\n const locationLabel = profile.location?.display ?? profile.location?.locality;\n\n return (\n <header className={styles.header}>\n {profile.avatar ? (\n <img\n className={styles.avatar}\n src={profile.avatar.url}\n alt={profile.avatar.alt ?? ''}\n width={128}\n height={128}\n loading=\"eager\"\n decoding=\"async\"\n />\n ) : null}\n <h1 className={styles.displayName}>{profile.displayName}</h1>\n {profile.tagline ? <p className={styles.tagline}>{profile.tagline}</p> : null}\n {locationLabel ? <p className={styles.location}>{locationLabel}</p> : null}\n {profile.bio ? <p className={styles.bio}>{profile.bio}</p> : null}\n </header>\n );\n}\n","import type { LocalizedProject } from '@takuhon/core';\n\nimport styles from './ProjectsList.module.css';\n\nexport interface ProjectsListProps {\n projects: LocalizedProject[];\n}\n\nfunction sortProjects(projects: LocalizedProject[]): LocalizedProject[] {\n return [...projects].sort((a, b) => {\n const aHighlighted = a.highlighted ? 1 : 0;\n const bHighlighted = b.highlighted ? 1 : 0;\n if (aHighlighted !== bHighlighted) return bHighlighted - aHighlighted;\n const aOrder = a.order ?? Number.POSITIVE_INFINITY;\n const bOrder = b.order ?? Number.POSITIVE_INFINITY;\n return aOrder - bOrder;\n });\n}\n\nexport function ProjectsList({ projects }: ProjectsListProps): React.JSX.Element | null {\n if (projects.length === 0) return null;\n const ordered = sortProjects(projects);\n\n return (\n <section className={styles.section} aria-labelledby=\"takuhon-projects-heading\">\n <h2 id=\"takuhon-projects-heading\" className={styles.heading}>\n Projects\n </h2>\n <ul className={styles.list}>\n {ordered.map((project) => (\n <li\n key={project.id}\n className={styles.item}\n data-highlighted={project.highlighted ? 'true' : undefined}\n >\n <p className={styles.title}>\n {project.url ? (\n <a\n className={styles.titleLink}\n href={project.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {project.title}\n </a>\n ) : (\n project.title\n )}\n </p>\n {project.startDate !== undefined ? (\n <p className={styles.range}>\n <time dateTime={project.startDate}>{project.startDate}</time>\n {' – '}\n {project.endDate ? (\n <time dateTime={project.endDate}>{project.endDate}</time>\n ) : (\n 'Present'\n )}\n </p>\n ) : null}\n {project.description ? (\n <p className={styles.description}>{project.description}</p>\n ) : null}\n {project.tags && project.tags.length > 0 ? (\n <ul className={styles.tags} aria-label=\"Tags\">\n {project.tags.map((tag) => (\n <li key={tag} className={styles.tag}>\n {tag}\n </li>\n ))}\n </ul>\n ) : null}\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","import type { Skill } from '@takuhon/core';\n\nimport styles from './SkillsList.module.css';\n\nexport interface SkillsListProps {\n skills: Skill[];\n}\n\ninterface SkillGroup {\n category: string;\n skills: Skill[];\n}\n\nfunction groupSkills(skills: Skill[]): SkillGroup[] {\n const sorted = [...skills].sort((a, b) => {\n const aOrder = a.order ?? Number.POSITIVE_INFINITY;\n const bOrder = b.order ?? Number.POSITIVE_INFINITY;\n return aOrder - bOrder;\n });\n const groups = new Map<string, Skill[]>();\n for (const skill of sorted) {\n const key = skill.category ?? 'other';\n const bucket = groups.get(key);\n if (bucket) {\n bucket.push(skill);\n } else {\n groups.set(key, [skill]);\n }\n }\n return [...groups.entries()].map(([category, items]) => ({ category, skills: items }));\n}\n\nexport function SkillsList({ skills }: SkillsListProps): React.JSX.Element | null {\n if (skills.length === 0) return null;\n const groups = groupSkills(skills);\n\n return (\n <section className={styles.section} aria-labelledby=\"takuhon-skills-heading\">\n <h2 id=\"takuhon-skills-heading\" className={styles.heading}>\n Skills\n </h2>\n <div className={styles.groups}>\n {groups.map((group) => (\n <div key={group.category} className={styles.group}>\n <h3 className={styles.category}>{group.category}</h3>\n <ul className={styles.list} aria-label={`${group.category} skills`}>\n {group.skills.map((skill) => (\n <li key={skill.id} className={styles.item}>\n {skill.label}\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n </section>\n );\n}\n","import type { LocaleTag } from '@takuhon/core';\n\nimport styles from './LocaleSwitcher.module.css';\n\nexport interface LocaleSwitcherProps {\n availableLocales: LocaleTag[];\n currentLocale: LocaleTag;\n onSelect: (locale: LocaleTag) => void;\n ariaLabel?: string;\n formatLocale?: (locale: LocaleTag) => string;\n}\n\nconst DEFAULT_ARIA_LABEL = 'Select language';\n\nexport function LocaleSwitcher({\n availableLocales,\n currentLocale,\n onSelect,\n ariaLabel,\n formatLocale,\n}: LocaleSwitcherProps): React.JSX.Element {\n const label = ariaLabel ?? DEFAULT_ARIA_LABEL;\n const format = formatLocale ?? ((locale: LocaleTag) => locale);\n return (\n <div className={styles.wrapper}>\n <select\n className={styles.select}\n aria-label={label}\n value={currentLocale}\n onChange={(event) => {\n onSelect(event.target.value);\n }}\n >\n {availableLocales.map((locale) => (\n <option key={locale} value={locale}>\n {format(locale)}\n </option>\n ))}\n </select>\n </div>\n );\n}\n","import { generateJsonLd, type LocalizedTakuhon } from '@takuhon/core';\nimport { useEffect } from 'react';\n\nexport interface TakuhonHeadProps {\n data: LocalizedTakuhon;\n siteUrl?: string;\n pageUrl?: string;\n}\n\nfunction buildLocaleUrl(pageUrl: string, locale: string): string {\n try {\n const url = new URL(pageUrl);\n url.searchParams.set('lang', locale);\n return url.toString();\n } catch {\n return pageUrl;\n }\n}\n\nfunction stripLangParam(pageUrl: string): string {\n try {\n const url = new URL(pageUrl);\n url.searchParams.delete('lang');\n return url.toString();\n } catch {\n return pageUrl;\n }\n}\n\nfunction absolutizeUrl(url: string | undefined, base: string | undefined): string | undefined {\n if (!url) return undefined;\n if (!base) return url;\n try {\n return new URL(url, base).toString();\n } catch {\n return url;\n }\n}\n\nfunction detectSiteUrl(explicit: string | undefined): string | undefined {\n if (explicit) return explicit;\n if (typeof window !== 'undefined') return window.location.origin;\n return undefined;\n}\n\nfunction detectPageUrl(\n explicit: string | undefined,\n siteUrl: string | undefined,\n): string | undefined {\n if (explicit) return stripLangParam(explicit);\n if (typeof window !== 'undefined') {\n return window.location.origin + window.location.pathname;\n }\n return siteUrl;\n}\n\nexport function TakuhonHead({ data, siteUrl, pageUrl }: TakuhonHeadProps): React.JSX.Element {\n const resolvedSiteUrl = detectSiteUrl(siteUrl);\n const resolvedPageUrl = detectPageUrl(pageUrl, resolvedSiteUrl);\n\n const { profile, settings, resolvedLocale } = data;\n const description = profile.bio ?? profile.tagline;\n const ogDescription = profile.tagline ?? profile.bio;\n const ogImage = absolutizeUrl(profile.avatar?.url, resolvedSiteUrl);\n const twitterCard = profile.avatar ? 'summary_large_image' : 'summary';\n\n const canonical = resolvedPageUrl ? buildLocaleUrl(resolvedPageUrl, resolvedLocale) : undefined;\n const alternates = resolvedPageUrl\n ? settings.availableLocales.map((loc) => ({\n locale: loc,\n href: buildLocaleUrl(resolvedPageUrl, loc),\n }))\n : [];\n const xDefaultHref = resolvedPageUrl\n ? buildLocaleUrl(resolvedPageUrl, settings.defaultLocale)\n : undefined;\n\n const ogAlternates = settings.availableLocales.filter((loc) => loc !== resolvedLocale);\n\n const emitJsonLd = settings.enableJsonLd !== false;\n const jsonLdPayload = emitJsonLd ? JSON.stringify(generateJsonLd(data)) : null;\n\n useEffect(() => {\n if (!jsonLdPayload) return;\n const script = document.createElement('script');\n script.type = 'application/ld+json';\n script.textContent = jsonLdPayload;\n document.head.appendChild(script);\n return () => {\n script.remove();\n };\n }, [jsonLdPayload]);\n\n return (\n <>\n <title>{profile.displayName}</title>\n {description ? <meta name=\"description\" content={description} /> : null}\n {canonical ? <link rel=\"canonical\" href={canonical} /> : null}\n\n <meta property=\"og:title\" content={profile.displayName} />\n {ogDescription ? <meta property=\"og:description\" content={ogDescription} /> : null}\n {canonical ? <meta property=\"og:url\" content={canonical} /> : null}\n {ogImage ? <meta property=\"og:image\" content={ogImage} /> : null}\n <meta property=\"og:type\" content=\"profile\" />\n <meta property=\"og:locale\" content={resolvedLocale} />\n {ogAlternates.map((loc) => (\n <meta key={loc} property=\"og:locale:alternate\" content={loc} />\n ))}\n\n <meta name=\"twitter:card\" content={twitterCard} />\n\n {alternates.map(({ locale, href }) => (\n <link key={locale} rel=\"alternate\" hrefLang={locale} href={href} />\n ))}\n {xDefaultHref ? <link rel=\"alternate\" hrefLang=\"x-default\" href={xDefaultHref} /> : null}\n </>\n );\n}\n"],"mappings":"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","names":["styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","jsxs","styles","jsx","Fragment","jsx","jsxs"]}
@@ -0,0 +1,55 @@
1
+ :root {
2
+ --takuhon-color-bg: #ffffff;
3
+ --takuhon-color-surface: #f6f7f9;
4
+ --takuhon-color-text: #1f2933;
5
+ --takuhon-color-text-muted: #52606d;
6
+ --takuhon-color-primary: #2563eb;
7
+ --takuhon-color-primary-contrast: #ffffff;
8
+ --takuhon-color-border: #d8dee7;
9
+ --takuhon-color-accent: #4f46e5;
10
+
11
+ --takuhon-space-1: 4px;
12
+ --takuhon-space-2: 8px;
13
+ --takuhon-space-3: 12px;
14
+ --takuhon-space-4: 16px;
15
+ --takuhon-space-5: 24px;
16
+ --takuhon-space-6: 32px;
17
+
18
+ --takuhon-radius-sm: 6px;
19
+ --takuhon-radius-md: 12px;
20
+ --takuhon-radius-lg: 20px;
21
+ --takuhon-radius-full: 9999px;
22
+
23
+ --takuhon-font-family:
24
+ system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
25
+ --takuhon-font-size-sm: 14px;
26
+ --takuhon-font-size-base: 16px;
27
+ --takuhon-font-size-lg: 18px;
28
+ --takuhon-font-size-xl: 22px;
29
+ --takuhon-font-size-2xl: 28px;
30
+ --takuhon-line-height: 1.6;
31
+
32
+ --takuhon-tap-target: 44px;
33
+ --takuhon-max-content-width: 480px;
34
+
35
+ --takuhon-transition-fast: 120ms ease;
36
+ }
37
+
38
+ @media (prefers-color-scheme: dark) {
39
+ :root {
40
+ --takuhon-color-bg: #0f172a;
41
+ --takuhon-color-surface: #1e293b;
42
+ --takuhon-color-text: #e2e8f0;
43
+ --takuhon-color-text-muted: #94a3b8;
44
+ --takuhon-color-primary: #60a5fa;
45
+ --takuhon-color-primary-contrast: #0f172a;
46
+ --takuhon-color-border: #334155;
47
+ --takuhon-color-accent: #818cf8;
48
+ }
49
+ }
50
+
51
+ @media (prefers-reduced-motion: reduce) {
52
+ :root {
53
+ --takuhon-transition-fast: 0ms;
54
+ }
55
+ }