@roottale/cms-renderer-next 0.46.0 → 0.47.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.
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as ConcernsSection } from './site-config-CeByrPJF.js';
2
+ import { C as ConcernsSection } from './site-config-Dicv57oY.js';
3
3
  import 'zod';
4
4
 
5
5
  type ConcernRegion = ConcernsSection["props"]["regions"][number];
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { E as ExpertiseSection } from './site-config-CeByrPJF.js';
2
+ import { E as ExpertiseSection } from './site-config-Dicv57oY.js';
3
3
  import 'zod';
4
4
 
5
5
  type ExpertiseItem = ExpertiseSection["props"]["items"][number];
@@ -0,0 +1,19 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ declare global {
4
+ interface Window {
5
+ /** beacon.ts RUNTIME 이 노출하는 `send()` — 비콘 미설정 사이트에선 undefined. */
6
+ __rtTrack?: (ev: string, extra?: Record<string, unknown>) => void;
7
+ }
8
+ }
9
+ interface LeadFormMetaProps {
10
+ /** `form_start`/`form_submit_attempt` 차원(form_id). 예: `"contact"`. */
11
+ formId: string;
12
+ }
13
+ /**
14
+ * `RootTaleLeadForm` 의 `<form>` 내부에 렌더 — hidden input 2개(어트리뷰션·
15
+ * 여정) + 폼 계측 리스너. 시각 출력 없음(레이아웃 영향 0).
16
+ */
17
+ declare function LeadFormMeta({ formId }: LeadFormMetaProps): ReactElement;
18
+
19
+ export { LeadFormMeta, type LeadFormMetaProps };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import {
3
+ LeadFormMeta
4
+ } from "./chunk-VG6UUF4D.js";
5
+ export {
6
+ LeadFormMeta
7
+ };
8
+ //# sourceMappingURL=lead-form-meta-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/routes.js CHANGED
@@ -671,7 +671,7 @@ function createFleetInfoRoute(config = {}) {
671
671
  site: config.site ?? env.VERCEL_PROJECT_PRODUCTION_URL ?? null,
672
672
  framework: "next",
673
673
  cms_client: CMS_CLIENT_VERSION,
674
- cms_renderer: true ? "0.46.0" : "dev",
674
+ cms_renderer: true ? "0.47.0" : "dev",
675
675
  build_sha: env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7) ?? null
676
676
  },
677
677
  { headers: { "cache-control": "public, max-age=300" } }
package/dist/server.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  renderBlocks,
18
18
  renderBoundBlocks,
19
19
  themeToCssVars
20
- } from "./chunk-JXRHZGRJ.js";
20
+ } from "./chunk-ZHXYCABN.js";
21
21
  import "./chunk-U5AKACPN.js";
22
22
  import "./chunk-LEHHSI6G.js";
23
23
  import "./chunk-6MFHA555.js";
@@ -25,6 +25,7 @@ import "./chunk-FZK3EXYZ.js";
25
25
  import "./chunk-K62RR4UQ.js";
26
26
  import "./chunk-H46JUIHZ.js";
27
27
  import "./chunk-ZL2NA3QT.js";
28
+ import "./chunk-VG6UUF4D.js";
28
29
  import {
29
30
  collectCategories,
30
31
  collectCategoriesWithCounts,
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { S as SpacesSection } from './site-config-CeByrPJF.js';
2
+ import { S as SpacesSection } from './site-config-Dicv57oY.js';
3
3
  import 'zod';
4
4
 
5
5
  type SpaceItem = SpacesSection["props"]["spaces"][number];
@@ -1,5 +1,5 @@
1
1
  import { ReactElement } from 'react';
2
- import { T as TeamSection } from './site-config-CeByrPJF.js';
2
+ import { T as TeamSection } from './site-config-Dicv57oY.js';
3
3
  import 'zod';
4
4
 
5
5
  type TeamMember = TeamSection["props"]["members"][number];
@@ -1,5 +1,5 @@
1
- import { C as ConcernsSection, E as ExpertiseSection, S as SpacesSection, B as BeforeAfterSection, a as ContactSection, b as CtaBannerSection, F as FaqSection, c as FooterSection, H as HeroSection, d as HoursSection, P as PricingSection, e as ProcessSection, R as RichTextSection, f as ServicesSection, T as TeamSection, g as TestimonialsSection, h as TrustSection, i as SiteConfig, j as SiteConfigInput } from './site-config-CeByrPJF.js';
2
- export { k as BUILTIN_SECTION_SCHEMAS, l as BUILTIN_SECTION_TYPES, m as BeforeAfterSectionSchema, n as BlogListSection, o as BlogListSectionSchema, p as ConcernsSectionSchema, q as ContactSectionSchema, r as CtaBannerSectionSchema, s as CustomSection, t as CustomSectionSchema, u as ExpertiseSectionSchema, v as FaqSectionSchema, w as FooterSectionSchema, x as HeroSectionSchema, y as HoursSectionSchema, L as LeadFormSection, z as LeadFormSectionSchema, N as NavItem, A as NavItemSchema, D as PricingSectionSchema, G as ProcessSectionSchema, I as RichTextSectionSchema, J as SECTION_VARIANTS, K as Section, M as SectionCta, O as SectionInput, Q as SectionSchema, U as ServicesSectionSchema, V as SiteConfigSchema, W as SpacesSectionSchema, X as TeamSectionSchema, Y as TestimonialsSectionSchema, Z as TrustSectionSchema, _ as defineSiteConfig, $ as parseSiteConfig } from './site-config-CeByrPJF.js';
1
+ import { C as ConcernsSection, E as ExpertiseSection, S as SpacesSection, B as BeforeAfterSection, a as ContactSection, b as CtaBannerSection, F as FaqSection, c as FooterSection, H as HeroSection, d as HoursSection, P as PricingSection, e as ProcessSection, R as RichTextSection, f as ServicesSection, T as TeamSection, g as TestimonialsSection, h as TrustSection, i as SiteConfig, j as SiteConfigInput } from './site-config-Dicv57oY.js';
2
+ export { k as BUILTIN_SECTION_SCHEMAS, l as BUILTIN_SECTION_TYPES, m as BeforeAfterSectionSchema, n as BlogListSection, o as BlogListSectionSchema, p as ConcernsSectionSchema, q as ContactSectionSchema, r as CtaBannerSectionSchema, s as CustomSection, t as CustomSectionSchema, u as ExpertiseSectionSchema, v as FaqSectionSchema, w as FooterSectionSchema, x as HeroSectionSchema, y as HoursSectionSchema, L as LeadFormSection, z as LeadFormSectionSchema, N as NavItem, A as NavItemSchema, D as PricingSectionSchema, G as ProcessSectionSchema, I as RichTextSectionSchema, J as SECTION_VARIANTS, K as Section, M as SectionCta, O as SectionInput, Q as SectionSchema, U as ServicesSectionSchema, V as SiteConfigSchema, W as SpacesSectionSchema, X as TeamSectionSchema, Y as TestimonialsSectionSchema, Z as TrustSectionSchema, _ as defineSiteConfig, $ as parseSiteConfig } from './site-config-Dicv57oY.js';
3
3
  export { R as RootTaleCssVars, t as themeToCssVars } from './theme-css-vars-CD3jcEkp.js';
4
4
  import { ReactElement, CSSProperties, ReactNode } from 'react';
5
5
  import 'zod';
package/dist/templates.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  TemplateTrust,
21
21
  sectionThemeStyle,
22
22
  themeToCssVars
23
- } from "./chunk-JXRHZGRJ.js";
23
+ } from "./chunk-ZHXYCABN.js";
24
24
  import "./chunk-U5AKACPN.js";
25
25
  import "./chunk-LEHHSI6G.js";
26
26
  import "./chunk-6MFHA555.js";
@@ -28,6 +28,7 @@ import "./chunk-FZK3EXYZ.js";
28
28
  import "./chunk-K62RR4UQ.js";
29
29
  import "./chunk-H46JUIHZ.js";
30
30
  import "./chunk-ZL2NA3QT.js";
31
+ import "./chunk-VG6UUF4D.js";
31
32
  import "./chunk-FUA5H6JE.js";
32
33
  import {
33
34
  BUILTIN_SECTION_SCHEMAS,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/templates/SiteRenderer.tsx"],"sourcesContent":["/**\n * `SiteRenderer` — `SiteConfig.pages[path]` 섹션 배열을 registry 로 렌더.\n *\n * ADR-0040 P2 + P0.5 RFC §①:\n * - 빌트인 registry + 클라 주입 `customSections` 머지. **빌트인이 우선** —\n * 클라가 빌트인 type 을 가로채 계약이 갈라지는 것 방지 (고유 섹션은 고유\n * type 으로).\n * - **render-prop 금지**: 임의 JSX escape hatch 는 customSections registry 로만.\n * - **admin preview parity**: admin draft/preview 도 동일 `SiteRenderer` + 동일\n * 빌트인 registry 를 탄다. customSections 는 클라 repo 자산 — preview 에선\n * 미주입 → `unknownSection: \"placeholder\"` 로 skeleton 표시.\n * - cms-bound 섹션(blog-list/lead-form)은 `RootTaleBlogList`/`RootTaleLeadForm`\n * 위임 (단방향: templates → renderer 코어).\n */\nimport {\n Fragment,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { RootTaleLeadForm } from \"../LeadForm.js\";\nimport { RootTaleBlogList } from \"../server.js\";\nimport {\n TemplateConcerns,\n TemplateExpertise,\n TemplateSpaces,\n} from \"./professional-sections.js\";\nimport {\n TemplateBeforeAfter,\n TemplateContact,\n TemplateCtaBanner,\n TemplateFaq,\n TemplateFooter,\n TemplateHero,\n TemplateHours,\n TemplatePricing,\n TemplateProcess,\n TemplateRichText,\n TemplateServices,\n TemplateTeam,\n TemplateTestimonials,\n TemplateTrust,\n sectionThemeStyle,\n} from \"./sections.js\";\nimport {\n type BeforeAfterSection,\n type BlogListSection,\n type ConcernsSection,\n type ContactSection,\n type CtaBannerSection,\n type ExpertiseSection,\n type FaqSection,\n type FooterSection,\n type HeroSection,\n type HoursSection,\n type LeadFormSection,\n type PricingSection,\n type ProcessSection,\n type RichTextSection,\n type Section,\n type ServicesSection,\n type SpacesSection,\n type SiteConfig,\n type SiteConfigInput,\n type TeamSection,\n type TestimonialsSection,\n type TrustSection,\n parseSiteConfig,\n} from \"./site-config.js\";\n\n/** customSections registry 컴포넌트 계약 (RFC §①). */\nexport type SectionComponent = (p: {\n id?: string;\n props: Record<string, unknown>;\n variant?: string;\n theme?: Record<string, string>;\n}) => ReactNode;\n\nexport interface SiteRendererProps {\n /** `defineSiteConfig()` 결과 또는 raw config (내부에서 검증). */\n config: SiteConfig | SiteConfigInput;\n /** 현재 페이지 경로 — `config.pages` 의 key (\"/\", \"/services\", …). */\n path: string;\n /** cms-bound 섹션(blog-list/lead-form 의 theme fetch) 위임용. 서버 전용. */\n apiKey: string;\n /** self-host 등 API base override (`ROOTTALE_API_BASE`). */\n baseUrl?: string;\n /** 클라 repo 고유 섹션 registry (escape hatch). 빌트인 type 은 가로챌 수 없음. */\n customSections?: Record<string, SectionComponent>;\n /**\n * registry 에 없는 type 처리. 기본 \"warn-dev\" = dev 에서 경고 박스, prod 에서\n * 생략. admin preview 는 \"placeholder\" (customSections 미주입 환경).\n */\n unknownSection?: \"warn-dev\" | \"placeholder\" | \"ignore\";\n}\n\nfunction isDev(): boolean {\n // dts 빌드는 node types 없이 돈다 — process 는 globalThis 경유로 안전 접근.\n const env =\n (globalThis as { process?: { env?: Record<string, string | undefined> } })\n .process?.env ?? {};\n return env.NODE_ENV !== \"production\";\n}\n\nfunction UnknownSection({\n type,\n mode,\n}: {\n type: string;\n mode: \"warn-dev\" | \"placeholder\" | \"ignore\";\n}): ReactElement | null {\n if (mode === \"ignore\") return null;\n if (mode === \"placeholder\") {\n return (\n <section\n data-roottale-tpl=\"placeholder\"\n className=\"rt-tpl-section rt-tpl-placeholder\"\n aria-hidden\n >\n <div className=\"rt-tpl-container\">\n <div className=\"rt-tpl-placeholder-box\">{type}</div>\n </div>\n </section>\n );\n }\n if (!isDev()) return null;\n const style: CSSProperties = {\n padding: \"1rem\",\n border: \"1px dashed #c00\",\n color: \"#c00\",\n };\n return (\n <div style={style}>\n 알 수 없는 섹션 type: <code>{type}</code> — SiteRenderer 의 customSections\n 에 등록하세요.\n </div>\n );\n}\n\nfunction renderBuiltin(\n section: Section,\n ctx: { apiKey: string; baseUrl?: string },\n): ReactNode | undefined {\n switch (section.type) {\n case \"hero\":\n return <TemplateHero {...(section as HeroSection)} />;\n case \"rich-text\":\n return <TemplateRichText {...(section as RichTextSection)} />;\n case \"services\":\n return <TemplateServices {...(section as ServicesSection)} />;\n case \"faq\":\n return <TemplateFaq {...(section as FaqSection)} />;\n case \"footer\":\n return <TemplateFooter {...(section as FooterSection)} />;\n case \"cta-banner\":\n return <TemplateCtaBanner {...(section as CtaBannerSection)} />;\n case \"blog-list\": {\n const s = section as BlogListSection;\n const base = s.props.blogBasePath ?? \"/blog\";\n return (\n <section\n data-roottale-tpl=\"blog-list\"\n data-roottale-cms=\"list\"\n id={s.id}\n className=\"rt-tpl-section rt-tpl-blog-list\"\n style={sectionThemeStyle(s.theme)}\n >\n <div className=\"rt-tpl-container\">\n {s.props.heading ? (\n <h2 className=\"rt-tpl-heading\">{s.props.heading}</h2>\n ) : null}\n <RootTaleBlogList\n apiKey={ctx.apiKey}\n baseUrl={ctx.baseUrl}\n limit={s.props.limit}\n emptyMessage={s.props.emptyMessage}\n postHref={(post) => `${base}/${post.slug}`}\n />\n </div>\n </section>\n );\n }\n case \"lead-form\": {\n const s = section as LeadFormSection;\n return (\n <section\n data-roottale-tpl=\"lead-form\"\n data-roottale-cms=\"lead-form\"\n id={s.id}\n className=\"rt-tpl-section rt-tpl-lead-form\"\n style={sectionThemeStyle(s.theme)}\n >\n <div className=\"rt-tpl-container\">\n <RootTaleLeadForm\n action={s.props.action}\n vertical={s.props.vertical}\n heading={s.props.heading}\n description={s.props.description}\n submitLabel={s.props.submitLabel}\n />\n </div>\n </section>\n );\n }\n case \"trust\":\n return <TemplateTrust {...(section as TrustSection)} />;\n case \"contact\":\n return <TemplateContact {...(section as ContactSection)} />;\n case \"hours\":\n return <TemplateHours {...(section as HoursSection)} />;\n case \"pricing\":\n return <TemplatePricing {...(section as PricingSection)} />;\n case \"process\":\n return <TemplateProcess {...(section as ProcessSection)} />;\n case \"before-after\":\n return <TemplateBeforeAfter {...(section as BeforeAfterSection)} />;\n case \"testimonials\":\n return <TemplateTestimonials {...(section as TestimonialsSection)} />;\n case \"expertise\":\n return <TemplateExpertise {...(section as ExpertiseSection)} />;\n case \"spaces\":\n return <TemplateSpaces {...(section as SpacesSection)} />;\n case \"concerns\":\n return <TemplateConcerns {...(section as ConcernsSection)} />;\n case \"team\":\n return <TemplateTeam {...(section as TeamSection)} />;\n default:\n return undefined;\n }\n}\n\n/**\n * 한 페이지의 섹션 배열을 순서대로 렌더.\n *\n * ```tsx\n * // app/services/page.tsx\n * import config from \"@/site.config\";\n * import { SiteRenderer } from \"@roottale/cms-renderer-next/templates\";\n *\n * export default function ServicesPage() {\n * return (\n * <SiteRenderer config={config} path=\"/services\"\n * apiKey={process.env.ROOTTALE_API_KEY!} />\n * );\n * }\n * ```\n */\nexport function SiteRenderer(props: SiteRendererProps): ReactElement {\n const {\n path,\n apiKey,\n baseUrl,\n customSections = {},\n unknownSection = \"warn-dev\",\n } = props;\n const config = parseSiteConfig(props.config);\n const sections = config.pages[path] ?? [];\n return (\n <>\n {sections.length === 0 ? (\n <UnknownSection\n type={`pages[\"${path}\"] (섹션 0개)`}\n mode={unknownSection}\n />\n ) : null}\n {sections.map((section, i) => {\n const key = section.id ?? `${section.type}-${i}`;\n const builtin = renderBuiltin(section, { apiKey, baseUrl });\n if (builtin !== undefined) {\n return <Fragment key={key}>{builtin}</Fragment>;\n }\n const Custom = customSections[section.type];\n if (Custom) {\n return (\n <Fragment key={key}>\n {Custom({\n id: section.id,\n props: section.props as Record<string, unknown>,\n variant: section.variant,\n theme: section.theme,\n })}\n </Fragment>\n );\n }\n return (\n <UnknownSection key={key} type={section.type} mode={unknownSection} />\n );\n })}\n </>\n );\n}\n"],"mappings":"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","names":["Fragment"]}
1
+ {"version":3,"sources":["../src/templates/SiteRenderer.tsx"],"sourcesContent":["/**\n * `SiteRenderer` — `SiteConfig.pages[path]` 섹션 배열을 registry 로 렌더.\n *\n * ADR-0040 P2 + P0.5 RFC §①:\n * - 빌트인 registry + 클라 주입 `customSections` 머지. **빌트인이 우선** —\n * 클라가 빌트인 type 을 가로채 계약이 갈라지는 것 방지 (고유 섹션은 고유\n * type 으로).\n * - **render-prop 금지**: 임의 JSX escape hatch 는 customSections registry 로만.\n * - **admin preview parity**: admin draft/preview 도 동일 `SiteRenderer` + 동일\n * 빌트인 registry 를 탄다. customSections 는 클라 repo 자산 — preview 에선\n * 미주입 → `unknownSection: \"placeholder\"` 로 skeleton 표시.\n * - cms-bound 섹션(blog-list/lead-form)은 `RootTaleBlogList`/`RootTaleLeadForm`\n * 위임 (단방향: templates → renderer 코어).\n */\nimport {\n Fragment,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { RootTaleLeadForm } from \"../LeadForm.js\";\nimport { RootTaleBlogList } from \"../server.js\";\nimport {\n TemplateConcerns,\n TemplateExpertise,\n TemplateSpaces,\n} from \"./professional-sections.js\";\nimport {\n TemplateBeforeAfter,\n TemplateContact,\n TemplateCtaBanner,\n TemplateFaq,\n TemplateFooter,\n TemplateHero,\n TemplateHours,\n TemplatePricing,\n TemplateProcess,\n TemplateRichText,\n TemplateServices,\n TemplateTeam,\n TemplateTestimonials,\n TemplateTrust,\n sectionThemeStyle,\n} from \"./sections.js\";\nimport {\n type BeforeAfterSection,\n type BlogListSection,\n type ConcernsSection,\n type ContactSection,\n type CtaBannerSection,\n type ExpertiseSection,\n type FaqSection,\n type FooterSection,\n type HeroSection,\n type HoursSection,\n type LeadFormSection,\n type PricingSection,\n type ProcessSection,\n type RichTextSection,\n type Section,\n type ServicesSection,\n type SpacesSection,\n type SiteConfig,\n type SiteConfigInput,\n type TeamSection,\n type TestimonialsSection,\n type TrustSection,\n parseSiteConfig,\n} from \"./site-config.js\";\n\n/** customSections registry 컴포넌트 계약 (RFC §①). */\nexport type SectionComponent = (p: {\n id?: string;\n props: Record<string, unknown>;\n variant?: string;\n theme?: Record<string, string>;\n}) => ReactNode;\n\nexport interface SiteRendererProps {\n /** `defineSiteConfig()` 결과 또는 raw config (내부에서 검증). */\n config: SiteConfig | SiteConfigInput;\n /** 현재 페이지 경로 — `config.pages` 의 key (\"/\", \"/services\", …). */\n path: string;\n /** cms-bound 섹션(blog-list/lead-form 의 theme fetch) 위임용. 서버 전용. */\n apiKey: string;\n /** self-host 등 API base override (`ROOTTALE_API_BASE`). */\n baseUrl?: string;\n /** 클라 repo 고유 섹션 registry (escape hatch). 빌트인 type 은 가로챌 수 없음. */\n customSections?: Record<string, SectionComponent>;\n /**\n * registry 에 없는 type 처리. 기본 \"warn-dev\" = dev 에서 경고 박스, prod 에서\n * 생략. admin preview 는 \"placeholder\" (customSections 미주입 환경).\n */\n unknownSection?: \"warn-dev\" | \"placeholder\" | \"ignore\";\n}\n\nfunction isDev(): boolean {\n // dts 빌드는 node types 없이 돈다 — process 는 globalThis 경유로 안전 접근.\n const env =\n (globalThis as { process?: { env?: Record<string, string | undefined> } })\n .process?.env ?? {};\n return env.NODE_ENV !== \"production\";\n}\n\nfunction UnknownSection({\n type,\n mode,\n}: {\n type: string;\n mode: \"warn-dev\" | \"placeholder\" | \"ignore\";\n}): ReactElement | null {\n if (mode === \"ignore\") return null;\n if (mode === \"placeholder\") {\n return (\n <section\n data-roottale-tpl=\"placeholder\"\n className=\"rt-tpl-section rt-tpl-placeholder\"\n aria-hidden\n >\n <div className=\"rt-tpl-container\">\n <div className=\"rt-tpl-placeholder-box\">{type}</div>\n </div>\n </section>\n );\n }\n if (!isDev()) return null;\n const style: CSSProperties = {\n padding: \"1rem\",\n border: \"1px dashed #c00\",\n color: \"#c00\",\n };\n return (\n <div style={style}>\n 알 수 없는 섹션 type: <code>{type}</code> — SiteRenderer 의 customSections\n 에 등록하세요.\n </div>\n );\n}\n\nfunction renderBuiltin(\n section: Section,\n ctx: { apiKey: string; baseUrl?: string },\n): ReactNode | undefined {\n switch (section.type) {\n case \"hero\":\n return <TemplateHero {...(section as HeroSection)} />;\n case \"rich-text\":\n return <TemplateRichText {...(section as RichTextSection)} />;\n case \"services\":\n return <TemplateServices {...(section as ServicesSection)} />;\n case \"faq\":\n return <TemplateFaq {...(section as FaqSection)} />;\n case \"footer\":\n return <TemplateFooter {...(section as FooterSection)} />;\n case \"cta-banner\":\n return <TemplateCtaBanner {...(section as CtaBannerSection)} />;\n case \"blog-list\": {\n const s = section as BlogListSection;\n const base = s.props.blogBasePath ?? \"/blog\";\n return (\n <section\n data-roottale-tpl=\"blog-list\"\n data-roottale-cms=\"list\"\n id={s.id}\n className=\"rt-tpl-section rt-tpl-blog-list\"\n style={sectionThemeStyle(s.theme)}\n >\n <div className=\"rt-tpl-container\">\n {s.props.heading ? (\n <h2 className=\"rt-tpl-heading\">{s.props.heading}</h2>\n ) : null}\n <RootTaleBlogList\n apiKey={ctx.apiKey}\n baseUrl={ctx.baseUrl}\n limit={s.props.limit}\n emptyMessage={s.props.emptyMessage}\n postHref={(post) => `${base}/${post.slug}`}\n />\n </div>\n </section>\n );\n }\n case \"lead-form\": {\n const s = section as LeadFormSection;\n return (\n <section\n data-roottale-tpl=\"lead-form\"\n data-roottale-cms=\"lead-form\"\n id={s.id}\n className=\"rt-tpl-section rt-tpl-lead-form\"\n style={sectionThemeStyle(s.theme)}\n >\n <div className=\"rt-tpl-container\">\n <RootTaleLeadForm\n action={s.props.action}\n vertical={s.props.vertical}\n heading={s.props.heading}\n description={s.props.description}\n submitLabel={s.props.submitLabel}\n />\n </div>\n </section>\n );\n }\n case \"trust\":\n return <TemplateTrust {...(section as TrustSection)} />;\n case \"contact\":\n return <TemplateContact {...(section as ContactSection)} />;\n case \"hours\":\n return <TemplateHours {...(section as HoursSection)} />;\n case \"pricing\":\n return <TemplatePricing {...(section as PricingSection)} />;\n case \"process\":\n return <TemplateProcess {...(section as ProcessSection)} />;\n case \"before-after\":\n return <TemplateBeforeAfter {...(section as BeforeAfterSection)} />;\n case \"testimonials\":\n return <TemplateTestimonials {...(section as TestimonialsSection)} />;\n case \"expertise\":\n return <TemplateExpertise {...(section as ExpertiseSection)} />;\n case \"spaces\":\n return <TemplateSpaces {...(section as SpacesSection)} />;\n case \"concerns\":\n return <TemplateConcerns {...(section as ConcernsSection)} />;\n case \"team\":\n return <TemplateTeam {...(section as TeamSection)} />;\n default:\n return undefined;\n }\n}\n\n/**\n * 한 페이지의 섹션 배열을 순서대로 렌더.\n *\n * ```tsx\n * // app/services/page.tsx\n * import config from \"@/site.config\";\n * import { SiteRenderer } from \"@roottale/cms-renderer-next/templates\";\n *\n * export default function ServicesPage() {\n * return (\n * <SiteRenderer config={config} path=\"/services\"\n * apiKey={process.env.ROOTTALE_API_KEY!} />\n * );\n * }\n * ```\n */\nexport function SiteRenderer(props: SiteRendererProps): ReactElement {\n const {\n path,\n apiKey,\n baseUrl,\n customSections = {},\n unknownSection = \"warn-dev\",\n } = props;\n const config = parseSiteConfig(props.config);\n const sections = config.pages[path] ?? [];\n return (\n <>\n {sections.length === 0 ? (\n <UnknownSection\n type={`pages[\"${path}\"] (섹션 0개)`}\n mode={unknownSection}\n />\n ) : null}\n {sections.map((section, i) => {\n const key = section.id ?? `${section.type}-${i}`;\n const builtin = renderBuiltin(section, { apiKey, baseUrl });\n if (builtin !== undefined) {\n return <Fragment key={key}>{builtin}</Fragment>;\n }\n const Custom = customSections[section.type];\n if (Custom) {\n return (\n <Fragment key={key}>\n {Custom({\n id: section.id,\n props: section.props as Record<string, unknown>,\n variant: section.variant,\n theme: section.theme,\n })}\n </Fragment>\n );\n }\n return (\n <UnknownSection key={key} type={section.type} mode={unknownSection} />\n );\n })}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA;AAAA,EACE;AAAA,OAIK;AAsGG,SA0IN,YAAAA,WA1IM,KAYN,YAZM;AAxBV,SAAS,QAAiB;AAExB,QAAM,MACH,WACE,SAAS,OAAO,CAAC;AACtB,SAAO,IAAI,aAAa;AAC1B;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AACF,GAGwB;AACtB,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI,SAAS,eAAe;AAC1B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,qBAAkB;AAAA,QAClB,WAAU;AAAA,QACV,eAAW;AAAA,QAEX,8BAAC,SAAI,WAAU,oBACb,8BAAC,SAAI,WAAU,0BAA0B,gBAAK,GAChD;AAAA;AAAA,IACF;AAAA,EAEJ;AACA,MAAI,CAAC,MAAM,EAAG,QAAO;AACrB,QAAM,QAAuB;AAAA,IAC3B,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,EACT;AACA,SACE,qBAAC,SAAI,OAAc;AAAA;AAAA,IACD,oBAAC,UAAM,gBAAK;AAAA,IAAO;AAAA,KAErC;AAEJ;AAEA,SAAS,cACP,SACA,KACuB;AACvB,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AACH,aAAO,oBAAC,gBAAc,GAAI,SAAyB;AAAA,IACrD,KAAK;AACH,aAAO,oBAAC,oBAAkB,GAAI,SAA6B;AAAA,IAC7D,KAAK;AACH,aAAO,oBAAC,oBAAkB,GAAI,SAA6B;AAAA,IAC7D,KAAK;AACH,aAAO,oBAAC,eAAa,GAAI,SAAwB;AAAA,IACnD,KAAK;AACH,aAAO,oBAAC,kBAAgB,GAAI,SAA2B;AAAA,IACzD,KAAK;AACH,aAAO,oBAAC,qBAAmB,GAAI,SAA8B;AAAA,IAC/D,KAAK,aAAa;AAChB,YAAM,IAAI;AACV,YAAM,OAAO,EAAE,MAAM,gBAAgB;AACrC,aACE;AAAA,QAAC;AAAA;AAAA,UACC,qBAAkB;AAAA,UAClB,qBAAkB;AAAA,UAClB,IAAI,EAAE;AAAA,UACN,WAAU;AAAA,UACV,OAAO,kBAAkB,EAAE,KAAK;AAAA,UAEhC,+BAAC,SAAI,WAAU,oBACZ;AAAA,cAAE,MAAM,UACP,oBAAC,QAAG,WAAU,kBAAkB,YAAE,MAAM,SAAQ,IAC9C;AAAA,YACJ;AAAA,cAAC;AAAA;AAAA,gBACC,QAAQ,IAAI;AAAA,gBACZ,SAAS,IAAI;AAAA,gBACb,OAAO,EAAE,MAAM;AAAA,gBACf,cAAc,EAAE,MAAM;AAAA,gBACtB,UAAU,CAAC,SAAS,GAAG,IAAI,IAAI,KAAK,IAAI;AAAA;AAAA,YAC1C;AAAA,aACF;AAAA;AAAA,MACF;AAAA,IAEJ;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,IAAI;AACV,aACE;AAAA,QAAC;AAAA;AAAA,UACC,qBAAkB;AAAA,UAClB,qBAAkB;AAAA,UAClB,IAAI,EAAE;AAAA,UACN,WAAU;AAAA,UACV,OAAO,kBAAkB,EAAE,KAAK;AAAA,UAEhC,8BAAC,SAAI,WAAU,oBACb;AAAA,YAAC;AAAA;AAAA,cACC,QAAQ,EAAE,MAAM;AAAA,cAChB,UAAU,EAAE,MAAM;AAAA,cAClB,SAAS,EAAE,MAAM;AAAA,cACjB,aAAa,EAAE,MAAM;AAAA,cACrB,aAAa,EAAE,MAAM;AAAA;AAAA,UACvB,GACF;AAAA;AAAA,MACF;AAAA,IAEJ;AAAA,IACA,KAAK;AACH,aAAO,oBAAC,iBAAe,GAAI,SAA0B;AAAA,IACvD,KAAK;AACH,aAAO,oBAAC,mBAAiB,GAAI,SAA4B;AAAA,IAC3D,KAAK;AACH,aAAO,oBAAC,iBAAe,GAAI,SAA0B;AAAA,IACvD,KAAK;AACH,aAAO,oBAAC,mBAAiB,GAAI,SAA4B;AAAA,IAC3D,KAAK;AACH,aAAO,oBAAC,mBAAiB,GAAI,SAA4B;AAAA,IAC3D,KAAK;AACH,aAAO,oBAAC,uBAAqB,GAAI,SAAgC;AAAA,IACnE,KAAK;AACH,aAAO,oBAAC,wBAAsB,GAAI,SAAiC;AAAA,IACrE,KAAK;AACH,aAAO,oBAAC,qBAAmB,GAAI,SAA8B;AAAA,IAC/D,KAAK;AACH,aAAO,oBAAC,kBAAgB,GAAI,SAA2B;AAAA,IACzD,KAAK;AACH,aAAO,oBAAC,oBAAkB,GAAI,SAA6B;AAAA,IAC7D,KAAK;AACH,aAAO,oBAAC,gBAAc,GAAI,SAAyB;AAAA,IACrD;AACE,aAAO;AAAA,EACX;AACF;AAkBO,SAAS,aAAa,OAAwC;AACnE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,CAAC;AAAA,IAClB,iBAAiB;AAAA,EACnB,IAAI;AACJ,QAAM,SAAS,gBAAgB,MAAM,MAAM;AAC3C,QAAM,WAAW,OAAO,MAAM,IAAI,KAAK,CAAC;AACxC,SACE,qBAAAA,WAAA,EACG;AAAA,aAAS,WAAW,IACnB;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,UAAU,IAAI;AAAA,QACpB,MAAM;AAAA;AAAA,IACR,IACE;AAAA,IACH,SAAS,IAAI,CAAC,SAAS,MAAM;AAC5B,YAAM,MAAM,QAAQ,MAAM,GAAG,QAAQ,IAAI,IAAI,CAAC;AAC9C,YAAM,UAAU,cAAc,SAAS,EAAE,QAAQ,QAAQ,CAAC;AAC1D,UAAI,YAAY,QAAW;AACzB,eAAO,oBAAC,YAAoB,qBAAN,GAAc;AAAA,MACtC;AACA,YAAM,SAAS,eAAe,QAAQ,IAAI;AAC1C,UAAI,QAAQ;AACV,eACE,oBAAC,YACE,iBAAO;AAAA,UACN,IAAI,QAAQ;AAAA,UACZ,OAAO,QAAQ;AAAA,UACf,SAAS,QAAQ;AAAA,UACjB,OAAO,QAAQ;AAAA,QACjB,CAAC,KANY,GAOf;AAAA,MAEJ;AACA,aACE,oBAAC,kBAAyB,MAAM,QAAQ,MAAM,MAAM,kBAA/B,GAA+C;AAAA,IAExE,CAAC;AAAA,KACH;AAEJ;","names":["Fragment"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roottale/cms-renderer-next",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "type": "module",
5
5
  "description": "RootTale CMS public-render React/Next.js Server Components. SSR-only RSC components (RootTaleBlogList / RootTaleBlogPost / RootTaleLeadForm) for external customer sites.",
6
6
  "main": "./dist/index.js",
@@ -57,7 +57,7 @@
57
57
  "embla-carousel-react": "^8.6.0",
58
58
  "motion": "^12.24.12",
59
59
  "zod": "^3.23.8",
60
- "@roottale/cms-client": "^0.46.0",
60
+ "@roottale/cms-client": "^0.47.0",
61
61
  "@roottale/cms-core": "^0.46.0"
62
62
  },
63
63
  "peerDependencies": {