@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.
- package/CHANGELOG.md +54 -0
- package/dist/before-after-client.d.ts +15 -0
- package/dist/before-after-client.js +8 -0
- package/dist/before-after-client.js.map +1 -0
- package/dist/chunk-4LQPZDXZ.js +75 -0
- package/dist/chunk-4LQPZDXZ.js.map +1 -0
- package/dist/chunk-6GQCFFHU.js +540 -0
- package/dist/chunk-6GQCFFHU.js.map +1 -0
- package/dist/chunk-F3G6RVIR.js +220 -0
- package/dist/chunk-F3G6RVIR.js.map +1 -0
- package/dist/chunk-H46JUIHZ.js +79 -0
- package/dist/chunk-H46JUIHZ.js.map +1 -0
- package/dist/chunk-K62RR4UQ.js +53 -0
- package/dist/chunk-K62RR4UQ.js.map +1 -0
- package/dist/chunk-NUUTTSGT.js +916 -0
- package/dist/chunk-NUUTTSGT.js.map +1 -0
- package/dist/chunk-RSNUZ4BF.js +3378 -0
- package/dist/chunk-RSNUZ4BF.js.map +1 -0
- package/dist/chunk-U5AKACPN.js +113 -0
- package/dist/chunk-U5AKACPN.js.map +1 -0
- package/dist/chunk-WWS43XCT.js +206 -0
- package/dist/chunk-WWS43XCT.js.map +1 -0
- package/dist/cms-public.css +164 -51
- package/dist/concerns-client.d.ts +12 -0
- package/dist/concerns-client.js +8 -0
- package/dist/concerns-client.js.map +1 -0
- package/dist/expertise-client.d.ts +10 -0
- package/dist/expertise-client.js +8 -0
- package/dist/expertise-client.js.map +1 -0
- package/dist/hero-carousel-client.d.ts +36 -0
- package/dist/hero-carousel-client.js +8 -0
- package/dist/hero-carousel-client.js.map +1 -0
- package/dist/routes.d.ts +51 -5
- package/dist/routes.js +109 -62
- package/dist/routes.js.map +1 -1
- package/dist/seo-checks.d.ts +87 -0
- package/dist/seo-checks.js +27 -0
- package/dist/seo-checks.js.map +1 -0
- package/dist/server.d.ts +51 -2
- package/dist/server.js +8 -2
- package/dist/site-config-DgYGGwu1.d.ts +3910 -0
- package/dist/spaces-client.d.ts +10 -0
- package/dist/spaces-client.js +8 -0
- package/dist/spaces-client.js.map +1 -0
- package/dist/templates.css +3246 -194
- package/dist/templates.d.ts +20 -1028
- package/dist/templates.js +70 -2
- package/dist/templates.js.map +1 -1
- package/dist/{theme-css-vars-BiyHBsJG.d.ts → theme-css-vars-CD3jcEkp.d.ts} +2 -2
- package/dist/ui-react-motion-client.d.ts +73 -0
- package/dist/ui-react-motion-client.js +14 -0
- package/dist/ui-react-motion-client.js.map +1 -0
- package/dist/validate.d.ts +15 -1
- package/dist/validate.js +806 -17
- package/dist/validate.js.map +1 -1
- package/package.json +18 -6
- package/dist/chunk-GOHP7RWD.js +0 -1647
- package/dist/chunk-GOHP7RWD.js.map +0 -1
- package/dist/chunk-IOYBEH6E.js +0 -271
- package/dist/chunk-IOYBEH6E.js.map +0 -1
package/dist/templates.js
CHANGED
|
@@ -1,36 +1,63 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RootTaleBlogList,
|
|
3
3
|
RootTaleLeadForm,
|
|
4
|
+
TemplateBeforeAfter,
|
|
5
|
+
TemplateConcerns,
|
|
4
6
|
TemplateContact,
|
|
5
7
|
TemplateCtaBanner,
|
|
8
|
+
TemplateExpertise,
|
|
6
9
|
TemplateFaq,
|
|
10
|
+
TemplateFooter,
|
|
7
11
|
TemplateHero,
|
|
12
|
+
TemplateHours,
|
|
13
|
+
TemplatePricing,
|
|
14
|
+
TemplateProcess,
|
|
8
15
|
TemplateRichText,
|
|
9
16
|
TemplateServices,
|
|
17
|
+
TemplateSpaces,
|
|
18
|
+
TemplateTeam,
|
|
19
|
+
TemplateTestimonials,
|
|
10
20
|
TemplateTrust,
|
|
11
21
|
sectionThemeStyle,
|
|
12
22
|
themeToCssVars
|
|
13
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-RSNUZ4BF.js";
|
|
24
|
+
import "./chunk-U5AKACPN.js";
|
|
25
|
+
import "./chunk-4LQPZDXZ.js";
|
|
26
|
+
import "./chunk-K62RR4UQ.js";
|
|
27
|
+
import "./chunk-H46JUIHZ.js";
|
|
28
|
+
import "./chunk-WWS43XCT.js";
|
|
14
29
|
import "./chunk-FUA5H6JE.js";
|
|
15
30
|
import {
|
|
16
31
|
BUILTIN_SECTION_SCHEMAS,
|
|
17
32
|
BUILTIN_SECTION_TYPES,
|
|
33
|
+
BeforeAfterSectionSchema,
|
|
18
34
|
BlogListSectionSchema,
|
|
35
|
+
ConcernsSectionSchema,
|
|
19
36
|
ContactSectionSchema,
|
|
20
37
|
CtaBannerSectionSchema,
|
|
21
38
|
CustomSectionSchema,
|
|
39
|
+
ExpertiseSectionSchema,
|
|
22
40
|
FaqSectionSchema,
|
|
41
|
+
FooterSectionSchema,
|
|
23
42
|
HeroSectionSchema,
|
|
43
|
+
HoursSectionSchema,
|
|
24
44
|
LeadFormSectionSchema,
|
|
25
45
|
NavItemSchema,
|
|
46
|
+
PricingSectionSchema,
|
|
47
|
+
ProcessSectionSchema,
|
|
26
48
|
RichTextSectionSchema,
|
|
49
|
+
SECTION_VARIANTS,
|
|
27
50
|
SectionSchema,
|
|
28
51
|
ServicesSectionSchema,
|
|
29
52
|
SiteConfigSchema,
|
|
53
|
+
SpacesSectionSchema,
|
|
54
|
+
TeamSectionSchema,
|
|
55
|
+
TestimonialsSectionSchema,
|
|
30
56
|
TrustSectionSchema,
|
|
31
57
|
defineSiteConfig,
|
|
32
58
|
parseSiteConfig
|
|
33
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-NUUTTSGT.js";
|
|
60
|
+
import "./chunk-F3G6RVIR.js";
|
|
34
61
|
|
|
35
62
|
// src/templates/SiteRenderer.tsx
|
|
36
63
|
import {
|
|
@@ -79,6 +106,8 @@ function renderBuiltin(section, ctx) {
|
|
|
79
106
|
return /* @__PURE__ */ jsx(TemplateServices, { ...section });
|
|
80
107
|
case "faq":
|
|
81
108
|
return /* @__PURE__ */ jsx(TemplateFaq, { ...section });
|
|
109
|
+
case "footer":
|
|
110
|
+
return /* @__PURE__ */ jsx(TemplateFooter, { ...section });
|
|
82
111
|
case "cta-banner":
|
|
83
112
|
return /* @__PURE__ */ jsx(TemplateCtaBanner, { ...section });
|
|
84
113
|
case "blog-list": {
|
|
@@ -135,6 +164,24 @@ function renderBuiltin(section, ctx) {
|
|
|
135
164
|
return /* @__PURE__ */ jsx(TemplateTrust, { ...section });
|
|
136
165
|
case "contact":
|
|
137
166
|
return /* @__PURE__ */ jsx(TemplateContact, { ...section });
|
|
167
|
+
case "hours":
|
|
168
|
+
return /* @__PURE__ */ jsx(TemplateHours, { ...section });
|
|
169
|
+
case "pricing":
|
|
170
|
+
return /* @__PURE__ */ jsx(TemplatePricing, { ...section });
|
|
171
|
+
case "process":
|
|
172
|
+
return /* @__PURE__ */ jsx(TemplateProcess, { ...section });
|
|
173
|
+
case "before-after":
|
|
174
|
+
return /* @__PURE__ */ jsx(TemplateBeforeAfter, { ...section });
|
|
175
|
+
case "testimonials":
|
|
176
|
+
return /* @__PURE__ */ jsx(TemplateTestimonials, { ...section });
|
|
177
|
+
case "expertise":
|
|
178
|
+
return /* @__PURE__ */ jsx(TemplateExpertise, { ...section });
|
|
179
|
+
case "spaces":
|
|
180
|
+
return /* @__PURE__ */ jsx(TemplateSpaces, { ...section });
|
|
181
|
+
case "concerns":
|
|
182
|
+
return /* @__PURE__ */ jsx(TemplateConcerns, { ...section });
|
|
183
|
+
case "team":
|
|
184
|
+
return /* @__PURE__ */ jsx(TemplateTeam, { ...section });
|
|
138
185
|
default:
|
|
139
186
|
return void 0;
|
|
140
187
|
}
|
|
@@ -179,26 +226,47 @@ function SiteRenderer(props) {
|
|
|
179
226
|
export {
|
|
180
227
|
BUILTIN_SECTION_SCHEMAS,
|
|
181
228
|
BUILTIN_SECTION_TYPES,
|
|
229
|
+
BeforeAfterSectionSchema,
|
|
182
230
|
BlogListSectionSchema,
|
|
231
|
+
ConcernsSectionSchema,
|
|
183
232
|
ContactSectionSchema,
|
|
184
233
|
CtaBannerSectionSchema,
|
|
185
234
|
CustomSectionSchema,
|
|
235
|
+
ExpertiseSectionSchema,
|
|
186
236
|
FaqSectionSchema,
|
|
237
|
+
FooterSectionSchema,
|
|
187
238
|
HeroSectionSchema,
|
|
239
|
+
HoursSectionSchema,
|
|
188
240
|
LeadFormSectionSchema,
|
|
189
241
|
NavItemSchema,
|
|
242
|
+
PricingSectionSchema,
|
|
243
|
+
ProcessSectionSchema,
|
|
190
244
|
RichTextSectionSchema,
|
|
245
|
+
SECTION_VARIANTS,
|
|
191
246
|
SectionSchema,
|
|
192
247
|
ServicesSectionSchema,
|
|
193
248
|
SiteConfigSchema,
|
|
194
249
|
SiteRenderer,
|
|
250
|
+
SpacesSectionSchema,
|
|
251
|
+
TeamSectionSchema,
|
|
252
|
+
TemplateBeforeAfter,
|
|
253
|
+
TemplateConcerns,
|
|
195
254
|
TemplateContact,
|
|
196
255
|
TemplateCtaBanner,
|
|
256
|
+
TemplateExpertise,
|
|
197
257
|
TemplateFaq,
|
|
258
|
+
TemplateFooter,
|
|
198
259
|
TemplateHero,
|
|
260
|
+
TemplateHours,
|
|
261
|
+
TemplatePricing,
|
|
262
|
+
TemplateProcess,
|
|
199
263
|
TemplateRichText,
|
|
200
264
|
TemplateServices,
|
|
265
|
+
TemplateSpaces,
|
|
266
|
+
TemplateTeam,
|
|
267
|
+
TemplateTestimonials,
|
|
201
268
|
TemplateTrust,
|
|
269
|
+
TestimonialsSectionSchema,
|
|
202
270
|
TrustSectionSchema,
|
|
203
271
|
defineSiteConfig,
|
|
204
272
|
parseSiteConfig,
|
package/dist/templates.js.map
CHANGED
|
@@ -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 TemplateContact,\n TemplateCtaBanner,\n TemplateFaq,\n TemplateHero,\n TemplateRichText,\n TemplateServices,\n TemplateTrust,\n sectionThemeStyle,\n} from \"./sections.js\";\nimport {\n type BlogListSection,\n type ContactSection,\n type CtaBannerSection,\n type FaqSection,\n type HeroSection,\n type LeadFormSection,\n type RichTextSection,\n type Section,\n type ServicesSection,\n type SiteConfig,\n type SiteConfigInput,\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 \"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 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;AAgFG,SAsHN,YAAAA,WAtHM,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,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;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"]}
|
|
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,7 +1,7 @@
|
|
|
1
|
-
import { CSSProperties } from 'react';
|
|
2
1
|
import { RootTaleTheme } from '@roottale/cms-client/server';
|
|
2
|
+
import { RootTaleThemeStyle } from '@roottale/ui-react/theme';
|
|
3
3
|
|
|
4
|
-
type RootTaleCssVars =
|
|
4
|
+
type RootTaleCssVars = RootTaleThemeStyle;
|
|
5
5
|
declare function themeToCssVars(theme: RootTaleTheme | null): RootTaleCssVars | null;
|
|
6
6
|
|
|
7
7
|
export { type RootTaleCssVars as R, themeToCssVars as t };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 공유 모션 variants — C5(생성물 손 복사 금지)의 정신을 따라 모든 `scroll`/`motion`
|
|
6
|
+
* 프리미티브가 이 한 파일에서 variants 를 파생한다. 각 컴포넌트가 hidden/visible
|
|
7
|
+
* keyframe 객체를 손으로 복사하지 않는다(ds-rule-pack.md C5).
|
|
8
|
+
*
|
|
9
|
+
* 타이밍 출처(C1 — 임의값 대신 토큰 참조):
|
|
10
|
+
* - `ENTRANCE_EASE` = DTCG `--ease-out`(packages/tokens/src/motion.dtcg.json:
|
|
11
|
+
* `cubic-bezier(0, 0, 0.2, 1)`)과 동일 곡선. 등장(entrance)에 쓰는 표준 감속.
|
|
12
|
+
* - `ENTRANCE_DURATION`(0.5s)·`STAGGER_STEP`(0.08s) = 앰비언트 등장 스케일.
|
|
13
|
+
* motion.dtcg 의 100~200ms 는 INP 예산이 걸린 마이크로 인터랙션
|
|
14
|
+
* (hover/focus/button)용 cap 이며(M5), 스크롤 진입 리빌은 입력 지연(INP)
|
|
15
|
+
* 경로가 아니라 그 cap 대상이 아니다 — 별도 앰비언트 스케일로 문서화한다.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type EntranceVariant = "fade-up" | "fade" | "scale-in";
|
|
19
|
+
|
|
20
|
+
type AnimateTag = "div" | "section" | "article" | "li" | "span";
|
|
21
|
+
interface AnimateOnScrollProps {
|
|
22
|
+
readonly children?: ReactNode;
|
|
23
|
+
readonly className?: string;
|
|
24
|
+
readonly style?: CSSProperties;
|
|
25
|
+
/** 진입 애니메이션 종류(구조 상이). 기본 `fade-up`. */
|
|
26
|
+
readonly variant?: EntranceVariant;
|
|
27
|
+
/** 렌더할 시맨틱 엘리먼트(래퍼 div 추가 없이 그 자리를 대체). 기본 `div`. */
|
|
28
|
+
readonly as?: AnimateTag;
|
|
29
|
+
/** 한 번만 트리거(재진입 시 재생 안 함). 기본 true. */
|
|
30
|
+
readonly once?: boolean;
|
|
31
|
+
/** whileInView viewport root margin. 기본 `-80px`. */
|
|
32
|
+
readonly margin?: string;
|
|
33
|
+
}
|
|
34
|
+
declare function AnimateOnScroll({ children, className, style, variant, as, once, margin, }: AnimateOnScrollProps): react_jsx_runtime.JSX.Element;
|
|
35
|
+
|
|
36
|
+
interface MarqueeProps {
|
|
37
|
+
readonly children?: ReactNode;
|
|
38
|
+
readonly className?: string;
|
|
39
|
+
/** 한 바퀴(자식 1벌 폭) 이동에 걸리는 시간(초). 기본 24. */
|
|
40
|
+
readonly speedSec?: number;
|
|
41
|
+
/** 항목 간격(CSS length). 기본 `2rem`(구조 간격 — 브랜드 토큰 아님). */
|
|
42
|
+
readonly gap?: string;
|
|
43
|
+
/** hover 시 일시정지. 기본 true. */
|
|
44
|
+
readonly pauseOnHover?: boolean;
|
|
45
|
+
/** 스크린리더용 그룹 레이블. */
|
|
46
|
+
readonly ariaLabel?: string;
|
|
47
|
+
}
|
|
48
|
+
declare function Marquee({ children, className, speedSec, gap, pauseOnHover, ariaLabel, }: MarqueeProps): react_jsx_runtime.JSX.Element;
|
|
49
|
+
|
|
50
|
+
type GroupTag = "div" | "ul" | "ol" | "section";
|
|
51
|
+
type ItemTag = "div" | "li" | "article";
|
|
52
|
+
interface StaggerGroupProps {
|
|
53
|
+
readonly children?: ReactNode;
|
|
54
|
+
readonly className?: string;
|
|
55
|
+
readonly style?: CSSProperties;
|
|
56
|
+
/** 렌더할 시맨틱 컨테이너 엘리먼트. 기본 `div`. */
|
|
57
|
+
readonly as?: GroupTag;
|
|
58
|
+
/** 자식 순차 지연(초). 기본 0.08. */
|
|
59
|
+
readonly staggerChildren?: number;
|
|
60
|
+
readonly once?: boolean;
|
|
61
|
+
readonly margin?: string;
|
|
62
|
+
}
|
|
63
|
+
declare function StaggerGroup({ children, className, style, as, staggerChildren, once, margin, }: StaggerGroupProps): react_jsx_runtime.JSX.Element;
|
|
64
|
+
interface StaggerItemProps {
|
|
65
|
+
readonly children?: ReactNode;
|
|
66
|
+
readonly className?: string;
|
|
67
|
+
readonly style?: CSSProperties;
|
|
68
|
+
/** 렌더할 시맨틱 아이템 엘리먼트. 기본 `div`. */
|
|
69
|
+
readonly as?: ItemTag;
|
|
70
|
+
}
|
|
71
|
+
declare function StaggerItem({ children, className, style, as }: StaggerItemProps): react_jsx_runtime.JSX.Element;
|
|
72
|
+
|
|
73
|
+
export { AnimateOnScroll, Marquee, StaggerGroup, StaggerItem };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Severity, MEDICAL_LINT_RULESET } from '@roottale/audit-medical';
|
|
2
|
+
import { TAX_LINT_RULESET } from '@roottale/audit-tax';
|
|
2
3
|
|
|
3
4
|
interface SectionLock {
|
|
4
5
|
move?: boolean;
|
|
@@ -27,9 +28,12 @@ type PublishGateError = {
|
|
|
27
28
|
type PublishGateRuleset = {
|
|
28
29
|
readonly publish: typeof PUBLISH_GATE_RULESET;
|
|
29
30
|
readonly medical?: typeof MEDICAL_LINT_RULESET;
|
|
31
|
+
readonly tax?: typeof TAX_LINT_RULESET;
|
|
30
32
|
};
|
|
31
33
|
type PublishGateResult = {
|
|
32
34
|
readonly errors: readonly PublishGateError[];
|
|
35
|
+
/** 비차단 소견 (warning·info) — 발행은 통과, meta.compliance 에 기록 (2026-07-11) */
|
|
36
|
+
readonly warnings: readonly PublishGateError[];
|
|
33
37
|
readonly ruleset: PublishGateRuleset;
|
|
34
38
|
};
|
|
35
39
|
type PublishGateDoc = {
|
|
@@ -37,6 +41,16 @@ type PublishGateDoc = {
|
|
|
37
41
|
readonly excerpt?: string | null;
|
|
38
42
|
readonly bodyJson: Record<string, unknown>;
|
|
39
43
|
readonly metaJson?: Record<string, unknown> | null;
|
|
44
|
+
/**
|
|
45
|
+
* seo-checks 의 slug-length 체크용(선택). 미전달 시 "" — 항상 good 판정이라
|
|
46
|
+
* 안전한 기본값(fail-open, 차단 아님).
|
|
47
|
+
*/
|
|
48
|
+
readonly slug?: string | null;
|
|
49
|
+
/**
|
|
50
|
+
* seo-checks 의 featured-image 체크용(선택). 미전달 시 false — 항상 warn
|
|
51
|
+
* 판정(경고일 뿐 warnings 도 아님, bad 만 승격되므로 무영향).
|
|
52
|
+
*/
|
|
53
|
+
readonly hasFeaturedImage?: boolean;
|
|
40
54
|
};
|
|
41
55
|
/**
|
|
42
56
|
* h1 모델 (W6-1) — 문서 타입별로 h1 소유자가 다르다:
|
|
@@ -48,7 +62,7 @@ type PublishGateDoc = {
|
|
|
48
62
|
* type 분기를 빼먹으면 컴파일 에러가 나야 한다.
|
|
49
63
|
*/
|
|
50
64
|
type PublishGateOptions = {
|
|
51
|
-
readonly vertical: string;
|
|
65
|
+
readonly vertical: string | null | undefined;
|
|
52
66
|
readonly complianceFlags?: readonly string[];
|
|
53
67
|
readonly documentType: "page" | "post";
|
|
54
68
|
};
|