@roottale/cms-renderer-next 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1628 @@
1
+ import {
2
+ collectCategories,
3
+ collectCategoriesWithCounts,
4
+ collectCollectionCategories,
5
+ defaultCategoryHref,
6
+ filterPostsByCategory
7
+ } from "./chunk-FUA5H6JE.js";
8
+ import {
9
+ SectionSchema
10
+ } from "./chunk-IOYBEH6E.js";
11
+
12
+ // src/server.tsx
13
+ import {
14
+ DEFAULT_BLOG_SETTINGS,
15
+ fetchBlogSettings,
16
+ fetchPost,
17
+ fetchPosts,
18
+ fetchTheme,
19
+ resolvePostDisplay,
20
+ selectRelatedPosts
21
+ } from "@roottale/cms-client/server";
22
+ import {
23
+ bindingSrcValue,
24
+ bindingTextValue,
25
+ boundFieldValue,
26
+ extractFaqEntries,
27
+ faqPageJsonLd,
28
+ isAllowedEmbedSrc as isAllowedEmbedSrc2,
29
+ postsForCollection,
30
+ resolvePostPath
31
+ } from "@roottale/cms-core";
32
+
33
+ // src/LeadForm.tsx
34
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
35
+ var VERTICAL_LABEL = {
36
+ consulting: "\uCEE8\uC124\uD305",
37
+ medical: "\uC758\uB8CC",
38
+ tax: "\uC138\uBB34",
39
+ legal: "\uBC95\uB960"
40
+ };
41
+ var VERTICAL_OPTIONS = [
42
+ { value: "consulting", label: "\uCEE8\uC124\uD305 / \uC77C\uBC18" },
43
+ { value: "medical", label: "\uC758\uB8CC (\uBCD1\uC758\uC6D0\xB7\uCE58\uACFC\xB7\uD55C\uC758\uC6D0)" },
44
+ { value: "tax", label: "\uC138\uBB34 (\uC138\uBB34\uC0AC\xB7\uD68C\uACC4\uC0AC)" },
45
+ { value: "legal", label: "\uBC95\uB960 (\uBC95\uBB34\uBC95\uC778\xB7\uBCC0\uD638\uC0AC)" }
46
+ ];
47
+ function RootTaleLeadForm(props) {
48
+ const {
49
+ action,
50
+ vertical,
51
+ redirectUrl,
52
+ heading,
53
+ description,
54
+ submitLabel = "\uC9C4\uB2E8 \uC2E0\uCCAD",
55
+ className
56
+ } = props;
57
+ const formClass = ["rt-cms-lead-form", className].filter(Boolean).join(" ");
58
+ return /* @__PURE__ */ jsxs(
59
+ "form",
60
+ {
61
+ method: "post",
62
+ action,
63
+ "data-roottale-cms": "lead-form",
64
+ className: formClass,
65
+ children: [
66
+ heading ? /* @__PURE__ */ jsx("h2", { className: "rt-cms-lead-heading", children: heading }) : null,
67
+ description ? /* @__PURE__ */ jsx("p", { className: "rt-cms-lead-description", children: description }) : null,
68
+ redirectUrl ? /* @__PURE__ */ jsx("input", { type: "hidden", name: "_redirect_url", value: redirectUrl }) : null,
69
+ vertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
70
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "vertical", value: vertical }),
71
+ /* @__PURE__ */ jsxs("p", { className: "rt-cms-lead-vertical-label", children: [
72
+ "\uBD84\uC57C: ",
73
+ /* @__PURE__ */ jsx("strong", { children: VERTICAL_LABEL[vertical] })
74
+ ] })
75
+ ] }) : /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
76
+ /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
77
+ "\uBD84\uC57C ",
78
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
79
+ ] }),
80
+ /* @__PURE__ */ jsxs(
81
+ "select",
82
+ {
83
+ name: "vertical",
84
+ required: true,
85
+ className: "rt-cms-field-input rt-cms-field-select",
86
+ defaultValue: "",
87
+ children: [
88
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "\uC120\uD0DD\uD558\uC138\uC694" }),
89
+ VERTICAL_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
90
+ ]
91
+ }
92
+ )
93
+ ] }),
94
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
95
+ /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
96
+ "\uC774\uB984 ",
97
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
98
+ ] }),
99
+ /* @__PURE__ */ jsx(
100
+ "input",
101
+ {
102
+ type: "text",
103
+ name: "contact_name",
104
+ required: true,
105
+ autoComplete: "name",
106
+ className: "rt-cms-field-input"
107
+ }
108
+ )
109
+ ] }),
110
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
111
+ /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
112
+ "\uC0AC\uC5C5\uCCB4\uBA85 ",
113
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
114
+ ] }),
115
+ /* @__PURE__ */ jsx(
116
+ "input",
117
+ {
118
+ type: "text",
119
+ name: "business_name",
120
+ required: true,
121
+ autoComplete: "organization",
122
+ className: "rt-cms-field-input"
123
+ }
124
+ )
125
+ ] }),
126
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
127
+ /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
128
+ "\uC774\uBA54\uC77C ",
129
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
130
+ ] }),
131
+ /* @__PURE__ */ jsx(
132
+ "input",
133
+ {
134
+ type: "email",
135
+ name: "email",
136
+ required: true,
137
+ autoComplete: "email",
138
+ className: "rt-cms-field-input"
139
+ }
140
+ )
141
+ ] }),
142
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
143
+ /* @__PURE__ */ jsxs("span", { className: "rt-cms-field-label", children: [
144
+ "\uC804\uD654\uBC88\uD638 ",
145
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "*" })
146
+ ] }),
147
+ /* @__PURE__ */ jsx(
148
+ "input",
149
+ {
150
+ type: "tel",
151
+ name: "phone",
152
+ required: true,
153
+ autoComplete: "tel",
154
+ inputMode: "tel",
155
+ pattern: "0\\d{1,2}-?\\d{3,4}-?\\d{4}",
156
+ placeholder: "010-1234-5678",
157
+ className: "rt-cms-field-input"
158
+ }
159
+ )
160
+ ] }),
161
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-field", children: [
162
+ /* @__PURE__ */ jsx("span", { className: "rt-cms-field-label", children: "\uD604\uC7AC \uC0AC\uC774\uD2B8 URL (\uC120\uD0DD)" }),
163
+ /* @__PURE__ */ jsx(
164
+ "input",
165
+ {
166
+ type: "url",
167
+ name: "current_site_url",
168
+ placeholder: "https://",
169
+ className: "rt-cms-field-input"
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx("span", { className: "rt-cms-field-hint", children: "\uC5C6\uC73C\uBA74 \uBE44\uC6CC\uB450\uC138\uC694." })
173
+ ] }),
174
+ /* @__PURE__ */ jsxs("label", { className: "rt-cms-lead-consent", children: [
175
+ /* @__PURE__ */ jsx("input", { type: "checkbox", name: "privacy_consent", required: true }),
176
+ /* @__PURE__ */ jsxs("span", { children: [
177
+ /* @__PURE__ */ jsx("strong", { children: "(\uD544\uC218)" }),
178
+ " \uAC1C\uC778\uC815\uBCF4 \uC218\uC9D1\xB7\uC774\uC6A9\uC5D0 \uB3D9\uC758\uD569\uB2C8\uB2E4. (\uC218\uC9D1 \uD56D\uBAA9: \uC774\uB984\xB7\uC5F0\uB77D\uCC98\xB7\uC774\uBA54\uC77C\xB7\uC0AC\uC5C5\uCCB4\uBA85. \uBCF4\uAD00 \uAE30\uAC04: \uBB38\uC758 \uC885\uB8CC \uD6C4 3\uB144)"
179
+ ] })
180
+ ] }),
181
+ vertical === "medical" ? /* @__PURE__ */ jsxs("label", { className: "rt-cms-lead-consent", children: [
182
+ /* @__PURE__ */ jsx(
183
+ "input",
184
+ {
185
+ type: "checkbox",
186
+ name: "overseas_transfer_consent",
187
+ required: true
188
+ }
189
+ ),
190
+ /* @__PURE__ */ jsxs("span", { children: [
191
+ /* @__PURE__ */ jsx("strong", { children: "(\uD544\uC218, \uC758\uB8CC)" }),
192
+ " \uC758\uB8CC PII \uC758 \uAD6D\uC678\uC774\uC804(\uBCF4\uAD00\xB7\uCC98\uB9AC)\uC5D0 \uB3D9\uC758\uD569\uB2C8\uB2E4. (\uC758\uB8CC\uBC95 \xA721\xB7\uAC1C\uC778\uC815\uBCF4\uBCF4\uD638\uBC95 \xA728 \u2014 ADR-0018)"
193
+ ] })
194
+ ] }) : null,
195
+ /* @__PURE__ */ jsx("div", { className: "rt-cms-lead-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "rt-cms-lead-submit", children: submitLabel }) })
196
+ ]
197
+ }
198
+ );
199
+ }
200
+
201
+ // src/templates/sections.tsx
202
+ import { isAllowedEmbedSrc } from "@roottale/cms-core";
203
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
204
+ function sectionThemeStyle(theme) {
205
+ if (!theme) return void 0;
206
+ const style = {};
207
+ for (const [key, value] of Object.entries(theme)) {
208
+ if (key.startsWith("--")) style[key] = value;
209
+ }
210
+ return Object.keys(style).length > 0 ? style : void 0;
211
+ }
212
+ function CtaLink({
213
+ cta,
214
+ className,
215
+ track
216
+ }) {
217
+ return /* @__PURE__ */ jsx2(
218
+ "a",
219
+ {
220
+ className: className ?? "rt-tpl-btn rt-tpl-btn--primary",
221
+ href: cta.href,
222
+ "data-track": track,
223
+ children: cta.label
224
+ }
225
+ );
226
+ }
227
+ function TemplateHero(section) {
228
+ const { eyebrow, title, description, imageSrc, imageAlt, cta } = section.props;
229
+ const variant = section.variant;
230
+ const showImage = variant !== "minimal" && imageSrc;
231
+ return /* @__PURE__ */ jsx2(
232
+ "section",
233
+ {
234
+ "data-roottale-tpl": "hero",
235
+ "data-variant": variant,
236
+ id: section.id,
237
+ className: "rt-tpl-section rt-tpl-hero",
238
+ style: sectionThemeStyle(section.theme),
239
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-hero-inner", children: [
240
+ /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-hero-copy", children: [
241
+ eyebrow ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-eyebrow", children: eyebrow }) : null,
242
+ /* @__PURE__ */ jsx2("h1", { className: "rt-tpl-hero-title", children: title.split("\n").map((line, i) => /* @__PURE__ */ jsx2("span", { className: "rt-tpl-hero-title-line", children: line }, i)) }),
243
+ description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-hero-desc", children: description }) : null,
244
+ cta ? /* @__PURE__ */ jsx2(CtaLink, { cta, track: "cta_hero" }) : null
245
+ ] }),
246
+ showImage ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-hero-media", children: /* @__PURE__ */ jsx2(
247
+ "img",
248
+ {
249
+ className: "rt-tpl-hero-img",
250
+ src: imageSrc,
251
+ alt: imageAlt ?? "",
252
+ loading: "eager"
253
+ }
254
+ ) }) : null
255
+ ] })
256
+ }
257
+ );
258
+ }
259
+ function TemplateRichText(section) {
260
+ const { heading, body } = section.props;
261
+ return /* @__PURE__ */ jsx2(
262
+ "section",
263
+ {
264
+ "data-roottale-tpl": "rich-text",
265
+ id: section.id,
266
+ className: "rt-tpl-section rt-tpl-rich-text",
267
+ style: sectionThemeStyle(section.theme),
268
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
269
+ heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
270
+ body.split("\n\n").map((para, i) => /* @__PURE__ */ jsx2("p", { className: "rt-tpl-rich-text-p", children: para }, i))
271
+ ] })
272
+ }
273
+ );
274
+ }
275
+ function TemplateServices(section) {
276
+ const { heading, description, items } = section.props;
277
+ return /* @__PURE__ */ jsx2(
278
+ "section",
279
+ {
280
+ "data-roottale-tpl": "services",
281
+ "data-variant": section.variant,
282
+ id: section.id,
283
+ className: "rt-tpl-section rt-tpl-services",
284
+ style: sectionThemeStyle(section.theme),
285
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
286
+ heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
287
+ description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-section-desc", children: description }) : null,
288
+ /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-services-list", children: items.map((item, i) => {
289
+ const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
290
+ /* @__PURE__ */ jsx2("h3", { className: "rt-tpl-services-title", children: item.title }),
291
+ item.description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-services-desc", children: item.description }) : null
292
+ ] });
293
+ return /* @__PURE__ */ jsx2("li", { className: "rt-tpl-services-item", children: item.href ? /* @__PURE__ */ jsx2(
294
+ "a",
295
+ {
296
+ className: "rt-tpl-services-link",
297
+ href: item.href,
298
+ "data-track": "service_card",
299
+ children: body
300
+ }
301
+ ) : body }, i);
302
+ }) })
303
+ ] })
304
+ }
305
+ );
306
+ }
307
+ function TemplateFaq(section) {
308
+ const { heading, items, jsonLd } = section.props;
309
+ return /* @__PURE__ */ jsxs2(
310
+ "section",
311
+ {
312
+ "data-roottale-tpl": "faq",
313
+ id: section.id,
314
+ className: "rt-tpl-section rt-tpl-faq",
315
+ style: sectionThemeStyle(section.theme),
316
+ children: [
317
+ /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container", children: [
318
+ heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
319
+ /* @__PURE__ */ jsx2("div", { className: "rt-tpl-faq-list", children: items.map((item, i) => /* @__PURE__ */ jsxs2("details", { className: "rt-tpl-faq-item", children: [
320
+ /* @__PURE__ */ jsx2("summary", { className: "rt-tpl-faq-q", children: item.question }),
321
+ /* @__PURE__ */ jsx2("p", { className: "rt-tpl-faq-a", children: item.answer })
322
+ ] }, i)) })
323
+ ] }),
324
+ jsonLd ? /* @__PURE__ */ jsx2(
325
+ "script",
326
+ {
327
+ type: "application/ld+json",
328
+ dangerouslySetInnerHTML: {
329
+ __html: JSON.stringify({
330
+ "@context": "https://schema.org",
331
+ "@type": "FAQPage",
332
+ mainEntity: items.map((item) => ({
333
+ "@type": "Question",
334
+ name: item.question,
335
+ acceptedAnswer: { "@type": "Answer", text: item.answer }
336
+ }))
337
+ })
338
+ }
339
+ }
340
+ ) : null
341
+ ]
342
+ }
343
+ );
344
+ }
345
+ function TemplateCtaBanner(section) {
346
+ const { title, description, cta } = section.props;
347
+ return /* @__PURE__ */ jsx2(
348
+ "section",
349
+ {
350
+ "data-roottale-tpl": "cta-banner",
351
+ id: section.id,
352
+ className: "rt-tpl-section rt-tpl-cta-banner",
353
+ style: sectionThemeStyle(section.theme),
354
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-cta-banner-inner", children: [
355
+ /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-cta-banner-title", children: title }),
356
+ description ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-cta-banner-desc", children: description }) : null,
357
+ cta ? /* @__PURE__ */ jsx2(CtaLink, { cta, track: "cta_banner" }) : null
358
+ ] })
359
+ }
360
+ );
361
+ }
362
+ function TrustCredentialIcon() {
363
+ return /* @__PURE__ */ jsxs2(
364
+ "svg",
365
+ {
366
+ className: "rt-cred__icon",
367
+ viewBox: "0 0 16 16",
368
+ "aria-hidden": "true",
369
+ focusable: "false",
370
+ children: [
371
+ /* @__PURE__ */ jsx2("path", { d: "M8 1.75 13 3.5v3.75c0 3-2 5.45-5 7-3-1.55-5-4-5-7V3.5z" }),
372
+ /* @__PURE__ */ jsx2("path", { d: "m5.75 8 1.45 1.45 3.2-3.4" })
373
+ ]
374
+ }
375
+ );
376
+ }
377
+ function TemplateTrust(section) {
378
+ const { heading, metrics, credentials, logos, disclaimer } = section.props;
379
+ return /* @__PURE__ */ jsx2(
380
+ "section",
381
+ {
382
+ "data-roottale-tpl": "trust",
383
+ "data-variant": section.variant,
384
+ id: section.id,
385
+ className: "rt-tpl-section rt-tpl-trust",
386
+ style: sectionThemeStyle(section.theme),
387
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-trust-inner", children: [
388
+ heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
389
+ section.variant === "stat-band" ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-trust-stat-band", children: (metrics ?? []).map((metric) => /* @__PURE__ */ jsxs2(
390
+ "div",
391
+ {
392
+ className: "rt-stat",
393
+ "data-variant": "band-item",
394
+ children: [
395
+ /* @__PURE__ */ jsxs2("span", { className: "rt-stat__value", children: [
396
+ metric.value,
397
+ metric.unit ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__unit", children: metric.unit }) : null
398
+ ] }),
399
+ /* @__PURE__ */ jsx2("span", { className: "rt-stat__label", children: metric.label }),
400
+ metric.source ? /* @__PURE__ */ jsx2("span", { className: "rt-stat__source", children: metric.source }) : null
401
+ ]
402
+ },
403
+ `${metric.value}-${metric.label}`
404
+ )) }) : null,
405
+ section.variant === "logo-cloud" ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-trust-logo-list", children: (logos ?? []).map((logo) => /* @__PURE__ */ jsx2("li", { className: "rt-tpl-trust-logo-item", children: logo.href ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-trust-logo-link", href: logo.href, children: /* @__PURE__ */ jsx2(
406
+ "img",
407
+ {
408
+ className: "rt-tpl-trust-logo",
409
+ src: logo.src,
410
+ alt: logo.alt,
411
+ loading: "lazy"
412
+ }
413
+ ) }) : /* @__PURE__ */ jsx2(
414
+ "img",
415
+ {
416
+ className: "rt-tpl-trust-logo",
417
+ src: logo.src,
418
+ alt: logo.alt,
419
+ loading: "lazy"
420
+ }
421
+ ) }, `${logo.src}-${logo.alt}`)) }) : null,
422
+ section.variant === "feature-grid" ? /* @__PURE__ */ jsx2("ul", { className: "rt-tpl-trust-cred-list", children: (credentials ?? []).map((credential) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsxs2("span", { className: "rt-cred", "data-kind": credential.kind, children: [
423
+ /* @__PURE__ */ jsx2(TrustCredentialIcon, {}),
424
+ /* @__PURE__ */ jsx2("span", { className: "rt-cred__label", children: credential.name }),
425
+ credential.issuer ? /* @__PURE__ */ jsx2("span", { className: "rt-cred__issuer", children: credential.issuer }) : null
426
+ ] }) }, `${credential.name}-${credential.issuer ?? ""}`)) }) : null,
427
+ disclaimer ? /* @__PURE__ */ jsx2("p", { className: "rt-tpl-trust-disclaimer", children: disclaimer }) : null
428
+ ] })
429
+ }
430
+ );
431
+ }
432
+ function telHref(phone) {
433
+ return `tel:${phone.replace(/[^\d+]/g, "")}`;
434
+ }
435
+ function ContactMap({ url }) {
436
+ if (isAllowedEmbedSrc(url)) {
437
+ return /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-map", children: /* @__PURE__ */ jsx2(
438
+ "iframe",
439
+ {
440
+ src: url,
441
+ title: "\uC624\uC2DC\uB294\uAE38 \uC9C0\uB3C4",
442
+ loading: "lazy",
443
+ referrerPolicy: "no-referrer-when-downgrade"
444
+ }
445
+ ) });
446
+ }
447
+ return /* @__PURE__ */ jsx2("p", { className: "rt-tpl-contact-map-fallback", children: /* @__PURE__ */ jsx2("a", { href: url, rel: "noopener noreferrer", target: "_blank", children: "\uAE38\uCC3E\uAE30" }) });
448
+ }
449
+ function TemplateContact(section) {
450
+ const { heading, form, phone, address, hours, mapEmbedUrl } = section.props;
451
+ return /* @__PURE__ */ jsx2(
452
+ "section",
453
+ {
454
+ "data-roottale-tpl": "contact",
455
+ "data-variant": section.variant,
456
+ id: section.id,
457
+ className: "rt-tpl-section rt-tpl-contact",
458
+ style: sectionThemeStyle(section.theme),
459
+ children: /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-container rt-tpl-contact-inner", children: [
460
+ /* @__PURE__ */ jsxs2("div", { className: "rt-tpl-contact-info", children: [
461
+ heading ? /* @__PURE__ */ jsx2("h2", { className: "rt-tpl-heading", children: heading }) : null,
462
+ phone ? /* @__PURE__ */ jsx2("a", { className: "rt-tpl-contact-phone", href: telHref(phone), "data-track": "cta_call", children: phone }) : null,
463
+ address ? /* @__PURE__ */ jsx2("address", { className: "rt-tpl-contact-address", children: address }) : null,
464
+ hours ? /* @__PURE__ */ jsxs2("table", { className: "rt-tpl-contact-hours", children: [
465
+ /* @__PURE__ */ jsx2("caption", { className: "rt-tpl-sr-only", children: "\uC9C4\uB8CC\uC2DC\uAC04" }),
466
+ /* @__PURE__ */ jsx2("tbody", { children: hours.map((row) => /* @__PURE__ */ jsxs2("tr", { children: [
467
+ /* @__PURE__ */ jsx2("th", { scope: "row", children: row.day }),
468
+ /* @__PURE__ */ jsxs2("td", { children: [
469
+ /* @__PURE__ */ jsx2("span", { children: row.open }),
470
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "-" }),
471
+ /* @__PURE__ */ jsx2("span", { children: row.close }),
472
+ row.note ? /* @__PURE__ */ jsx2("span", { className: "rt-tpl-contact-hours-note", children: row.note }) : null
473
+ ] })
474
+ ] }, `${row.day}-${row.open}-${row.close}`)) })
475
+ ] }) : null,
476
+ mapEmbedUrl ? /* @__PURE__ */ jsx2(ContactMap, { url: mapEmbedUrl }) : null
477
+ ] }),
478
+ form.enabled ? /* @__PURE__ */ jsx2("div", { className: "rt-tpl-contact-form", children: /* @__PURE__ */ jsx2(RootTaleLeadForm, { action: "/api/inquiry", vertical: form.vertical }) }) : null
479
+ ] })
480
+ }
481
+ );
482
+ }
483
+
484
+ // src/render-section-node.tsx
485
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
486
+ function warnSkippedCmsBound(sectionType, isDev) {
487
+ if (isDev()) {
488
+ console.warn(
489
+ `[cms-renderer-next] section \uB178\uB4DC sectionType=${sectionType} \uB294 apiKey \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC5B4 \uB80C\uB354 skip`
490
+ );
491
+ }
492
+ }
493
+ function renderSectionNode(node, key, context) {
494
+ const attrs = node.attrs ?? {};
495
+ const input = {
496
+ type: typeof attrs.sectionType === "string" ? attrs.sectionType : "",
497
+ variant: attrs.variant ?? void 0,
498
+ theme: attrs.theme ?? void 0,
499
+ props: attrs.props ?? {}
500
+ };
501
+ const parsed = SectionSchema.safeParse(input);
502
+ if (!parsed.success) {
503
+ if (context.isDev()) {
504
+ console.warn(
505
+ `[cms-renderer-next] section \uB178\uB4DC parse \uC2E4\uD328 (sectionType=${input.type}) \u2014 \uB80C\uB354 skip:`,
506
+ parsed.error.issues
507
+ );
508
+ }
509
+ return null;
510
+ }
511
+ const section = parsed.data;
512
+ switch (section.type) {
513
+ case "hero":
514
+ return /* @__PURE__ */ jsx3(TemplateHero, { ...section }, key);
515
+ case "rich-text":
516
+ return /* @__PURE__ */ jsx3(TemplateRichText, { ...section }, key);
517
+ case "services":
518
+ return /* @__PURE__ */ jsx3(TemplateServices, { ...section }, key);
519
+ case "faq":
520
+ return /* @__PURE__ */ jsx3(TemplateFaq, { ...section }, key);
521
+ case "cta-banner":
522
+ return /* @__PURE__ */ jsx3(TemplateCtaBanner, { ...section }, key);
523
+ case "trust":
524
+ return /* @__PURE__ */ jsx3(TemplateTrust, { ...section }, key);
525
+ case "contact":
526
+ if (!context.cms) {
527
+ warnSkippedCmsBound(section.type, context.isDev);
528
+ return null;
529
+ }
530
+ return /* @__PURE__ */ jsx3(TemplateContact, { ...section }, key);
531
+ case "blog-list": {
532
+ if (!context.cms) {
533
+ warnSkippedCmsBound(section.type, context.isDev);
534
+ return null;
535
+ }
536
+ const s = section;
537
+ const base = s.props.blogBasePath ?? "/blog";
538
+ return /* @__PURE__ */ jsx3(
539
+ "section",
540
+ {
541
+ "data-roottale-tpl": "blog-list",
542
+ "data-roottale-cms": "list",
543
+ id: s.id,
544
+ className: "rt-tpl-section rt-tpl-blog-list",
545
+ style: sectionThemeStyle(s.theme),
546
+ children: /* @__PURE__ */ jsxs3("div", { className: "rt-tpl-container", children: [
547
+ s.props.heading ? /* @__PURE__ */ jsx3("h2", { className: "rt-tpl-heading", children: s.props.heading }) : null,
548
+ context.renderBlogList({
549
+ apiKey: context.cms.apiKey,
550
+ baseUrl: context.cms.baseUrl,
551
+ limit: s.props.limit,
552
+ emptyMessage: s.props.emptyMessage,
553
+ postHref: (post) => `${base}/${post.slug}`
554
+ })
555
+ ] })
556
+ },
557
+ key
558
+ );
559
+ }
560
+ case "lead-form": {
561
+ if (!context.cms) {
562
+ warnSkippedCmsBound(section.type, context.isDev);
563
+ return null;
564
+ }
565
+ const s = section;
566
+ return /* @__PURE__ */ jsx3(
567
+ "section",
568
+ {
569
+ "data-roottale-tpl": "lead-form",
570
+ "data-roottale-cms": "lead-form",
571
+ id: s.id,
572
+ className: "rt-tpl-section rt-tpl-lead-form",
573
+ style: sectionThemeStyle(s.theme),
574
+ children: /* @__PURE__ */ jsx3("div", { className: "rt-tpl-container", children: /* @__PURE__ */ jsx3(
575
+ RootTaleLeadForm,
576
+ {
577
+ action: s.props.action,
578
+ vertical: s.props.vertical,
579
+ heading: s.props.heading,
580
+ description: s.props.description,
581
+ submitLabel: s.props.submitLabel
582
+ }
583
+ ) })
584
+ },
585
+ key
586
+ );
587
+ }
588
+ default:
589
+ if (context.isDev()) {
590
+ console.warn(
591
+ `[cms-renderer-next] section \uB178\uB4DC sectionType=${section.type} \uB294 renderNode \uC704\uC784 \uB300\uC0C1\uC774 \uC544\uB2D8(cms-bound/\uBBF8\uC9C0) \u2014 skip`
592
+ );
593
+ }
594
+ return null;
595
+ }
596
+ }
597
+
598
+ // src/PostCard.tsx
599
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
600
+ function firstCategoryName(terms) {
601
+ if (!terms) return null;
602
+ const cat = terms.find((t) => t.taxonomy === "category");
603
+ return cat ? cat.name : null;
604
+ }
605
+ function RootTalePostCard(props) {
606
+ const { post, href = defaultHref, display } = props;
607
+ const showDate = display ? display.showDate : true;
608
+ const showAuthor = display ? display.showAuthor : false;
609
+ const showImage = display?.showImage !== false;
610
+ const showCategory = display?.showCategory !== false;
611
+ const authorName = post.authorName;
612
+ const publishedDate = showDate ? formatPublishedDate(post.publishedAt) : "";
613
+ const views = typeof post.viewCount === "number" ? post.viewCount : null;
614
+ const imageUrl = showImage ? post.featuredImageUrl : null;
615
+ const category = showCategory ? firstCategoryName(post.terms) : null;
616
+ return /* @__PURE__ */ jsx4("article", { "data-roottale-cms": "card", className: "rt-cms-card", children: /* @__PURE__ */ jsxs4("a", { className: "rt-cms-card-link", href: href(post), children: [
617
+ imageUrl ? (
618
+ // eslint-disable-next-line @next/next/no-img-element -- SSR string URL, framework-agnostic
619
+ /* @__PURE__ */ jsx4(
620
+ "img",
621
+ {
622
+ className: "rt-cms-card-image",
623
+ src: imageUrl,
624
+ alt: "",
625
+ loading: "lazy",
626
+ decoding: "async"
627
+ }
628
+ )
629
+ ) : null,
630
+ category ? /* @__PURE__ */ jsx4("span", { className: "rt-cms-card-category", children: category }) : null,
631
+ publishedDate || showAuthor && authorName || views !== null ? /* @__PURE__ */ jsxs4("p", { className: "rt-cms-meta", children: [
632
+ publishedDate ? /* @__PURE__ */ jsx4("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "date", children: publishedDate }) : null,
633
+ showAuthor && authorName ? /* @__PURE__ */ jsxs4("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "author", children: [
634
+ "\uC791\uC131\uC790 ",
635
+ authorName
636
+ ] }) : null,
637
+ views !== null ? /* @__PURE__ */ jsxs4("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "views", children: [
638
+ "\uC870\uD68C ",
639
+ views.toLocaleString("ko-KR")
640
+ ] }) : null
641
+ ] }) : null,
642
+ /* @__PURE__ */ jsx4("h2", { className: "rt-cms-title", children: post.title }),
643
+ post.excerpt ? /* @__PURE__ */ jsx4("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null,
644
+ /* @__PURE__ */ jsx4("span", { className: "rt-cms-card-action", "aria-hidden": "true", children: "\uC77D\uAE30" })
645
+ ] }) });
646
+ }
647
+ function defaultHref(post) {
648
+ return `/blog/${post.slug}`;
649
+ }
650
+ function formatPublishedDate(iso) {
651
+ const d = new Date(iso);
652
+ if (Number.isNaN(d.getTime())) return "";
653
+ return new Intl.DateTimeFormat("ko-KR", {
654
+ year: "numeric",
655
+ month: "2-digit",
656
+ day: "2-digit"
657
+ }).format(d);
658
+ }
659
+
660
+ // src/TableOfContents.tsx
661
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
662
+ function RootTaleTableOfContents(props) {
663
+ const { entries, title = "\uBAA9\uCC28" } = props;
664
+ if (entries.length === 0) return null;
665
+ return /* @__PURE__ */ jsx5("nav", { "data-roottale-cms": "toc", className: "rt-cms-toc", "aria-label": title, children: /* @__PURE__ */ jsxs5("details", { className: "rt-cms-toc-details", open: true, children: [
666
+ /* @__PURE__ */ jsxs5("summary", { className: "rt-cms-toc-summary", children: [
667
+ title ? /* @__PURE__ */ jsx5("span", { className: "rt-cms-toc-title", children: title }) : null,
668
+ /* @__PURE__ */ jsx5("span", { className: "rt-cms-toc-toggle", "aria-hidden": "true" })
669
+ ] }),
670
+ /* @__PURE__ */ jsx5("ol", { className: "rt-cms-toc-list", children: entries.map((entry) => /* @__PURE__ */ jsx5(
671
+ "li",
672
+ {
673
+ className: `rt-cms-toc-item rt-cms-toc-level-${entry.level}`,
674
+ children: /* @__PURE__ */ jsx5("a", { href: `#${entry.id}`, children: entry.text })
675
+ },
676
+ entry.id
677
+ )) })
678
+ ] }) });
679
+ }
680
+
681
+ // src/theme-css-vars.ts
682
+ function themeToCssVars(theme) {
683
+ if (!theme) return null;
684
+ const vars = {};
685
+ const c = theme.colors ?? {};
686
+ if (c.primary) vars["--rt-color-primary"] = c.primary;
687
+ if (c.primaryForeground) vars["--rt-color-primary-foreground"] = c.primaryForeground;
688
+ if (c.foreground) vars["--rt-color-foreground"] = c.foreground;
689
+ if (c.background) vars["--rt-color-background"] = c.background;
690
+ if (c.secondary) vars["--rt-color-secondary"] = c.secondary;
691
+ if (c.secondaryForeground) vars["--rt-color-secondary-foreground"] = c.secondaryForeground;
692
+ if (c.accent) vars["--rt-color-accent"] = c.accent;
693
+ if (c.accentForeground) vars["--rt-color-accent-foreground"] = c.accentForeground;
694
+ if (c.muted) vars["--rt-color-muted"] = c.muted;
695
+ if (c.mutedForeground) vars["--rt-color-muted-foreground"] = c.mutedForeground;
696
+ if (c.destructive) vars["--rt-color-destructive"] = c.destructive;
697
+ if (c.destructiveForeground) vars["--rt-color-destructive-foreground"] = c.destructiveForeground;
698
+ if (c.border) vars["--rt-color-border"] = c.border;
699
+ if (c.input) vars["--rt-color-input"] = c.input;
700
+ if (c.ring) vars["--rt-color-ring"] = c.ring;
701
+ const f = theme.fonts ?? {};
702
+ if (f.body) vars["--rt-font-body"] = f.body;
703
+ if (f.display) vars["--rt-font-display"] = f.display;
704
+ const r = theme.radius ?? {};
705
+ if (r.sm) vars["--rt-radius-sm"] = r.sm;
706
+ if (r.md) vars["--rt-radius-md"] = r.md;
707
+ if (r.lg) vars["--rt-radius-lg"] = r.lg;
708
+ if (r.xl) vars["--rt-radius-xl"] = r.xl;
709
+ if (Object.keys(vars).length === 0) return null;
710
+ return vars;
711
+ }
712
+
713
+ // src/toc.ts
714
+ function tocLevel(rawLevel) {
715
+ if (rawLevel < 1 || rawLevel > 4) return null;
716
+ if (rawLevel < 3) return 2;
717
+ return rawLevel === 3 ? 3 : 4;
718
+ }
719
+ function headingToId(text) {
720
+ return text.trim().toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
721
+ }
722
+ function textOf(node) {
723
+ if (typeof node.text === "string") return node.text;
724
+ if (!Array.isArray(node.content)) return "";
725
+ return node.content.map(textOf).join("");
726
+ }
727
+ function extractToc(doc) {
728
+ const root = doc;
729
+ const content = Array.isArray(root.content) ? root.content : [];
730
+ const entries = [];
731
+ const idCounts = /* @__PURE__ */ new Map();
732
+ for (const node of content) {
733
+ if (node?.type !== "heading") continue;
734
+ const rawLevel = Number(node.attrs?.level ?? 2);
735
+ const level = tocLevel(rawLevel);
736
+ if (level === null) continue;
737
+ const text = textOf(node).trim();
738
+ if (!text) continue;
739
+ const base = headingToId(text) || "heading";
740
+ const count = idCounts.get(base) ?? 0;
741
+ const id = count === 0 ? base : `${base}-${count}`;
742
+ idCounts.set(base, count + 1);
743
+ entries.push({ level, text, id });
744
+ }
745
+ return entries;
746
+ }
747
+ function attachHeadingIds(doc, entries) {
748
+ const root = doc;
749
+ const content = Array.isArray(root.content) ? root.content : [];
750
+ let cursor = 0;
751
+ const nextContent = content.map((node) => {
752
+ if (node?.type !== "heading") return node;
753
+ const rawLevel = Number(node.attrs?.level ?? 2);
754
+ if (rawLevel < 1 || rawLevel > 4) return node;
755
+ if (!textOf(node).trim()) return node;
756
+ const entry = entries[cursor++];
757
+ if (!entry) return node;
758
+ return {
759
+ ...node,
760
+ attrs: { ...node.attrs ?? {}, id: entry.id }
761
+ };
762
+ });
763
+ return { ...root, content: nextContent };
764
+ }
765
+
766
+ // src/FloatingCta.tsx
767
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
768
+ function RootTaleFloatingCta(props) {
769
+ const { buttons, position = "bottom-right" } = props;
770
+ if (!buttons || buttons.length === 0) return null;
771
+ return /* @__PURE__ */ jsx6(
772
+ "div",
773
+ {
774
+ "data-roottale-cms": "floating-cta",
775
+ className: `rt-cms-floating-cta rt-cms-floating-cta--${position}`,
776
+ children: buttons.map((btn, idx) => {
777
+ const isExternal = btn.type === "kakao" || btn.type === "custom";
778
+ return /* @__PURE__ */ jsxs6(
779
+ "a",
780
+ {
781
+ href: btn.href,
782
+ className: `rt-cms-floating-cta__btn rt-cms-floating-cta__btn--${btn.type}`,
783
+ target: isExternal ? "_blank" : void 0,
784
+ rel: isExternal ? "noopener noreferrer" : void 0,
785
+ children: [
786
+ /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", className: "rt-cms-floating-cta__icon", children: btn.icon ?? defaultIcon(btn.type) }),
787
+ /* @__PURE__ */ jsx6("span", { className: "rt-cms-floating-cta__label", children: btn.label })
788
+ ]
789
+ },
790
+ `${btn.type}-${idx}`
791
+ );
792
+ })
793
+ }
794
+ );
795
+ }
796
+ function defaultIcon(type) {
797
+ switch (type) {
798
+ case "phone":
799
+ return "\u260E";
800
+ case "contact":
801
+ return "\u2709";
802
+ case "kakao":
803
+ return "\u{1F4AC}";
804
+ case "custom":
805
+ return "\u2192";
806
+ }
807
+ }
808
+
809
+ // src/block-to-react.tsx
810
+ import {
811
+ applyBlockBindings,
812
+ createFieldBindingResolver,
813
+ sanitizeHtml
814
+ } from "@roottale/cms-core";
815
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
816
+ function isString(value) {
817
+ return typeof value === "string";
818
+ }
819
+ function htmlContent(html) {
820
+ return { __html: sanitizeHtml(html) };
821
+ }
822
+ function renderInner(blocks) {
823
+ return blocks.map((b, i) => renderBlock(b, i));
824
+ }
825
+ function renderBlock(block, key = 0) {
826
+ const dataBlockId = block._id;
827
+ switch (block.name) {
828
+ case "core/paragraph": {
829
+ const text = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
830
+ return /* @__PURE__ */ jsx7(
831
+ "p",
832
+ {
833
+ "data-block-id": dataBlockId,
834
+ dangerouslySetInnerHTML: htmlContent(text)
835
+ },
836
+ key
837
+ );
838
+ }
839
+ case "core/heading": {
840
+ const levelRaw = block.attributes.level;
841
+ const level = typeof levelRaw === "number" && levelRaw >= 2 && levelRaw <= 4 ? levelRaw : 2;
842
+ const text = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
843
+ const props = {
844
+ "data-block-id": dataBlockId,
845
+ dangerouslySetInnerHTML: htmlContent(text)
846
+ };
847
+ if (level === 2) return /* @__PURE__ */ jsx7("h2", { ...props }, key);
848
+ if (level === 3) return /* @__PURE__ */ jsx7("h3", { ...props }, key);
849
+ return /* @__PURE__ */ jsx7("h4", { ...props }, key);
850
+ }
851
+ case "core/image": {
852
+ const src = isString(block.attributes.url) ? block.attributes.url : "";
853
+ const alt = isString(block.attributes.alt) ? block.attributes.alt : "";
854
+ const caption = isString(block.attributes.caption) ? block.attributes.caption : null;
855
+ return /* @__PURE__ */ jsxs7("figure", { "data-block-id": dataBlockId, children: [
856
+ /* @__PURE__ */ jsx7("img", { src, alt, loading: "lazy" }),
857
+ caption && /* @__PURE__ */ jsx7("figcaption", { children: caption })
858
+ ] }, key);
859
+ }
860
+ case "core/list": {
861
+ const ordered = block.attributes.ordered === true;
862
+ const items = (block.innerBlocks ?? []).map(
863
+ (item, i) => item.rawHtml ? /* @__PURE__ */ jsx7(
864
+ "li",
865
+ {
866
+ dangerouslySetInnerHTML: htmlContent(item.rawHtml)
867
+ },
868
+ i
869
+ ) : /* @__PURE__ */ jsx7("li", { children: renderInner(item.innerBlocks ?? []) }, i)
870
+ );
871
+ return ordered ? /* @__PURE__ */ jsx7("ol", { "data-block-id": dataBlockId, children: items }, key) : /* @__PURE__ */ jsx7("ul", { "data-block-id": dataBlockId, children: items }, key);
872
+ }
873
+ case "core/quote": {
874
+ const inner = renderInner(block.innerBlocks ?? []);
875
+ const citation = isString(block.attributes.citation) ? block.attributes.citation : null;
876
+ return /* @__PURE__ */ jsxs7("blockquote", { "data-block-id": dataBlockId, children: [
877
+ inner,
878
+ citation && /* @__PURE__ */ jsx7("cite", { children: citation })
879
+ ] }, key);
880
+ }
881
+ case "core/code": {
882
+ const code = isString(block.attributes.content) ? block.attributes.content : block.rawHtml ?? "";
883
+ const lang = isString(block.attributes.language) ? block.attributes.language : void 0;
884
+ return /* @__PURE__ */ jsx7(
885
+ "pre",
886
+ {
887
+ "data-block-id": dataBlockId,
888
+ "data-language": lang,
889
+ children: /* @__PURE__ */ jsx7("code", { children: code })
890
+ },
891
+ key
892
+ );
893
+ }
894
+ case "core/columns": {
895
+ return /* @__PURE__ */ jsx7(
896
+ "div",
897
+ {
898
+ className: "rt-columns",
899
+ "data-block-id": dataBlockId,
900
+ children: renderInner(block.innerBlocks ?? [])
901
+ },
902
+ key
903
+ );
904
+ }
905
+ case "core/separator":
906
+ return /* @__PURE__ */ jsx7("hr", { "data-block-id": dataBlockId }, key);
907
+ case "core/spacer": {
908
+ const heightRaw = block.attributes.height;
909
+ const height = isString(heightRaw) ? heightRaw : "32px";
910
+ return /* @__PURE__ */ jsx7(
911
+ "div",
912
+ {
913
+ className: "rt-spacer",
914
+ "data-block-id": dataBlockId,
915
+ style: { height }
916
+ },
917
+ key
918
+ );
919
+ }
920
+ case "core/group": {
921
+ return /* @__PURE__ */ jsx7(
922
+ "div",
923
+ {
924
+ className: "rt-group",
925
+ "data-block-id": dataBlockId,
926
+ children: renderInner(block.innerBlocks ?? [])
927
+ },
928
+ key
929
+ );
930
+ }
931
+ default: {
932
+ const rawHtml = block.rawHtml;
933
+ return /* @__PURE__ */ jsx7(
934
+ "div",
935
+ {
936
+ className: "rt-unknown-block",
937
+ "data-block-id": dataBlockId,
938
+ "data-block-name": block.name,
939
+ "data-rawhtml-suppressed": rawHtml ? "true" : void 0,
940
+ children: renderInner(block.innerBlocks ?? [])
941
+ },
942
+ key
943
+ );
944
+ }
945
+ }
946
+ }
947
+ function renderBlocks(blocks) {
948
+ return blocks.map((b, i) => renderBlock(b, i));
949
+ }
950
+ function renderBoundBlocks(blocks, opts = {}) {
951
+ const resolver = opts.resolver ?? createFieldBindingResolver(opts.fields);
952
+ return renderBlocks(applyBlockBindings(blocks, resolver));
953
+ }
954
+
955
+ // src/server.tsx
956
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
957
+ function authorImageRadiusCss(radius) {
958
+ if (radius === "circle") return "999px";
959
+ if (radius === "rounded") return "0.875rem";
960
+ return "0";
961
+ }
962
+ function RootTaleThemeProvider(props) {
963
+ const style = themeToCssVars(props.theme ?? null);
964
+ if (!style) return /* @__PURE__ */ jsx8(Fragment3, { children: props.children });
965
+ return /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "theme-root", "data-rt-theme": "", style, children: props.children });
966
+ }
967
+ async function RootTaleBlogList(props) {
968
+ const {
969
+ apiKey,
970
+ baseUrl,
971
+ limit,
972
+ type = "post",
973
+ collection,
974
+ collections,
975
+ emptyMessage = "\uC544\uC9C1 \uBC1C\uD589\uB41C \uAE00\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
976
+ showCategoryFilter = false,
977
+ activeCategory,
978
+ categoryHref = defaultCategoryHref,
979
+ allCategoriesLabel = "\uC804\uCCB4",
980
+ author,
981
+ theme: themeProp
982
+ } = props;
983
+ const postHref = props.postHref ?? (collections ? makeCollectionPostHref(collections) : defaultPostHref);
984
+ const [page, themeStyle, blogSettings] = await Promise.all([
985
+ fetchPosts({ apiKey, baseUrl, limit, type, ...author ? { author } : {} }),
986
+ resolveThemeStyle({ themeProp, apiKey, baseUrl }),
987
+ fetchBlogSettings({ apiKey, baseUrl }).catch(() => DEFAULT_BLOG_SETTINGS)
988
+ ]);
989
+ const items = collection && collections ? postsForCollection(page.items, collections, collection) : page.items;
990
+ if (items.length === 0) {
991
+ return /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "list", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx8("p", { className: "rt-cms-empty", children: emptyMessage }) });
992
+ }
993
+ const categories = showCategoryFilter ? collectCategories(items) : [];
994
+ const visiblePosts = showCategoryFilter ? filterPostsByCategory(items, activeCategory) : items;
995
+ return /* @__PURE__ */ jsxs8("div", { "data-roottale-cms": "list", style: themeStyle ?? void 0, children: [
996
+ showCategoryFilter && categories.length > 0 ? /* @__PURE__ */ jsxs8("nav", { className: "rt-cms-filter", "aria-label": "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130", children: [
997
+ /* @__PURE__ */ jsx8(
998
+ "a",
999
+ {
1000
+ className: "rt-cms-filter-chip",
1001
+ href: categoryHref(null),
1002
+ "data-active": !activeCategory ? "" : void 0,
1003
+ "aria-current": !activeCategory ? "true" : void 0,
1004
+ children: allCategoriesLabel
1005
+ }
1006
+ ),
1007
+ categories.map((c) => /* @__PURE__ */ jsx8(
1008
+ "a",
1009
+ {
1010
+ className: "rt-cms-filter-chip",
1011
+ href: categoryHref(c.slug),
1012
+ "data-active": activeCategory === c.slug ? "" : void 0,
1013
+ "aria-current": activeCategory === c.slug ? "true" : void 0,
1014
+ children: c.name
1015
+ },
1016
+ c.slug
1017
+ ))
1018
+ ] }) : null,
1019
+ visiblePosts.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-empty", children: emptyMessage }) : /* @__PURE__ */ jsx8("ul", { className: "rt-cms-list", children: visiblePosts.map((post) => /* @__PURE__ */ jsx8("li", { className: "rt-cms-list-item", children: /* @__PURE__ */ jsx8(
1020
+ RootTalePostCard,
1021
+ {
1022
+ post,
1023
+ href: postHref,
1024
+ display: resolvePostDisplay(post, blogSettings)
1025
+ }
1026
+ ) }, post.id)) })
1027
+ ] });
1028
+ }
1029
+ async function RootTaleBlogCategories(props) {
1030
+ const {
1031
+ apiKey,
1032
+ baseUrl,
1033
+ limit,
1034
+ collection,
1035
+ collections,
1036
+ categoryHref = (slug) => defaultCategoryHref(slug),
1037
+ emptyMessage = "\uC544\uC9C1 \uCE74\uD14C\uACE0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
1038
+ theme: themeProp
1039
+ } = props;
1040
+ const [page, themeStyle] = await Promise.all([
1041
+ fetchPosts({ apiKey, baseUrl, limit, type: "post" }),
1042
+ resolveThemeStyle({ themeProp, apiKey, baseUrl })
1043
+ ]);
1044
+ const categories = collection && collections ? collectCollectionCategories(page.items, collections, collection) : collectCategoriesWithCounts(page.items);
1045
+ return /* @__PURE__ */ jsx8(
1046
+ "nav",
1047
+ {
1048
+ "data-roottale-cms": "categories",
1049
+ className: "rt-cms-categories",
1050
+ "aria-label": "\uBE14\uB85C\uADF8 \uCE74\uD14C\uACE0\uB9AC",
1051
+ style: themeStyle ?? void 0,
1052
+ children: categories.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-empty", children: emptyMessage }) : /* @__PURE__ */ jsx8("ul", { className: "rt-cms-category-list", children: categories.map((category) => /* @__PURE__ */ jsx8("li", { className: "rt-cms-category-list-item", children: /* @__PURE__ */ jsxs8(
1053
+ "a",
1054
+ {
1055
+ className: "rt-cms-category-link",
1056
+ href: categoryHref(category.slug),
1057
+ children: [
1058
+ /* @__PURE__ */ jsx8("span", { className: "rt-cms-category-name", children: category.name }),
1059
+ /* @__PURE__ */ jsx8("span", { className: "rt-cms-category-count", children: category.count })
1060
+ ]
1061
+ }
1062
+ ) }, category.slug)) })
1063
+ }
1064
+ );
1065
+ }
1066
+ async function RootTaleBlogPost(props) {
1067
+ const {
1068
+ apiKey,
1069
+ slugOrId,
1070
+ baseUrl,
1071
+ notFoundElement,
1072
+ showTableOfContents,
1073
+ tableOfContentsTitle,
1074
+ relatedPostsCount = 0,
1075
+ relatedPostsTitle = "\uAD00\uB828 \uAE00",
1076
+ collections,
1077
+ theme: themeProp
1078
+ } = props;
1079
+ const [post, themeStyle, blogSettings] = await Promise.all([
1080
+ fetchPost({ apiKey, slugOrId, baseUrl }),
1081
+ resolveThemeStyle({ themeProp, apiKey, baseUrl }),
1082
+ // 사이트 기본 표시 설정 — 실패해도 렌더는 진행 (안전 기본값).
1083
+ fetchBlogSettings({ apiKey, baseUrl }).catch(() => DEFAULT_BLOG_SETTINGS)
1084
+ ]);
1085
+ if (!post) {
1086
+ return notFoundElement ?? /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "post-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx8("p", { className: "rt-cms-empty", children: "\uAE00\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." }) });
1087
+ }
1088
+ const display = resolvePostDisplay(post, blogSettings, {
1089
+ showTableOfContents,
1090
+ tableOfContentsTitle
1091
+ });
1092
+ const toc = display.showTableOfContents ? extractToc(post.bodyJson) : [];
1093
+ const renderDoc = toc.length > 0 ? attachHeadingIds(post.bodyJson, toc) : post.bodyJson;
1094
+ let related = [];
1095
+ if (relatedPostsCount > 0) {
1096
+ try {
1097
+ const candidates = await fetchPosts({
1098
+ apiKey,
1099
+ baseUrl,
1100
+ type: "post",
1101
+ limit: 30
1102
+ });
1103
+ related = selectRelatedPosts(post, candidates.items, relatedPostsCount);
1104
+ } catch {
1105
+ }
1106
+ }
1107
+ const relatedHref = props.postHref ?? (collections ? makeCollectionPostHref(collections) : defaultPostHref);
1108
+ const authorName = post.authorName;
1109
+ const authorImageUrl = post.authorImageUrl;
1110
+ const postAuthorBio = post.authorBio;
1111
+ const views = typeof post.viewCount === "number" ? post.viewCount : null;
1112
+ const publishedDate = display.showDate ? formatPublishedDate2(post.publishedAt) : "";
1113
+ const showTopAuthor = display.showAuthor && Boolean(authorName);
1114
+ const displayAuthorName = authorName ?? display.authorProfileName;
1115
+ const authorBio = postAuthorBio ?? display.authorProfileBio ?? display.authorCardDescription;
1116
+ const authorImageUrlForCard = authorImageUrl ?? display.authorProfileImageUrl;
1117
+ const authorImageAlt = `${displayAuthorName ?? ""} \uD504\uB85C\uD544 \uC0AC\uC9C4`.trim();
1118
+ const authorImageStyle = {
1119
+ borderRadius: authorImageRadiusCss(display.authorProfileImageRadius),
1120
+ objectPosition: `${display.authorProfileImagePositionX}% ${display.authorProfileImagePositionY}%`
1121
+ };
1122
+ const showAuthorCard = display.showAuthorCard && Boolean(displayAuthorName);
1123
+ return /* @__PURE__ */ jsxs8(
1124
+ "article",
1125
+ {
1126
+ "data-roottale-cms": "post",
1127
+ className: "rt-cms-article",
1128
+ style: themeStyle ?? void 0,
1129
+ children: [
1130
+ /* @__PURE__ */ jsxs8("header", { className: "rt-cms-post-header", children: [
1131
+ publishedDate || showTopAuthor || views !== null ? /* @__PURE__ */ jsxs8("p", { className: "rt-cms-meta", children: [
1132
+ publishedDate ? /* @__PURE__ */ jsx8("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "date", children: publishedDate }) : null,
1133
+ showTopAuthor ? /* @__PURE__ */ jsxs8("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "author", children: [
1134
+ "\uC791\uC131\uC790 ",
1135
+ authorName
1136
+ ] }) : null,
1137
+ views !== null ? /* @__PURE__ */ jsxs8("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "views", children: [
1138
+ "\uC870\uD68C ",
1139
+ views.toLocaleString("ko-KR")
1140
+ ] }) : null
1141
+ ] }) : null,
1142
+ /* @__PURE__ */ jsx8("h1", { className: "rt-cms-title", children: post.title }),
1143
+ post.excerpt ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null
1144
+ ] }),
1145
+ renderArchiveRelated(post.archiveRelated, collections),
1146
+ (() => {
1147
+ const tocEl = toc.length > 0 ? /* @__PURE__ */ jsx8(RootTaleTableOfContents, { entries: toc, title: display.tocTitle }) : null;
1148
+ const bodyEl = /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: /* @__PURE__ */ jsx8(
1149
+ RenderTiptap,
1150
+ {
1151
+ doc: renderDoc,
1152
+ fields: post.fields,
1153
+ apiKey,
1154
+ baseUrl
1155
+ }
1156
+ ) });
1157
+ if (display.tocPosition === "sidebar" && tocEl) {
1158
+ return /* @__PURE__ */ jsxs8("div", { className: "rt-cms-layout rt-cms-layout--toc-side", children: [
1159
+ bodyEl,
1160
+ /* @__PURE__ */ jsx8("aside", { className: "rt-cms-toc-side", children: tocEl })
1161
+ ] });
1162
+ }
1163
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1164
+ tocEl,
1165
+ bodyEl
1166
+ ] });
1167
+ })(),
1168
+ showAuthorCard && displayAuthorName ? /* @__PURE__ */ jsxs8("section", { className: "rt-cms-author-card", "aria-label": "\uC791\uC131\uC790", children: [
1169
+ authorImageUrlForCard ? /* @__PURE__ */ jsx8(
1170
+ "img",
1171
+ {
1172
+ className: "rt-cms-author-card__image",
1173
+ src: authorImageUrlForCard,
1174
+ alt: authorImageAlt,
1175
+ loading: "lazy",
1176
+ style: authorImageStyle
1177
+ }
1178
+ ) : /* @__PURE__ */ jsx8("div", { className: "rt-cms-author-card__avatar", "aria-hidden": "true", children: authorInitials(displayAuthorName) }),
1179
+ /* @__PURE__ */ jsxs8("div", { className: "rt-cms-author-card__body", children: [
1180
+ /* @__PURE__ */ jsx8("p", { className: "rt-cms-author-card__eyebrow", children: display.authorCardEyebrow }),
1181
+ /* @__PURE__ */ jsx8("p", { className: "rt-cms-author-card__name", children: displayAuthorName }),
1182
+ authorBio ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-author-card__description", children: authorBio }) : null
1183
+ ] })
1184
+ ] }) : null,
1185
+ renderRelated(related, relatedHref, relatedPostsTitle),
1186
+ renderPostCta(blogSettings.postCta),
1187
+ renderFaqJsonLd(post.bodyJson)
1188
+ ]
1189
+ }
1190
+ );
1191
+ }
1192
+ function renderArchiveRelated(groups, collections) {
1193
+ const visible = groups.filter((group) => group.items.length > 0);
1194
+ if (visible.length === 0) return null;
1195
+ return /* @__PURE__ */ jsx8("aside", { className: "rt-archive-related", "aria-label": "\uC5F0\uAD00 \uC815\uBCF4", children: visible.map((group) => /* @__PURE__ */ jsxs8("section", { className: "rt-archive-related__group", children: [
1196
+ /* @__PURE__ */ jsx8("h2", { className: "rt-archive-related__title", children: archiveGroupTitle(group) }),
1197
+ /* @__PURE__ */ jsx8("ul", { className: "rt-archive-related__list", children: group.items.map((item) => /* @__PURE__ */ jsx8("li", { className: "rt-archive-related__item", children: /* @__PURE__ */ jsxs8(
1198
+ "a",
1199
+ {
1200
+ className: "rt-archive-related__link",
1201
+ href: archivePostHref(item.collectionKey, item.slug, collections),
1202
+ children: [
1203
+ /* @__PURE__ */ jsx8("span", { className: "rt-archive-related__item-title", children: item.title }),
1204
+ item.excerpt ? /* @__PURE__ */ jsx8("span", { className: "rt-archive-related__item-excerpt", children: item.excerpt }) : null
1205
+ ]
1206
+ }
1207
+ ) }, item.id)) })
1208
+ ] }, group.collectionKey)) });
1209
+ }
1210
+ function archiveGroupTitle(group) {
1211
+ if (group.collectionKey === "conditions") {
1212
+ return `\uC774 \uC99D\uC0C1\uACFC \uAD00\uB828\uB41C \uC9C8\uD658 ${group.items.length}`;
1213
+ }
1214
+ if (group.collectionKey === "treatments") {
1215
+ return `\uAD00\uB828 \uCE58\uB8CC\uBC95 ${group.items.length}`;
1216
+ }
1217
+ if (group.collectionKey === "symptoms") {
1218
+ return `\uAD00\uB828 \uC99D\uC0C1 ${group.items.length}`;
1219
+ }
1220
+ return `${group.label} ${group.items.length}`;
1221
+ }
1222
+ function archivePostHref(collectionKey, slug, collections) {
1223
+ const collection = collections?.find((item) => item.key === collectionKey);
1224
+ const basePath = collection?.basePath ?? defaultArchiveBasePath(collectionKey);
1225
+ return `${basePath.replace(/\/+$/, "")}/${slug}`;
1226
+ }
1227
+ function defaultArchiveBasePath(collectionKey) {
1228
+ if (collectionKey === "symptoms") return "/symptoms";
1229
+ if (collectionKey === "conditions") return "/conditions";
1230
+ if (collectionKey === "treatments") return "/treatments";
1231
+ return "/blog";
1232
+ }
1233
+ function renderPostCta(cta) {
1234
+ if (!cta) return null;
1235
+ const href = cta.buttonHref.trim();
1236
+ const safe = href.startsWith("/") || href.startsWith("#") || /^(https?:|mailto:|tel:)/i.test(href);
1237
+ if (!safe) return null;
1238
+ const external = /^https?:/i.test(href);
1239
+ return /* @__PURE__ */ jsxs8("aside", { "data-roottale-cms": "post-cta", className: "rt-cms-post-cta", children: [
1240
+ cta.title ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-post-cta__title", children: cta.title }) : null,
1241
+ cta.description ? /* @__PURE__ */ jsx8("p", { className: "rt-cms-post-cta__desc", children: cta.description }) : null,
1242
+ /* @__PURE__ */ jsx8(
1243
+ "a",
1244
+ {
1245
+ className: "rt-cms-post-cta__btn",
1246
+ href,
1247
+ target: external ? "_blank" : void 0,
1248
+ rel: external ? "noopener noreferrer" : void 0,
1249
+ children: cta.buttonLabel
1250
+ }
1251
+ )
1252
+ ] });
1253
+ }
1254
+ function renderRelated(posts, href, title) {
1255
+ if (posts.length === 0) return null;
1256
+ return /* @__PURE__ */ jsxs8("nav", { "data-roottale-cms": "related", className: "rt-cms-related", "aria-label": title, children: [
1257
+ /* @__PURE__ */ jsx8("h2", { className: "rt-cms-related-title", children: title }),
1258
+ /* @__PURE__ */ jsx8("ul", { className: "rt-cms-related-list", children: posts.map((p) => /* @__PURE__ */ jsx8("li", { className: "rt-cms-related-item", children: /* @__PURE__ */ jsxs8("a", { className: "rt-cms-related-link", href: href(p), children: [
1259
+ p.featuredImageUrl ? /* @__PURE__ */ jsx8(
1260
+ "img",
1261
+ {
1262
+ className: "rt-cms-related-thumb",
1263
+ src: p.featuredImageUrl,
1264
+ alt: "",
1265
+ loading: "lazy"
1266
+ }
1267
+ ) : null,
1268
+ /* @__PURE__ */ jsx8("span", { className: "rt-cms-related-post-title", children: p.title })
1269
+ ] }) }, p.id)) })
1270
+ ] });
1271
+ }
1272
+ function renderFaqJsonLd(doc) {
1273
+ const ld = faqPageJsonLd(extractFaqEntries(doc));
1274
+ if (!ld) return null;
1275
+ const json = JSON.stringify(ld).replace(/</g, "\\u003c");
1276
+ return /* @__PURE__ */ jsx8(
1277
+ "script",
1278
+ {
1279
+ type: "application/ld+json",
1280
+ dangerouslySetInnerHTML: { __html: json }
1281
+ }
1282
+ );
1283
+ }
1284
+ function RootTaleFaqJsonLd({
1285
+ doc
1286
+ }) {
1287
+ return renderFaqJsonLd(doc);
1288
+ }
1289
+ async function RootTalePage(props) {
1290
+ const {
1291
+ apiKey,
1292
+ slugOrId,
1293
+ baseUrl,
1294
+ notFoundElement,
1295
+ showTitle = true,
1296
+ theme: themeProp
1297
+ } = props;
1298
+ const [post, themeStyle] = await Promise.all([
1299
+ fetchPost({ apiKey, slugOrId, baseUrl }),
1300
+ resolveThemeStyle({ themeProp, apiKey, baseUrl })
1301
+ ]);
1302
+ if (!post) {
1303
+ return notFoundElement ?? /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "page-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx8("p", { className: "rt-cms-empty", children: "\uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." }) });
1304
+ }
1305
+ return /* @__PURE__ */ jsxs8(
1306
+ "article",
1307
+ {
1308
+ "data-roottale-cms": "page",
1309
+ className: "rt-cms-article",
1310
+ style: themeStyle ?? void 0,
1311
+ children: [
1312
+ showTitle ? /* @__PURE__ */ jsx8("header", { className: "rt-cms-post-header", children: /* @__PURE__ */ jsx8("h1", { className: "rt-cms-title", children: post.title }) }) : null,
1313
+ /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: /* @__PURE__ */ jsx8(
1314
+ RenderTiptap,
1315
+ {
1316
+ doc: post.bodyJson,
1317
+ fields: post.fields,
1318
+ apiKey,
1319
+ baseUrl
1320
+ }
1321
+ ) })
1322
+ ]
1323
+ }
1324
+ );
1325
+ }
1326
+ function authorInitials(name) {
1327
+ const trimmed = name.trim();
1328
+ if (!trimmed) return "";
1329
+ const parts = trimmed.split(/\s+/).filter(Boolean);
1330
+ if (parts.length >= 2) {
1331
+ return `${parts[0]?.[0] ?? ""}${parts[1]?.[0] ?? ""}`.toUpperCase();
1332
+ }
1333
+ return [...trimmed].slice(0, 2).join("").toUpperCase();
1334
+ }
1335
+ async function resolveThemeStyle(input) {
1336
+ const { themeProp, apiKey, baseUrl } = input;
1337
+ if (themeProp === null) return null;
1338
+ if (themeProp !== void 0) return themeToCssVars(themeProp);
1339
+ try {
1340
+ const fetched = await fetchTheme({ apiKey, baseUrl });
1341
+ return themeToCssVars(fetched);
1342
+ } catch {
1343
+ return null;
1344
+ }
1345
+ }
1346
+ function defaultPostHref(post) {
1347
+ return `/blog/${post.slug}`;
1348
+ }
1349
+ function makeCollectionPostHref(collections) {
1350
+ return (post) => resolvePostPath(post, collections) ?? defaultPostHref(post);
1351
+ }
1352
+ function formatPublishedDate2(iso) {
1353
+ const d = new Date(iso);
1354
+ if (Number.isNaN(d.getTime())) return "";
1355
+ return new Intl.DateTimeFormat("ko-KR", {
1356
+ year: "numeric",
1357
+ month: "2-digit",
1358
+ day: "2-digit"
1359
+ }).format(d);
1360
+ }
1361
+ function RenderTiptap({
1362
+ doc,
1363
+ fields,
1364
+ apiKey,
1365
+ baseUrl
1366
+ }) {
1367
+ const rawContent = doc.content;
1368
+ const content = Array.isArray(rawContent) ? rawContent : [];
1369
+ const hasToc = content.some((n) => n?.type === "tableOfContents");
1370
+ const docRecord = doc;
1371
+ const entries = hasToc ? extractToc(docRecord) : [];
1372
+ const renderDoc = hasToc && entries.length > 0 ? attachHeadingIds(docRecord, entries) : docRecord;
1373
+ const nodes = Array.isArray(renderDoc.content) ? renderDoc.content : content;
1374
+ const cms = apiKey ? { apiKey, baseUrl } : null;
1375
+ const context = {
1376
+ fields: fields ?? null,
1377
+ section: {
1378
+ cms,
1379
+ isDev: isDevEnv,
1380
+ renderBlogList: (props) => /* @__PURE__ */ jsx8(RootTaleBlogList, { ...props })
1381
+ }
1382
+ };
1383
+ return /* @__PURE__ */ jsx8(Fragment3, { children: nodes.map(
1384
+ (node, i) => node?.type === "tableOfContents" ? /* @__PURE__ */ jsx8(
1385
+ RootTaleTableOfContents,
1386
+ {
1387
+ entries,
1388
+ title: typeof node.attrs?.title === "string" ? node.attrs.title : "\uBAA9\uCC28"
1389
+ },
1390
+ i
1391
+ ) : renderNode(node, i, context)
1392
+ ) });
1393
+ }
1394
+ function boundContentText(node, fields) {
1395
+ return bindingTextValue(
1396
+ boundFieldValue(node.attrs?.bindings, "content", fields)
1397
+ );
1398
+ }
1399
+ function isSafeHref(value) {
1400
+ if (typeof value !== "string") return false;
1401
+ const v = value.trim();
1402
+ if (v.length === 0) return false;
1403
+ if (v.startsWith("/") || v.startsWith("#") || v.startsWith("?")) return true;
1404
+ return /^(https?:|mailto:|tel:)/i.test(v);
1405
+ }
1406
+ function alignStyle(node) {
1407
+ const align = node.attrs?.textAlign;
1408
+ if (typeof align !== "string" || align === "" || align === "left") return void 0;
1409
+ if (align === "center" || align === "right" || align === "justify") {
1410
+ return { textAlign: align };
1411
+ }
1412
+ return void 0;
1413
+ }
1414
+ function tableCellSpans(node) {
1415
+ const colspan = Number(node.attrs?.colspan);
1416
+ const rowspan = Number(node.attrs?.rowspan);
1417
+ return {
1418
+ colSpan: Number.isFinite(colspan) && colspan > 1 ? colspan : void 0,
1419
+ rowSpan: Number.isFinite(rowspan) && rowspan > 1 ? rowspan : void 0
1420
+ };
1421
+ }
1422
+ function isDevEnv() {
1423
+ const env = globalThis.process?.env ?? {};
1424
+ return env.NODE_ENV !== "production";
1425
+ }
1426
+ function renderNode(node, key, context = {
1427
+ fields: null,
1428
+ section: {
1429
+ cms: null,
1430
+ isDev: isDevEnv,
1431
+ renderBlogList: (props) => /* @__PURE__ */ jsx8(RootTaleBlogList, { ...props })
1432
+ }
1433
+ }) {
1434
+ if (!node || typeof node !== "object") return null;
1435
+ const children = Array.isArray(node.content) ? node.content.map((child, i) => renderNode(child, i, context)) : void 0;
1436
+ switch (node.type) {
1437
+ case "paragraph": {
1438
+ const bound = boundContentText(node, context.fields);
1439
+ return /* @__PURE__ */ jsx8("p", { style: alignStyle(node), children: bound ?? children }, key);
1440
+ }
1441
+ case "heading": {
1442
+ const rawLevel = Number(node.attrs?.level ?? 2);
1443
+ const level = Math.min(Math.max(Number.isFinite(rawLevel) ? rawLevel : 2, 2), 4);
1444
+ const idAttr = node.attrs?.id;
1445
+ const id = typeof idAttr === "string" && idAttr.length > 0 ? idAttr : void 0;
1446
+ const style = alignStyle(node);
1447
+ const inner = boundContentText(node, context.fields) ?? children;
1448
+ if (level === 2) return /* @__PURE__ */ jsx8("h2", { id, style, children: inner }, key);
1449
+ if (level === 3) return /* @__PURE__ */ jsx8("h3", { id, style, children: inner }, key);
1450
+ return /* @__PURE__ */ jsx8("h4", { id, style, children: inner }, key);
1451
+ }
1452
+ case "bulletList":
1453
+ return /* @__PURE__ */ jsx8("ul", { children }, key);
1454
+ case "orderedList":
1455
+ return /* @__PURE__ */ jsx8("ol", { children }, key);
1456
+ case "listItem":
1457
+ return /* @__PURE__ */ jsx8("li", { children }, key);
1458
+ case "blockquote":
1459
+ return /* @__PURE__ */ jsx8("blockquote", { children }, key);
1460
+ case "codeBlock":
1461
+ return /* @__PURE__ */ jsx8("pre", { children: /* @__PURE__ */ jsx8("code", { children }) }, key);
1462
+ case "horizontalRule":
1463
+ return /* @__PURE__ */ jsx8("hr", {}, key);
1464
+ case "hardBreak":
1465
+ return /* @__PURE__ */ jsx8("br", {}, key);
1466
+ case "image": {
1467
+ const boundSrc = bindingSrcValue(
1468
+ boundFieldValue(node.attrs?.bindings, "src", context.fields)
1469
+ );
1470
+ const src = boundSrc ?? node.attrs?.src;
1471
+ if (typeof src !== "string" || src.length === 0) return null;
1472
+ const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
1473
+ const title = typeof node.attrs?.title === "string" ? node.attrs.title : void 0;
1474
+ const width = typeof node.attrs?.width === "number" && node.attrs.width > 0 ? node.attrs.width : void 0;
1475
+ const height = typeof node.attrs?.height === "number" && node.attrs.height > 0 ? node.attrs.height : void 0;
1476
+ return /* @__PURE__ */ jsx8(
1477
+ "img",
1478
+ {
1479
+ src,
1480
+ alt,
1481
+ title,
1482
+ width,
1483
+ height,
1484
+ loading: "lazy"
1485
+ },
1486
+ key
1487
+ );
1488
+ }
1489
+ case "columns":
1490
+ return /* @__PURE__ */ jsx8("div", { className: "rt-columns", children }, key);
1491
+ case "column":
1492
+ return /* @__PURE__ */ jsx8("div", { className: "rt-column", children }, key);
1493
+ case "table":
1494
+ return /* @__PURE__ */ jsx8("div", { className: "rt-table-wrap", children: /* @__PURE__ */ jsx8("table", { className: "rt-table", children: /* @__PURE__ */ jsx8("tbody", { children }) }) }, key);
1495
+ case "tableRow":
1496
+ return /* @__PURE__ */ jsx8("tr", { children }, key);
1497
+ case "tableHeader": {
1498
+ const { colSpan, rowSpan } = tableCellSpans(node);
1499
+ return /* @__PURE__ */ jsx8("th", { colSpan, rowSpan, children }, key);
1500
+ }
1501
+ case "tableCell": {
1502
+ const { colSpan, rowSpan } = tableCellSpans(node);
1503
+ return /* @__PURE__ */ jsx8("td", { colSpan, rowSpan, children }, key);
1504
+ }
1505
+ case "embed":
1506
+ return renderEmbed(node, key);
1507
+ case "faq":
1508
+ return /* @__PURE__ */ jsx8("div", { "data-roottale-cms": "faq", className: "rt-cms-faq", children }, key);
1509
+ case "faqItem":
1510
+ return /* @__PURE__ */ jsx8("details", { className: "rt-cms-faq-item", children }, key);
1511
+ case "faqQuestion":
1512
+ return /* @__PURE__ */ jsx8("summary", { className: "rt-cms-faq-q", children }, key);
1513
+ case "faqAnswer":
1514
+ return /* @__PURE__ */ jsx8("div", { className: "rt-cms-faq-a", children }, key);
1515
+ case "tableOfContents":
1516
+ return null;
1517
+ case "section":
1518
+ return renderSectionNode(node, key, context.section);
1519
+ case "text":
1520
+ return renderText(node, key);
1521
+ default:
1522
+ return children ? /* @__PURE__ */ jsx8("span", { children }, key) : null;
1523
+ }
1524
+ }
1525
+ function renderEmbed(node, key) {
1526
+ const src = node.attrs?.src;
1527
+ if (isAllowedEmbedSrc2(src)) {
1528
+ const provider = typeof node.attrs?.provider === "string" ? node.attrs.provider : void 0;
1529
+ return /* @__PURE__ */ jsx8(
1530
+ "div",
1531
+ {
1532
+ className: "rt-embed",
1533
+ "data-provider": provider,
1534
+ style: { aspectRatio: "16 / 9" },
1535
+ children: /* @__PURE__ */ jsx8(
1536
+ "iframe",
1537
+ {
1538
+ src,
1539
+ title: "\uC784\uBCA0\uB4DC\uB41C \uC601\uC0C1",
1540
+ loading: "lazy",
1541
+ allowFullScreen: true,
1542
+ referrerPolicy: "strict-origin-when-cross-origin",
1543
+ allow: "accelerometer; clipboard-write; encrypted-media; picture-in-picture; web-share",
1544
+ style: { width: "100%", height: "100%", border: 0 }
1545
+ }
1546
+ )
1547
+ },
1548
+ key
1549
+ );
1550
+ }
1551
+ const orig = node.attrs?.originalUrl;
1552
+ if (isSafeHref(orig)) {
1553
+ return /* @__PURE__ */ jsx8("p", { className: "rt-embed-fallback", children: /* @__PURE__ */ jsx8("a", { href: orig, rel: "noopener noreferrer", target: "_blank", children: orig }) }, key);
1554
+ }
1555
+ return null;
1556
+ }
1557
+ function renderText(node, key) {
1558
+ const text = node.text ?? "";
1559
+ const marks = node.marks ?? [];
1560
+ if (marks.length === 0) return text;
1561
+ let element = text;
1562
+ for (const mark of marks) {
1563
+ element = applyMark(mark, element);
1564
+ }
1565
+ return /* @__PURE__ */ jsx8("span", { children: element }, key);
1566
+ }
1567
+ function applyMark(mark, child) {
1568
+ switch (mark.type) {
1569
+ case "bold":
1570
+ return /* @__PURE__ */ jsx8("strong", { children: child });
1571
+ case "italic":
1572
+ return /* @__PURE__ */ jsx8("em", { children: child });
1573
+ case "underline":
1574
+ return /* @__PURE__ */ jsx8("u", { children: child });
1575
+ case "strike":
1576
+ return /* @__PURE__ */ jsx8("s", { children: child });
1577
+ case "code":
1578
+ return /* @__PURE__ */ jsx8("code", { children: child });
1579
+ case "highlight": {
1580
+ const color = mark.attrs?.color;
1581
+ const style = typeof color === "string" && color.length > 0 ? { background: color } : void 0;
1582
+ return /* @__PURE__ */ jsx8("mark", { style, children: child });
1583
+ }
1584
+ case "textStyle": {
1585
+ const color = mark.attrs?.color;
1586
+ const style = typeof color === "string" && color.length > 0 ? { color } : void 0;
1587
+ if (!style) return /* @__PURE__ */ jsx8(Fragment3, { children: child });
1588
+ return /* @__PURE__ */ jsx8("span", { style, children: child });
1589
+ }
1590
+ case "link": {
1591
+ const raw = mark.attrs?.href;
1592
+ const href = isSafeHref(raw) ? raw : "#";
1593
+ return /* @__PURE__ */ jsx8("a", { href, rel: "noopener noreferrer", target: "_blank", children: child });
1594
+ }
1595
+ default:
1596
+ return /* @__PURE__ */ jsx8("span", { children: child });
1597
+ }
1598
+ }
1599
+
1600
+ export {
1601
+ RootTaleLeadForm,
1602
+ sectionThemeStyle,
1603
+ TemplateHero,
1604
+ TemplateRichText,
1605
+ TemplateServices,
1606
+ TemplateFaq,
1607
+ TemplateCtaBanner,
1608
+ TemplateTrust,
1609
+ TemplateContact,
1610
+ RootTalePostCard,
1611
+ RootTaleTableOfContents,
1612
+ themeToCssVars,
1613
+ headingToId,
1614
+ extractToc,
1615
+ attachHeadingIds,
1616
+ RootTaleFloatingCta,
1617
+ renderBlock,
1618
+ renderBlocks,
1619
+ renderBoundBlocks,
1620
+ RootTaleThemeProvider,
1621
+ RootTaleBlogList,
1622
+ RootTaleBlogCategories,
1623
+ RootTaleBlogPost,
1624
+ RootTaleFaqJsonLd,
1625
+ RootTalePage,
1626
+ RenderTiptap
1627
+ };
1628
+ //# sourceMappingURL=chunk-6AFM2UR5.js.map