@roottale/cms-renderer-next 0.41.1 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/before-after-client.d.ts +15 -0
  3. package/dist/before-after-client.js +8 -0
  4. package/dist/before-after-client.js.map +1 -0
  5. package/dist/chunk-4LQPZDXZ.js +75 -0
  6. package/dist/chunk-4LQPZDXZ.js.map +1 -0
  7. package/dist/chunk-6GQCFFHU.js +540 -0
  8. package/dist/chunk-6GQCFFHU.js.map +1 -0
  9. package/dist/chunk-F3G6RVIR.js +220 -0
  10. package/dist/chunk-F3G6RVIR.js.map +1 -0
  11. package/dist/chunk-H46JUIHZ.js +79 -0
  12. package/dist/chunk-H46JUIHZ.js.map +1 -0
  13. package/dist/chunk-K62RR4UQ.js +53 -0
  14. package/dist/chunk-K62RR4UQ.js.map +1 -0
  15. package/dist/chunk-NUUTTSGT.js +916 -0
  16. package/dist/chunk-NUUTTSGT.js.map +1 -0
  17. package/dist/chunk-RSNUZ4BF.js +3378 -0
  18. package/dist/chunk-RSNUZ4BF.js.map +1 -0
  19. package/dist/chunk-U5AKACPN.js +113 -0
  20. package/dist/chunk-U5AKACPN.js.map +1 -0
  21. package/dist/chunk-WWS43XCT.js +206 -0
  22. package/dist/chunk-WWS43XCT.js.map +1 -0
  23. package/dist/cms-public.css +164 -51
  24. package/dist/concerns-client.d.ts +12 -0
  25. package/dist/concerns-client.js +8 -0
  26. package/dist/concerns-client.js.map +1 -0
  27. package/dist/expertise-client.d.ts +10 -0
  28. package/dist/expertise-client.js +8 -0
  29. package/dist/expertise-client.js.map +1 -0
  30. package/dist/hero-carousel-client.d.ts +36 -0
  31. package/dist/hero-carousel-client.js +8 -0
  32. package/dist/hero-carousel-client.js.map +1 -0
  33. package/dist/routes.d.ts +51 -5
  34. package/dist/routes.js +109 -62
  35. package/dist/routes.js.map +1 -1
  36. package/dist/seo-checks.d.ts +87 -0
  37. package/dist/seo-checks.js +27 -0
  38. package/dist/seo-checks.js.map +1 -0
  39. package/dist/server.d.ts +51 -2
  40. package/dist/server.js +8 -2
  41. package/dist/site-config-DgYGGwu1.d.ts +3910 -0
  42. package/dist/spaces-client.d.ts +10 -0
  43. package/dist/spaces-client.js +8 -0
  44. package/dist/spaces-client.js.map +1 -0
  45. package/dist/templates.css +3246 -194
  46. package/dist/templates.d.ts +20 -1028
  47. package/dist/templates.js +70 -2
  48. package/dist/templates.js.map +1 -1
  49. package/dist/{theme-css-vars-BiyHBsJG.d.ts → theme-css-vars-CD3jcEkp.d.ts} +2 -2
  50. package/dist/ui-react-motion-client.d.ts +73 -0
  51. package/dist/ui-react-motion-client.js +14 -0
  52. package/dist/ui-react-motion-client.js.map +1 -0
  53. package/dist/validate.d.ts +15 -1
  54. package/dist/validate.js +806 -17
  55. package/dist/validate.js.map +1 -1
  56. package/package.json +18 -6
  57. package/dist/chunk-GOHP7RWD.js +0 -1647
  58. package/dist/chunk-GOHP7RWD.js.map +0 -1
  59. package/dist/chunk-IOYBEH6E.js +0 -271
  60. package/dist/chunk-IOYBEH6E.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @roottale/cms-renderer-next
2
2
 
3
+ ## 0.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d7dfe0a: `postsNotInAnyCollection`(cms-core) — `postsInAnyCollection` 의 대칭으로, 어느 스트림에도 안 속한("미소속") 글만 남긴다.
8
+
9
+ `RootTaleBlogList` 에 `excludeCollections?: boolean` prop 추가 — `collections` 와 함께 주면 스트림 소속 글을 전부 제외해 `/blog` 목록의 스트림 글 누출을 막는다(`collection` 과 배타, `collections` 없이 단독 지정은 throw). 아카이브 스트림(증상/질환/치료)만 선언하고 나머지 글을 `/blog` 로 보여주는 사이트용 — `blog` 자체가 선언 섹션인 사이트는 반대로 `collection="blog"` 가 정경로.
10
+
11
+ - d054d50: 게시글 읽기 경험 리디자인 — 중앙 정렬 읽기 컬럼(`--rt-cms-measure`, 기본 680px)·본문 18px/행간 1.8·크롬리스 타이포그래픽 헤더(제목→요약→메타). 새 테마 변수 `--rt-cms-measure`·`--rt-cms-body-size`·`--rt-cms-scroll-offset`.
12
+ - d6313d2: ADR-0052 A안(콘텐츠 다국어) W4-6 PR C1 — 렌더러 라우팅 + hreflang.
13
+ - `PostMetadata.alternates` 타입 확장: `{ canonical, languages?: Record<string,string> }`. `buildPostMetadata()` 가 새 `translations`(글 응답)·`locales`(defaultLocale/pathFor) 옵션을 받으면 hreflang `alternates.languages` 를 계산한다 — 양방향 + `x-default` = 기본 locale(자기 자신 포함). 신규 export `buildHreflangLanguages()` 를 별도로도 쓸 수 있다(OG `type:"article"` 이 안 맞는 CMS 정적 페이지 등).
14
+ - `createSitemapIndex()` 의 `blog` 섹션이 글 단위 실제 `translations[]` 로 hreflang alternates 를 계산한다(slug 1:1 path-prefix 가정 제거) — 언어판 없는 글은 alternates 생략. `static`/`categories`/`authors` 섹션은 v1 라우트 범위 밖(locale 라우트가 실제로 없음)이라 alternates 를 붙이지 않는다(기존 slug-1:1 스캐폴딩 제거 — breaking for consumers relying on that scaffolding, 그래서 minor).
15
+ - `@roottale/cms-client`: `fetchPageBySlug()` 에 `locale?` 옵션 추가(기존 `fetchPosts`/`fetchPost` 와 동일 시맨틱). `server.ts` 배럴이 `CmsPostTranslationRef` 를 재노출하지 않던 PR B 누락도 함께 수정.
16
+
17
+ ### Patch Changes
18
+
19
+ - 9699c3f: `fetchPosts()`(cms-client)에 `collectionKey?`/`excludeCollections?` 옵션 추가 — 공개 posts API(`GET /v1/cms/public/posts`)의 새 `collection_key=`/`exclude_collections=true` 파라미터를 실어 보낸다. 서버가 페이지네이션 `LIMIT` 전에 스트림 글을 걸러내므로, 아카이브 스트림(증상/질환/치료 등)이 있는 사이트의 `/blog` 목록에서 `limit` 슬롯이 스트림 글에 낭비되던 문제(블로그 글 3개만 남는 왜곡)가 해소된다.
20
+
21
+ `RootTaleBlogList`(cms-renderer-next)가 `excludeCollections`+`collections` 지정 시 이 서버 파라미터를 함께 전달하도록 갱신 — 기존 클라이언트 측 `postsNotInAnyCollection` 필터는 방어선으로 그대로 유지한다(동작 diff 0, `blog-list-exclude-collections.md` 후속).
22
+
23
+ - 68df1be: `jsonLdJson(schema)`(cms-client) — script 태그 없이 XSS-safe(`</script>` 주입 방지) JSON-LD 문자열만 주는 헬퍼. React 소비자가 `<script type="application/ld+json" dangerouslySetInnerHTML>` 로 직접 방출할 때 쓴다(escape 단일 소스). `jsonLdScript` 는 이 헬퍼를 감싼다. cms-renderer-next 의 FAQ JSON-LD 도 동일 헬퍼로 통일(동작 불변).
24
+ - b8588b5: fix: `ImageResponseLike` 반변성 수정 — `element: unknown`·`weight: number` 탓에
25
+ `next/og` 의 실제 `ImageResponse` 가 타입 대입 불가(고객 연동 TS 에러). `element:
26
+ any`(eslint 예외 명시) + satori `Weight` 리터럴 유니온으로 교정. docs 예시
27
+ (`opengraph-image.tsx`)가 이제 strict TS 에서도 그대로 컴파일된다.
28
+ - 97f4ddd: fix: ui-react 모션 아일랜드 번들 내장(noExternal) — npm 미공개 의존 제거로 외부 설치 404 방지. ui-react 정식 publish 후 일반 의존으로 복귀 예정.
29
+ - 804dda1: ADR-0034 §3 / ADR-0052 A안 W4-6 PR C2 — `@roottale/cms-i18n` 1차 실체화(최초 릴리즈).
30
+ - `t(locale, key)` — ko/en 사전(gettext·ICU 플러럴은 Phase 4 몫, 과설계 금지). `cms-renderer-next` 의 고객 노출 기본 문자열(`RootTaleBlogList.emptyMessage`/`allCategoriesLabel`, `RootTaleBlogCategories.emptyMessage`, `RootTaleBlogPost.relatedPostsTitle`/미발견 안내, `RootTalePage` 미발견 안내, `RootTaleTableOfContents.title`)이 명시 prop override 가 없을 때 `t(props.locale, ...)` 로 해석한다. `locale` 미지정(undefined) 또는 사전 없는 locale 은 ko 기본값 — 기존 소비자(단일 언어 사이트)는 diff 0.
31
+
32
+ - 7429d10: ADR-0052 A안(콘텐츠 다국어) W4-6 PR B — `fetchPosts()`/`fetchPost()`(cms-client)에 `locale?` 옵션 추가. 지정 시 그 BCP-47 locale 로 필터, 미지정 시 서버가 사이트 기본 locale(`settingsJson.locales[0]`, 미설정이면 'ko')만 반환한다(하위호환 — 번역판이 생겨도 기존 호출부는 원어 콘텐츠만 계속 본다). 응답 `CmsPostContent`에 `locale?`/`translations?`(같은 글의 발행된 언어판 목록, 이 글 자신 포함) 필드 추가 — 둘 다 구 서버 대비 optional.
33
+
34
+ `createSitemapIndex()`(cms-renderer-next)의 `localizeEntries` 가 fail-closed 로 바뀜: admin `locales` 설정에 값이 있어도, 그 locale 로 발행된 글이 실제로 0건이면 hreflang alternates 생성 대상에서 제외한다(공개 API/공용 함수 시그니처 변경 없음 — 존재하지 않는 `/{locale}/...` URL 을 검색엔진에 알리던 버그 수정).
35
+
36
+ - Updated dependencies [9699c3f]
37
+ - Updated dependencies [d7dfe0a]
38
+ - Updated dependencies [68df1be]
39
+ - Updated dependencies [8e8bc1c]
40
+ - Updated dependencies [7429d10]
41
+ - Updated dependencies [d6313d2]
42
+ - @roottale/cms-client@0.42.0
43
+ - @roottale/cms-core@0.42.0
44
+
45
+ ## 0.41.3
46
+
47
+ ### Patch Changes
48
+
49
+ - fix: 게시글 상세 헤더 제목 max-inline-size 15ch 제거 — 한글 제목 고아 줄바꿈 방지, 행간·패딩 조정 (gpt-5.6-sol high)
50
+
51
+ ## 0.41.2
52
+
53
+ ### Patch Changes
54
+
55
+ - f10aa21: fix: npm 미공개 `@roottale/audit-medical` 을 dependencies 에서 제거하고 번들에 내장(tsup noExternal) — 0.41.0·0.41.1 의 외부 `pnpm install` 404 실패 해소
56
+
3
57
  ## 0.41.1
4
58
 
5
59
  ### Patch Changes
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface BeforeAfterClientImage {
4
+ readonly src: string;
5
+ readonly alt: string;
6
+ }
7
+ interface BeforeAfterSliderProps {
8
+ readonly beforeImage: BeforeAfterClientImage;
9
+ readonly afterImage: BeforeAfterClientImage;
10
+ readonly beforeLabel: string;
11
+ readonly afterLabel: string;
12
+ }
13
+ declare function BeforeAfterSlider({ beforeImage, afterImage, beforeLabel, afterLabel, }: BeforeAfterSliderProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { type BeforeAfterClientImage, BeforeAfterSlider, type BeforeAfterSliderProps };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import {
3
+ BeforeAfterSlider
4
+ } from "./chunk-U5AKACPN.js";
5
+ export {
6
+ BeforeAfterSlider
7
+ };
8
+ //# sourceMappingURL=before-after-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ // src/templates/expertise-client.tsx
4
+ import { useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ function ExpertiseTabStage({
7
+ items
8
+ }) {
9
+ const [selectedId, setSelectedId] = useState(items[0]?.id ?? "");
10
+ const selected = items.find((item) => item.id === selectedId) ?? items[0];
11
+ if (!selected) return null;
12
+ return /* @__PURE__ */ jsxs("div", { className: "rt-tpl-expertise-stage", children: [
13
+ /* @__PURE__ */ jsx(
14
+ "div",
15
+ {
16
+ className: "rt-tpl-expertise-tabs",
17
+ role: "tablist",
18
+ "aria-label": "\uC804\uBB38 \uBD84\uC57C \uC120\uD0DD",
19
+ children: items.map((item) => {
20
+ const active = item.id === selected.id;
21
+ return /* @__PURE__ */ jsxs(
22
+ "button",
23
+ {
24
+ type: "button",
25
+ role: "tab",
26
+ className: "rt-tpl-expertise-tab",
27
+ "aria-selected": active,
28
+ "aria-controls": `rt-expertise-panel-${item.id}`,
29
+ id: `rt-expertise-tab-${item.id}`,
30
+ "data-active": active ? "true" : "false",
31
+ onClick: () => setSelectedId(item.id),
32
+ children: [
33
+ /* @__PURE__ */ jsx("span", { className: "rt-tpl-expertise-tab-label", children: item.label }),
34
+ /* @__PURE__ */ jsx("span", { className: "rt-tpl-expertise-tab-title", children: item.title })
35
+ ]
36
+ },
37
+ item.id
38
+ );
39
+ })
40
+ }
41
+ ),
42
+ /* @__PURE__ */ jsxs(
43
+ "article",
44
+ {
45
+ className: "rt-tpl-expertise-panel",
46
+ role: "tabpanel",
47
+ id: `rt-expertise-panel-${selected.id}`,
48
+ "aria-labelledby": `rt-expertise-tab-${selected.id}`,
49
+ children: [
50
+ selected.imageSrc ? /* @__PURE__ */ jsx(
51
+ "img",
52
+ {
53
+ className: "rt-tpl-expertise-panel-img",
54
+ src: selected.imageSrc,
55
+ alt: selected.imageAlt ?? "",
56
+ loading: "lazy"
57
+ }
58
+ ) : null,
59
+ /* @__PURE__ */ jsxs("div", { className: "rt-tpl-expertise-panel-body", children: [
60
+ selected.kicker ? /* @__PURE__ */ jsx("p", { className: "rt-tpl-expertise-kicker", children: selected.kicker }) : null,
61
+ /* @__PURE__ */ jsx("h3", { className: "rt-tpl-expertise-title", children: selected.title }),
62
+ /* @__PURE__ */ jsx("p", { className: "rt-tpl-expertise-body", children: selected.body }),
63
+ selected.tags ? /* @__PURE__ */ jsx("ul", { className: "rt-tpl-expertise-tags", "aria-label": `${selected.title} \uD0DC\uADF8`, children: selected.tags.map((tag) => /* @__PURE__ */ jsx("li", { children: tag }, tag)) }) : null,
64
+ selected.href ? /* @__PURE__ */ jsx("a", { className: "rt-tpl-expertise-cta", href: selected.href, children: selected.ctaLabel ?? "\uC790\uC138\uD788 \uBCF4\uAE30" }) : null
65
+ ] })
66
+ ]
67
+ }
68
+ )
69
+ ] });
70
+ }
71
+
72
+ export {
73
+ ExpertiseTabStage
74
+ };
75
+ //# sourceMappingURL=chunk-4LQPZDXZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates/expertise-client.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\n\nimport type { ExpertiseSection } from \"./site-config.js\";\n\ntype ExpertiseItem = ExpertiseSection[\"props\"][\"items\"][number];\n\nexport function ExpertiseTabStage({\n items,\n}: {\n readonly items: readonly ExpertiseItem[];\n}) {\n const [selectedId, setSelectedId] = useState(items[0]?.id ?? \"\");\n const selected = items.find((item) => item.id === selectedId) ?? items[0];\n\n if (!selected) return null;\n\n return (\n <div className=\"rt-tpl-expertise-stage\">\n <div\n className=\"rt-tpl-expertise-tabs\"\n role=\"tablist\"\n aria-label=\"전문 분야 선택\"\n >\n {items.map((item) => {\n const active = item.id === selected.id;\n return (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n className=\"rt-tpl-expertise-tab\"\n aria-selected={active}\n aria-controls={`rt-expertise-panel-${item.id}`}\n id={`rt-expertise-tab-${item.id}`}\n data-active={active ? \"true\" : \"false\"}\n onClick={() => setSelectedId(item.id)}\n >\n <span className=\"rt-tpl-expertise-tab-label\">{item.label}</span>\n <span className=\"rt-tpl-expertise-tab-title\">{item.title}</span>\n </button>\n );\n })}\n </div>\n <article\n className=\"rt-tpl-expertise-panel\"\n role=\"tabpanel\"\n id={`rt-expertise-panel-${selected.id}`}\n aria-labelledby={`rt-expertise-tab-${selected.id}`}\n >\n {selected.imageSrc ? (\n <img\n className=\"rt-tpl-expertise-panel-img\"\n src={selected.imageSrc}\n alt={selected.imageAlt ?? \"\"}\n loading=\"lazy\"\n />\n ) : null}\n <div className=\"rt-tpl-expertise-panel-body\">\n {selected.kicker ? (\n <p className=\"rt-tpl-expertise-kicker\">{selected.kicker}</p>\n ) : null}\n <h3 className=\"rt-tpl-expertise-title\">{selected.title}</h3>\n <p className=\"rt-tpl-expertise-body\">{selected.body}</p>\n {selected.tags ? (\n <ul className=\"rt-tpl-expertise-tags\" aria-label={`${selected.title} 태그`}>\n {selected.tags.map((tag) => (\n <li key={tag}>{tag}</li>\n ))}\n </ul>\n ) : null}\n {selected.href ? (\n <a className=\"rt-tpl-expertise-cta\" href={selected.href}>\n {selected.ctaLabel ?? \"자세히 보기\"}\n </a>\n ) : null}\n </div>\n </article>\n </div>\n );\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AA0Bb,SAWE,KAXF;AApBL,SAAS,kBAAkB;AAAA,EAChC;AACF,GAEG;AACD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE;AAC/D,QAAM,WAAW,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU,KAAK,MAAM,CAAC;AAExE,MAAI,CAAC,SAAU,QAAO;AAEtB,SACE,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,cAAW;AAAA,QAEV,gBAAM,IAAI,CAAC,SAAS;AACnB,gBAAM,SAAS,KAAK,OAAO,SAAS;AACpC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,MAAK;AAAA,cACL,WAAU;AAAA,cACV,iBAAe;AAAA,cACf,iBAAe,sBAAsB,KAAK,EAAE;AAAA,cAC5C,IAAI,oBAAoB,KAAK,EAAE;AAAA,cAC/B,eAAa,SAAS,SAAS;AAAA,cAC/B,SAAS,MAAM,cAAc,KAAK,EAAE;AAAA,cAEpC;AAAA,oCAAC,UAAK,WAAU,8BAA8B,eAAK,OAAM;AAAA,gBACzD,oBAAC,UAAK,WAAU,8BAA8B,eAAK,OAAM;AAAA;AAAA;AAAA,YAXpD,KAAK;AAAA,UAYZ;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,IAAI,sBAAsB,SAAS,EAAE;AAAA,QACrC,mBAAiB,oBAAoB,SAAS,EAAE;AAAA,QAE/C;AAAA,mBAAS,WACR;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,KAAK,SAAS;AAAA,cACd,KAAK,SAAS,YAAY;AAAA,cAC1B,SAAQ;AAAA;AAAA,UACV,IACE;AAAA,UACJ,qBAAC,SAAI,WAAU,+BACZ;AAAA,qBAAS,SACR,oBAAC,OAAE,WAAU,2BAA2B,mBAAS,QAAO,IACtD;AAAA,YACJ,oBAAC,QAAG,WAAU,0BAA0B,mBAAS,OAAM;AAAA,YACvD,oBAAC,OAAE,WAAU,yBAAyB,mBAAS,MAAK;AAAA,YACnD,SAAS,OACR,oBAAC,QAAG,WAAU,yBAAwB,cAAY,GAAG,SAAS,KAAK,iBAChE,mBAAS,KAAK,IAAI,CAAC,QAClB,oBAAC,QAAc,iBAAN,GAAU,CACpB,GACH,IACE;AAAA,YACH,SAAS,OACR,oBAAC,OAAE,WAAU,wBAAuB,MAAM,SAAS,MAChD,mBAAS,YAAY,mCACxB,IACE;AAAA,aACN;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}