@roottale/cms-renderer-next 0.45.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.
@@ -1213,6 +1213,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1213
1213
  }, "strip", z.ZodTypeAny, {
1214
1214
  jsonLd: boolean;
1215
1215
  heading?: string | undefined;
1216
+ contact?: {
1217
+ title: string;
1218
+ description?: string | undefined;
1219
+ phone?: string | undefined;
1220
+ cta?: {
1221
+ href: string;
1222
+ label: string;
1223
+ } | undefined;
1224
+ } | undefined;
1216
1225
  items?: {
1217
1226
  question: string;
1218
1227
  answer: string;
@@ -1224,15 +1233,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1224
1233
  answer: string;
1225
1234
  }[];
1226
1235
  }[] | undefined;
1227
- contact?: {
1228
- title: string;
1229
- description?: string | undefined;
1230
- phone?: string | undefined;
1231
- cta?: {
1232
- href: string;
1233
- label: string;
1234
- } | undefined;
1235
- } | undefined;
1236
1236
  contentGroups?: {
1237
1237
  title: string;
1238
1238
  items: {
@@ -1245,6 +1245,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1245
1245
  }[] | undefined;
1246
1246
  }, {
1247
1247
  heading?: string | undefined;
1248
+ contact?: {
1249
+ title: string;
1250
+ description?: string | undefined;
1251
+ phone?: string | undefined;
1252
+ cta?: {
1253
+ href: string;
1254
+ label: string;
1255
+ } | undefined;
1256
+ } | undefined;
1248
1257
  items?: {
1249
1258
  question: string;
1250
1259
  answer: string;
@@ -1256,15 +1265,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1256
1265
  answer: string;
1257
1266
  }[];
1258
1267
  }[] | undefined;
1259
- contact?: {
1260
- title: string;
1261
- description?: string | undefined;
1262
- phone?: string | undefined;
1263
- cta?: {
1264
- href: string;
1265
- label: string;
1266
- } | undefined;
1267
- } | undefined;
1268
1268
  contentGroups?: {
1269
1269
  title: string;
1270
1270
  items: {
@@ -1283,6 +1283,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1283
1283
  props: {
1284
1284
  jsonLd: boolean;
1285
1285
  heading?: string | undefined;
1286
+ contact?: {
1287
+ title: string;
1288
+ description?: string | undefined;
1289
+ phone?: string | undefined;
1290
+ cta?: {
1291
+ href: string;
1292
+ label: string;
1293
+ } | undefined;
1294
+ } | undefined;
1286
1295
  items?: {
1287
1296
  question: string;
1288
1297
  answer: string;
@@ -1294,15 +1303,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1294
1303
  answer: string;
1295
1304
  }[];
1296
1305
  }[] | undefined;
1297
- contact?: {
1298
- title: string;
1299
- description?: string | undefined;
1300
- phone?: string | undefined;
1301
- cta?: {
1302
- href: string;
1303
- label: string;
1304
- } | undefined;
1305
- } | undefined;
1306
1306
  contentGroups?: {
1307
1307
  title: string;
1308
1308
  items: {
@@ -1320,6 +1320,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1320
1320
  type: "faq";
1321
1321
  props: {
1322
1322
  heading?: string | undefined;
1323
+ contact?: {
1324
+ title: string;
1325
+ description?: string | undefined;
1326
+ phone?: string | undefined;
1327
+ cta?: {
1328
+ href: string;
1329
+ label: string;
1330
+ } | undefined;
1331
+ } | undefined;
1323
1332
  items?: {
1324
1333
  question: string;
1325
1334
  answer: string;
@@ -1331,15 +1340,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1331
1340
  answer: string;
1332
1341
  }[];
1333
1342
  }[] | undefined;
1334
- contact?: {
1335
- title: string;
1336
- description?: string | undefined;
1337
- phone?: string | undefined;
1338
- cta?: {
1339
- href: string;
1340
- label: string;
1341
- } | undefined;
1342
- } | undefined;
1343
1343
  contentGroups?: {
1344
1344
  title: string;
1345
1345
  items: {
@@ -1361,6 +1361,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1361
1361
  props: {
1362
1362
  jsonLd: boolean;
1363
1363
  heading?: string | undefined;
1364
+ contact?: {
1365
+ title: string;
1366
+ description?: string | undefined;
1367
+ phone?: string | undefined;
1368
+ cta?: {
1369
+ href: string;
1370
+ label: string;
1371
+ } | undefined;
1372
+ } | undefined;
1364
1373
  items?: {
1365
1374
  question: string;
1366
1375
  answer: string;
@@ -1372,15 +1381,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1372
1381
  answer: string;
1373
1382
  }[];
1374
1383
  }[] | undefined;
1375
- contact?: {
1376
- title: string;
1377
- description?: string | undefined;
1378
- phone?: string | undefined;
1379
- cta?: {
1380
- href: string;
1381
- label: string;
1382
- } | undefined;
1383
- } | undefined;
1384
1384
  contentGroups?: {
1385
1385
  title: string;
1386
1386
  items: {
@@ -1398,6 +1398,15 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1398
1398
  type: "faq";
1399
1399
  props: {
1400
1400
  heading?: string | undefined;
1401
+ contact?: {
1402
+ title: string;
1403
+ description?: string | undefined;
1404
+ phone?: string | undefined;
1405
+ cta?: {
1406
+ href: string;
1407
+ label: string;
1408
+ } | undefined;
1409
+ } | undefined;
1401
1410
  items?: {
1402
1411
  question: string;
1403
1412
  answer: string;
@@ -1409,15 +1418,6 @@ declare const FaqSectionSchema: z.ZodEffects<z.ZodObject<{
1409
1418
  answer: string;
1410
1419
  }[];
1411
1420
  }[] | undefined;
1412
- contact?: {
1413
- title: string;
1414
- description?: string | undefined;
1415
- phone?: string | undefined;
1416
- cta?: {
1417
- href: string;
1418
- label: string;
1419
- } | undefined;
1420
- } | undefined;
1421
1421
  contentGroups?: {
1422
1422
  title: string;
1423
1423
  items: {
@@ -3804,10 +3804,10 @@ declare const SiteConfigSchema: z.ZodObject<{
3804
3804
  icon: z.ZodOptional<z.ZodString>;
3805
3805
  }, "strip", z.ZodTypeAny, {
3806
3806
  layout: string;
3807
+ basePath: string;
3807
3808
  categories: string[];
3808
3809
  label: string;
3809
3810
  key: string;
3810
- basePath: string;
3811
3811
  feed: boolean;
3812
3812
  archives: boolean;
3813
3813
  og: boolean;
@@ -3815,9 +3815,9 @@ declare const SiteConfigSchema: z.ZodObject<{
3815
3815
  pinning: boolean;
3816
3816
  icon?: string | undefined;
3817
3817
  }, {
3818
+ basePath: string;
3818
3819
  label: string;
3819
3820
  key: string;
3820
- basePath: string;
3821
3821
  layout?: string | undefined;
3822
3822
  icon?: string | undefined;
3823
3823
  categories?: string[] | undefined;
@@ -3829,6 +3829,17 @@ declare const SiteConfigSchema: z.ZodObject<{
3829
3829
  }>, "many">>;
3830
3830
  /** 사이트 전역 토큰 override (`--rt-*` CSS 변수 → 값). 비우면 admin /design SSoT. */
3831
3831
  theme: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
3832
+ /**
3833
+ * 테마 방향(Theme Directions, `@roottale/tokens` 프리셋 id — 예: "d1-editorial").
3834
+ * root layout 이 `<html data-rt-direction="…">` 로 부여해
3835
+ * `@roottale/tokens/theme-directions.css` 의 `[data-rt-direction="…"]` 규칙을
3836
+ * 활성화한다(roottale-docs `docs/design/theme-directions.md`). 값은 문자열로만
3837
+ * 검증한다 — `@roottale/cms-renderer-next` 는 `@roottale/tokens` 를 직접
3838
+ * 의존하지 않으므로 유효 id 집합은 `@roottale/tokens/theme-directions` 의
3839
+ * `ThemeDirectionId` 를 참조해 `site.config.ts` 작성 시점에 타입으로 좁힌다.
3840
+ * 미지정 시 속성 자체가 안 붙어 완전히 무변화(기존 동작).
3841
+ */
3842
+ direction: z.ZodOptional<z.ZodString>;
3832
3843
  nav: z.ZodDefault<z.ZodArray<z.ZodObject<{
3833
3844
  label: z.ZodString;
3834
3845
  href: z.ZodString;
@@ -3862,10 +3873,10 @@ declare const SiteConfigSchema: z.ZodObject<{
3862
3873
  }, "strip", z.ZodTypeAny, {
3863
3874
  collections: {
3864
3875
  layout: string;
3876
+ basePath: string;
3865
3877
  categories: string[];
3866
3878
  label: string;
3867
3879
  key: string;
3868
- basePath: string;
3869
3880
  feed: boolean;
3870
3881
  archives: boolean;
3871
3882
  og: boolean;
@@ -3893,6 +3904,7 @@ declare const SiteConfigSchema: z.ZodObject<{
3893
3904
  };
3894
3905
  pages: Record<string, Section[]>;
3895
3906
  schemaVersion?: number | undefined;
3907
+ direction?: string | undefined;
3896
3908
  }, {
3897
3909
  site: {
3898
3910
  title: string;
@@ -3905,9 +3917,9 @@ declare const SiteConfigSchema: z.ZodObject<{
3905
3917
  };
3906
3918
  pages: Record<string, SectionInput[]>;
3907
3919
  collections?: {
3920
+ basePath: string;
3908
3921
  label: string;
3909
3922
  key: string;
3910
- basePath: string;
3911
3923
  layout?: string | undefined;
3912
3924
  icon?: string | undefined;
3913
3925
  categories?: string[] | undefined;
@@ -3927,6 +3939,7 @@ declare const SiteConfigSchema: z.ZodObject<{
3927
3939
  }[] | undefined;
3928
3940
  }[] | undefined;
3929
3941
  schemaVersion?: number | undefined;
3942
+ direction?: string | undefined;
3930
3943
  }>;
3931
3944
  type SiteConfig = z.output<typeof SiteConfigSchema>;
3932
3945
  /** config 작성용 (defaults 생략 가능). */
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { S as SpacesSection } from './site-config-kqurIH-k.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-kqurIH-k.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-kqurIH-k.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-kqurIH-k.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,14 +20,15 @@ import {
20
20
  TemplateTrust,
21
21
  sectionThemeStyle,
22
22
  themeToCssVars
23
- } from "./chunk-45KYMMDY.js";
23
+ } from "./chunk-ZHXYCABN.js";
24
24
  import "./chunk-U5AKACPN.js";
25
25
  import "./chunk-LEHHSI6G.js";
26
26
  import "./chunk-6MFHA555.js";
27
27
  import "./chunk-FZK3EXYZ.js";
28
28
  import "./chunk-K62RR4UQ.js";
29
29
  import "./chunk-H46JUIHZ.js";
30
- import "./chunk-Z3ZSCPG4.js";
30
+ import "./chunk-ZL2NA3QT.js";
31
+ import "./chunk-VG6UUF4D.js";
31
32
  import "./chunk-FUA5H6JE.js";
32
33
  import {
33
34
  BUILTIN_SECTION_SCHEMAS,
@@ -60,7 +61,7 @@ import {
60
61
  defineSiteConfig,
61
62
  parseSiteConfig,
62
63
  resolveSiteConfigSchemaVersion
63
- } from "./chunk-GUV5WHD5.js";
64
+ } from "./chunk-UM37DOIK.js";
64
65
  import "./chunk-OMXEU4LS.js";
65
66
  import "./chunk-YLSQAT6B.js";
66
67
 
@@ -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":"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","names":["Fragment"]}
@@ -45,8 +45,10 @@ interface MarqueeProps {
45
45
  readonly pauseOnHover?: boolean;
46
46
  /** 스크린리더용 그룹 레이블. */
47
47
  readonly ariaLabel?: string;
48
+ /** 이동 방향. 기본 `left`(기존 동작 불변). `right` 는 반대 방향 이중 마퀴 패턴용. */
49
+ readonly direction?: "left" | "right";
48
50
  }
49
- declare function Marquee({ children, className, speedSec, gap, pauseOnHover, ariaLabel, }: MarqueeProps): react_jsx_runtime.JSX.Element;
51
+ declare function Marquee({ children, className, speedSec, gap, pauseOnHover, ariaLabel, direction, }: MarqueeProps): react_jsx_runtime.JSX.Element;
50
52
 
51
53
  type GroupTag = "div" | "ul" | "ol" | "section";
52
54
  type ItemTag = "div" | "li" | "article";
@@ -5,7 +5,7 @@ import {
5
5
  Marquee,
6
6
  StaggerGroup,
7
7
  StaggerItem
8
- } from "./chunk-Z3ZSCPG4.js";
8
+ } from "./chunk-ZL2NA3QT.js";
9
9
  export {
10
10
  AnimateOnScroll,
11
11
  CountUpStat,
package/dist/validate.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SectionSchema
3
- } from "./chunk-GUV5WHD5.js";
3
+ } from "./chunk-UM37DOIK.js";
4
4
  import {
5
5
  runReadabilityChecks,
6
6
  runSeoChecks
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@roottale/cms-renderer-next",
3
- "version": "0.45.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",
7
7
  "types": "./dist/index.d.ts",
8
+ "sideEffects": [
9
+ "*.css"
10
+ ],
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/index.d.ts",
@@ -54,8 +57,8 @@
54
57
  "embla-carousel-react": "^8.6.0",
55
58
  "motion": "^12.24.12",
56
59
  "zod": "^3.23.8",
57
- "@roottale/cms-client": "^0.45.0",
58
- "@roottale/cms-core": "^0.45.0"
60
+ "@roottale/cms-client": "^0.47.0",
61
+ "@roottale/cms-core": "^0.46.0"
59
62
  },
60
63
  "peerDependencies": {
61
64
  "react": "^19.0.0"
@@ -72,7 +75,7 @@
72
75
  "@roottale/audit-medical": "^0.0.1",
73
76
  "@roottale/audit-tax": "^0.0.1",
74
77
  "@roottale/cms-i18n": "^0.2.0",
75
- "@roottale/ui-react": "^0.4.0"
78
+ "@roottale/ui-react": "^0.5.0"
76
79
  },
77
80
  "publishConfig": {
78
81
  "access": "public"