@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.
- package/CHANGELOG.md +70 -0
- package/dist/chunk-6AFM2UR5.js +1628 -0
- package/dist/chunk-6AFM2UR5.js.map +1 -0
- package/dist/chunk-IOYBEH6E.js +271 -0
- package/dist/chunk-IOYBEH6E.js.map +1 -0
- package/dist/cms-public.css +243 -2
- package/dist/routes.d.ts +13 -2
- package/dist/routes.js +12 -1
- package/dist/routes.js.map +1 -1
- package/dist/server.d.ts +21 -2
- package/dist/server.js +8 -3
- package/dist/templates.css +238 -7
- package/dist/templates.d.ts +414 -94
- package/dist/templates.js +63 -347
- package/dist/templates.js.map +1 -1
- package/dist/theme-css-vars-BiyHBsJG.d.ts +7 -0
- package/dist/validate.d.ts +90 -0
- package/dist/validate.js +415 -0
- package/dist/validate.js.map +1 -0
- package/package.json +10 -4
- package/dist/chunk-3JUIHMPP.js +0 -1057
- package/dist/chunk-3JUIHMPP.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/templates/site-config.ts"],"sourcesContent":["/**\n * `@roottale/cms-renderer-next/templates` — canonical `SiteConfig` 스키마.\n *\n * ADR-0040 P2 + P0.5 RFC §① (`roottale-docs/docs/services/external-site-blockification.md`).\n *\n * 설계 원칙 (codex consult #1 반영):\n * - **계약 = `section type + variant + renderer registry`.** tier 는 스키마 필드가\n * 아니라 문서/거버넌스용 성숙도 메타데이터.\n * - **100% serializable** — 함수/JSX 금지 (Astro parity + admin preview 동일 경로).\n * escape hatch 는 config 안 함수가 아니라 `customSections` registry\n * (`SiteRenderer` 에 빌드 시 type→컴포넌트 주입).\n * - CMS-bound 섹션(`blog-list`/`lead-form`)은 `RootTaleBlogList`/`RootTaleLeadForm`\n * 으로 위임 — templates 는 cms-renderer 코어를 단방향 의존 (RFC §②).\n *\n * RFC §① 대비 구현 정정:\n * - `site.url` 은 optional — 스타터/외부 사이트가 `SITE_URL` env 로 런타임 해석하는\n * 기존 패턴(P4 스타터)과 충돌하므로 config 강제 대신 env fallback 허용.\n * - 빌트인에 `rich-text` 추가 (P4 스타터 기 운영 섹션 — \"동일 패턴\" 군).\n * allow: SIZE_OK — canonical SectionSchema registry이며 W1-2 계약이 이 파일 수정을 요구한다.\n */\nimport { z } from \"zod\";\n\nconst JsonValue: z.ZodType<unknown> = z.lazy(() =>\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.array(JsonValue),\n z.record(JsonValue),\n ]),\n);\n\nexport const NavItemSchema = z.object({\n label: z.string(),\n href: z.string(),\n children: z\n .array(z.object({ label: z.string(), href: z.string() }))\n .optional(),\n});\nexport type NavItem = z.infer<typeof NavItemSchema>;\n\nconst CtaSchema = z.object({ label: z.string(), href: z.string() });\nexport type SectionCta = z.infer<typeof CtaSchema>;\n\n/**\n * 모든 섹션 공통 필드. `theme` 는 섹션 스코프 CSS 변수 override —\n * key 는 `--rt-*` custom property 이름, 값은 CSS 값.\n * 예) `{ \"--rt-color-primary\": \"#0B1F49\" }`\n */\nconst SectionBase = z.object({\n /** 안정적 anchor·dedup·preview 매핑용. 페이지 내 유일. */\n id: z.string().optional(),\n /** 빌트인 또는 customSections registry 키. kebab-case. */\n type: z.string(),\n /** 같은 type 의 비주얼 변형. 빌트인 type 은 enum 으로 좁힘. */\n variant: z.string().optional(),\n /** 섹션 스코프 토큰 override (`--rt-*` CSS 변수 → 값). */\n theme: z.record(z.string()).optional(),\n /** 섹션별 직렬화 props. */\n props: z.record(JsonValue).default({}),\n});\n\n// ── 빌트인 섹션 (props 좁힘 — 런타임 + 타입) ────────────────────────────────\n\nexport const HeroSectionSchema = SectionBase.extend({\n type: z.literal(\"hero\"),\n variant: z.enum([\"split\", \"image\", \"minimal\"]).default(\"split\"),\n props: z.object({\n eyebrow: z.string().optional(),\n /** `\\n` 으로 줄바꿈. */\n title: z.string(),\n description: z.string().optional(),\n imageSrc: z.string().optional(),\n imageAlt: z.string().optional(),\n cta: CtaSchema.optional(),\n }),\n});\n\nexport const RichTextSectionSchema = SectionBase.extend({\n type: z.literal(\"rich-text\"),\n props: z.object({\n heading: z.string().optional(),\n /** `\\n\\n` 으로 문단 분리. */\n body: z.string(),\n }),\n});\n\nexport const ServicesSectionSchema = SectionBase.extend({\n type: z.literal(\"services\"),\n variant: z.enum([\"grid\", \"list\"]).default(\"grid\"),\n props: z.object({\n heading: z.string().optional(),\n description: z.string().optional(),\n items: z\n .array(\n z.object({\n title: z.string(),\n description: z.string().optional(),\n href: z.string().optional(),\n }),\n )\n .min(1)\n .max(12),\n }),\n});\n\nexport const FaqSectionSchema = SectionBase.extend({\n type: z.literal(\"faq\"),\n props: z.object({\n heading: z.string().optional(),\n items: z\n .array(z.object({ question: z.string(), answer: z.string() }))\n .min(1)\n .max(20),\n /** FAQPage JSON-LD 주입 (기본 true — SEO 3 pillars). 페이지에 FAQ 섹션이 여럿이면 하나만 true 로. */\n jsonLd: z.boolean().default(true),\n }),\n});\n\nexport const CtaBannerSectionSchema = SectionBase.extend({\n type: z.literal(\"cta-banner\"),\n props: z.object({\n title: z.string(),\n description: z.string().optional(),\n cta: CtaSchema.optional(),\n }),\n});\n\n/** → `RootTaleBlogList` 위임 (cms-bound). */\nexport const BlogListSectionSchema = SectionBase.extend({\n type: z.literal(\"blog-list\"),\n props: z.object({\n heading: z.string().optional(),\n limit: z.number().int().positive().optional(),\n /** 포스트 링크 베이스. 기본 `/blog`. */\n blogBasePath: z.string().optional(),\n emptyMessage: z.string().optional(),\n }),\n});\n\n/** → `RootTaleLeadForm` 위임 (cms-bound). */\nexport const LeadFormSectionSchema = SectionBase.extend({\n type: z.literal(\"lead-form\"),\n props: z.object({\n /** 사이트 자체 server 엔드포인트 (예: `/api/inquiry`). intake API 직접 지정 금지. */\n action: z.string(),\n vertical: z.enum([\"consulting\", \"medical\", \"tax\", \"legal\"]).optional(),\n heading: z.string().optional(),\n description: z.string().optional(),\n submitLabel: z.string().optional(),\n }),\n});\n\nconst TrustMetricSchema = z.object({\n value: z.string(),\n unit: z.string().optional(),\n label: z.string(),\n source: z.string().optional(),\n});\n\nconst TrustCredentialSchema = z.object({\n name: z.string(),\n issuer: z.string().optional(),\n kind: z.enum([\"license\", \"education\", \"association\"]).optional(),\n image: z.string().optional(),\n});\n\nconst TrustLogoSchema = z.object({\n src: z.string(),\n alt: z.string(),\n href: z.string().optional(),\n});\n\nexport const TrustSectionSchema = SectionBase.extend({\n type: z.literal(\"trust\"),\n variant: z\n .enum([\"stat-band\", \"logo-cloud\", \"feature-grid\"])\n .default(\"stat-band\"),\n props: z.object({\n heading: z.string().optional(),\n metrics: z.array(TrustMetricSchema).min(2).max(4).optional(),\n credentials: z.array(TrustCredentialSchema).max(8).optional(),\n logos: z.array(TrustLogoSchema).max(8).optional(),\n disclaimer: z.string().optional(),\n }),\n}).superRefine((section, ctx) => {\n if (section.variant === \"stat-band\" && !section.props.metrics) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"metrics\"],\n message: \"stat-band trust sections require 2-4 metrics\",\n });\n }\n if (section.variant === \"logo-cloud\" && !section.props.logos) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"logos\"],\n message: \"logo-cloud trust sections require logos\",\n });\n }\n if (section.variant === \"feature-grid\" && !section.props.credentials) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"props\", \"credentials\"],\n message: \"feature-grid trust sections require credentials\",\n });\n }\n});\n\nconst ContactHoursSchema = z.object({\n day: z.string(),\n open: z.string(),\n close: z.string(),\n note: z.string().optional(),\n});\n\nexport const ContactSectionSchema = SectionBase.extend({\n type: z.literal(\"contact\"),\n variant: z.enum([\"form-map-5050\", \"split\"]).default(\"form-map-5050\"),\n props: z.object({\n heading: z.string().optional(),\n form: z.object({\n enabled: z.boolean(),\n vertical: z.enum([\"medical\", \"tax\", \"legal\", \"consulting\"]).optional(),\n }),\n phone: z.string().optional(),\n address: z.string().optional(),\n hours: z.array(ContactHoursSchema).max(8).optional(),\n mapEmbedUrl: z.string().optional(),\n }),\n});\n\n/** customSections 용 fallback — `type` 자유, props 는 직렬화만 강제. */\nexport const CustomSectionSchema = SectionBase;\n\nexport const BUILTIN_SECTION_SCHEMAS: Record<string, z.ZodTypeAny> = {\n hero: HeroSectionSchema,\n \"rich-text\": RichTextSectionSchema,\n services: ServicesSectionSchema,\n faq: FaqSectionSchema,\n \"cta-banner\": CtaBannerSectionSchema,\n \"blog-list\": BlogListSectionSchema,\n \"lead-form\": LeadFormSectionSchema,\n trust: TrustSectionSchema,\n contact: ContactSectionSchema,\n};\n\n/** 빌트인 type 목록 (renderer registry 와 1:1). */\nexport const BUILTIN_SECTION_TYPES = Object.keys(\n BUILTIN_SECTION_SCHEMAS,\n) as readonly string[];\n\nexport type HeroSection = z.output<typeof HeroSectionSchema>;\nexport type RichTextSection = z.output<typeof RichTextSectionSchema>;\nexport type ServicesSection = z.output<typeof ServicesSectionSchema>;\nexport type FaqSection = z.output<typeof FaqSectionSchema>;\nexport type CtaBannerSection = z.output<typeof CtaBannerSectionSchema>;\nexport type BlogListSection = z.output<typeof BlogListSectionSchema>;\nexport type LeadFormSection = z.output<typeof LeadFormSectionSchema>;\nexport type TrustSection = z.output<typeof TrustSectionSchema>;\nexport type ContactSection = z.output<typeof ContactSectionSchema>;\nexport type CustomSection = z.output<typeof CustomSectionSchema>;\n\n/** 파싱 결과 섹션 (defaults 적용 후). */\nexport type Section =\n | HeroSection\n | RichTextSection\n | ServicesSection\n | FaqSection\n | CtaBannerSection\n | BlogListSection\n | LeadFormSection\n | TrustSection\n | ContactSection\n | CustomSection;\n\n/** config 작성용 섹션 (defaults 생략 가능). `site.config.ts` 에서 이 타입 사용. */\nexport type SectionInput =\n | z.input<typeof HeroSectionSchema>\n | z.input<typeof RichTextSectionSchema>\n | z.input<typeof ServicesSectionSchema>\n | z.input<typeof FaqSectionSchema>\n | z.input<typeof CtaBannerSectionSchema>\n | z.input<typeof BlogListSectionSchema>\n | z.input<typeof LeadFormSectionSchema>\n | z.input<typeof TrustSectionSchema>\n | z.input<typeof ContactSectionSchema>\n | z.input<typeof CustomSectionSchema>;\n\n/**\n * 섹션 스키마 — 빌트인 type 이면 해당 스키마로 *강제* 검증 (union fallthrough 로\n * 빌트인 오타이핑이 CustomSection 으로 조용히 통과하는 것을 차단), 미지 type 은\n * CustomSection 으로 통과 (렌더러가 customSections registry 로 해석).\n */\nexport const SectionSchema: z.ZodType<Section, z.ZodTypeDef, SectionInput> = z\n .object({ type: z.string() })\n .passthrough()\n .transform((section, ctx) => {\n const schema = BUILTIN_SECTION_SCHEMAS[section.type] ?? CustomSectionSchema;\n const result = schema.safeParse(section);\n if (!result.success) {\n for (const issue of result.error.issues) ctx.addIssue(issue);\n return z.NEVER;\n }\n return result.data as Section;\n }) as unknown as z.ZodType<Section, z.ZodTypeDef, SectionInput>;\n\n/**\n * 콘텐츠 Collection (ADR-0060) — 같은 글 풀(post)을 스트림으로 분리(공지 게시판 /\n * 블로그 그리드 등). `routes.ts` 의 `RouteCollection` 의 타입드·직렬화 상위집합 —\n * 한 category slug 는 정확히 하나의 collection 에 속해야 한다(다대일 = 라우팅 모호).\n * `layout` 은 레지스트리 키(open string) — 신규 레이아웃 추가 시 스키마 불변.\n * 선언 순서 = 라우팅 우선순위(`resolvePostCollection`).\n */\nexport const CollectionSchema = z.object({\n /** 안정 식별자 (라우트/가드/파생 키). */\n key: z.string(),\n /** 작성자 카드·nav 라벨. */\n label: z.string(),\n /** 라우트 base path (예: \"/notice\" | \"/blog\"). 앞 슬래시 포함. */\n basePath: z.string(),\n /** 레이아웃 레지스트리 키 (board=게시판, grid=카드 그리드, …). 미지 키는 렌더러 fallback. */\n layout: z.string().default(\"grid\"),\n /** 이 스트림에 속하는 category slug 들. */\n categories: z.array(z.string()).default([]),\n /** RSS 피드 포함. */\n feed: z.boolean().default(false),\n /** `${basePath}/categories/{slug}` 아카이브 생성. */\n archives: z.boolean().default(false),\n /** 글별 동적 OG. */\n og: z.boolean().default(false),\n /** 작성 UX: 하위 카테고리 1개 필수(미선택 발행 차단). */\n requireSubcategory: z.boolean().default(false),\n /** 작성 UX: 상단고정 토글 노출. */\n pinning: z.boolean().default(false),\n /** nav/카드 아이콘 키 (선택). */\n icon: z.string().optional(),\n});\nexport type Collection = z.output<typeof CollectionSchema>;\n\nexport const SiteConfigSchema = z.object({\n site: z.object({\n /** canonical origin. 생략 시 소비측이 `SITE_URL` env 등으로 해석. */\n url: z.string().url().optional(),\n title: z.string(),\n description: z.string().optional(),\n locale: z.string().default(\"ko-KR\"),\n og: z.object({ image: z.string().optional() }).optional(),\n }),\n /**\n * 콘텐츠 스트림 분리 (ADR-0060). 비우면 단일 /blog(레거시 동작). 선언 순서 =\n * 라우팅 우선순위. `routes.ts` 팩토리에 `collections` 로 전달해 sitemap/feed/\n * 아카이브를 스트림별로 파생.\n */\n collections: z.array(CollectionSchema).default([]),\n /** 사이트 전역 토큰 override (`--rt-*` CSS 변수 → 값). 비우면 admin /design SSoT. */\n theme: z.record(z.string()).default({}),\n nav: z.array(NavItemSchema).default([]),\n /** path(\"/\", \"/services\", …) → 섹션 배열. */\n pages: z.record(z.array(SectionSchema)),\n});\n\nexport type SiteConfig = z.output<typeof SiteConfigSchema>;\n/** config 작성용 (defaults 생략 가능). */\nexport type SiteConfigInput = Omit<\n z.input<typeof SiteConfigSchema>,\n \"pages\"\n> & { pages: Record<string, SectionInput[]> };\n\n/** config 검증 + defaults 적용. 실패 시 ZodError throw (빌드 타임 fail-fast 용). */\nexport function parseSiteConfig(input: unknown): SiteConfig {\n return SiteConfigSchema.parse(input);\n}\n\n/**\n * `site.config.ts` 작성 헬퍼 — 타입 추론 + 즉시 검증.\n *\n * ```ts\n * import { defineSiteConfig } from \"@roottale/cms-renderer-next/templates\";\n * export default defineSiteConfig({ site: { title: \"…\" }, pages: { \"/\": [ … ] } });\n * ```\n */\nexport function defineSiteConfig(input: SiteConfigInput): SiteConfig {\n return parseSiteConfig(input);\n}\n"],"mappings":"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","names":[]}
|
package/dist/cms-public.css
CHANGED
|
@@ -305,12 +305,54 @@
|
|
|
305
305
|
color: var(--rt-color-foreground, #06162f);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
/* 목차 사이드바 레이아웃의 컨테이너 기준 — 뷰포트가 아닌 "본문 컬럼 폭" 으로
|
|
309
|
+
사이드바 전환을 판단(임베드 위치·주변 레이아웃과 무관하게 안전). 사이드바를
|
|
310
|
+
실제 쓰는 글에만 컨테이너 지정(:has) → 인라인 모드 글엔 containment 영향 0. */
|
|
311
|
+
[data-roottale-cms] :where(.rt-cms-article:has(.rt-cms-layout--toc-side)) {
|
|
312
|
+
container: rt-cms-article / inline-size;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* 목차 사이드바 (opt-in, tocPosition="sidebar") — 넓은 화면에서만 본문 오른쪽
|
|
316
|
+
sticky 목차, 좁으면 단일 컬럼 + 목차가 본문 위로. */
|
|
317
|
+
[data-roottale-cms] :where(.rt-cms-layout--toc-side) {
|
|
318
|
+
display: grid;
|
|
319
|
+
gap: var(--rt-spacing-6, 1.5rem);
|
|
320
|
+
}
|
|
321
|
+
[data-roottale-cms] :where(.rt-cms-layout--toc-side .rt-cms-toc-side) {
|
|
322
|
+
min-inline-size: 0;
|
|
323
|
+
order: -1; /* 좁은 화면: 목차를 본문 위로 */
|
|
324
|
+
}
|
|
325
|
+
@container rt-cms-article (min-width: 60rem) {
|
|
326
|
+
[data-roottale-cms] :where(.rt-cms-layout--toc-side) {
|
|
327
|
+
grid-template-columns: minmax(0, 1fr) 15rem;
|
|
328
|
+
align-items: start;
|
|
329
|
+
}
|
|
330
|
+
[data-roottale-cms] :where(.rt-cms-layout--toc-side .rt-cms-toc-side) {
|
|
331
|
+
order: 0; /* 넓은 화면: 오른쪽 컬럼 */
|
|
332
|
+
position: sticky;
|
|
333
|
+
top: var(--rt-spacing-4, 1rem);
|
|
334
|
+
max-block-size: calc(100vh - 2rem);
|
|
335
|
+
overflow-y: auto;
|
|
336
|
+
}
|
|
337
|
+
/* 사이드바 목차는 컬럼 폭(15rem)에 맞춤 — 본문 measure cap 무시. */
|
|
338
|
+
[data-roottale-cms] :where(.rt-cms-layout--toc-side .rt-cms-toc) {
|
|
339
|
+
max-inline-size: none;
|
|
340
|
+
margin-block-end: 0;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
[data-roottale-cms] :where(.rt-cms-body),
|
|
345
|
+
[data-roottale-cms] :where([data-roottale-cms="body"]) {
|
|
309
346
|
min-inline-size: 0;
|
|
310
347
|
font-family: var(--rt-font-body, system-ui, sans-serif);
|
|
311
348
|
font-size: var(--rt-font-size-base, 1rem);
|
|
312
349
|
line-height: 1.7;
|
|
313
350
|
color: var(--rt-color-foreground, #06162f);
|
|
351
|
+
/* 한글 조판 — 어절 단위 줄바꿈. 긴 URL·토큰만 anywhere 로 넘침 방지.
|
|
352
|
+
에디터(admin globals .tiptap-content)와 1:1 파리티. astro 는 rt-cms-body
|
|
353
|
+
class 없이 data-roottale-cms="body" 만 쓰므로 attribute 셀렉터도 함께. */
|
|
354
|
+
word-break: keep-all;
|
|
355
|
+
overflow-wrap: anywhere;
|
|
314
356
|
}
|
|
315
357
|
|
|
316
358
|
[data-roottale-cms] :where(.rt-cms-post-header) {
|
|
@@ -351,8 +393,84 @@
|
|
|
351
393
|
margin-block-start: 0.65em;
|
|
352
394
|
}
|
|
353
395
|
|
|
354
|
-
|
|
396
|
+
/* 읽기 measure — 본문의 모든 최상위 블록(문단·리스트·제목·표·FAQ)을 같은 폭으로
|
|
397
|
+
줄바꿈. 어드민 에디터(.tiptap-content > *)와 1:1 파리티. */
|
|
398
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] > *) {
|
|
355
399
|
max-inline-size: 72ch;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
[data-roottale-cms] :where(.rt-archive-related) {
|
|
403
|
+
display: grid;
|
|
404
|
+
gap: var(--rt-spacing-4, 1rem);
|
|
405
|
+
max-inline-size: 72ch;
|
|
406
|
+
margin-block: var(--rt-spacing-6, 1.5rem) var(--rt-spacing-7, 2rem);
|
|
407
|
+
padding: var(--rt-spacing-4, 1rem);
|
|
408
|
+
border: 1px solid var(--rt-color-border, currentColor);
|
|
409
|
+
border-radius: var(--rt-radius-md, 0.5rem);
|
|
410
|
+
background: color-mix(in srgb, var(--rt-color-muted, transparent) 58%, transparent);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
[data-roottale-cms] :where(.rt-archive-related__group) {
|
|
414
|
+
display: grid;
|
|
415
|
+
gap: var(--rt-spacing-3, 0.75rem);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
[data-roottale-cms] :where(.rt-archive-related__title) {
|
|
419
|
+
margin: 0;
|
|
420
|
+
color: var(--rt-color-foreground, CanvasText);
|
|
421
|
+
font-family: var(--rt-font-body, system-ui, sans-serif);
|
|
422
|
+
font-size: var(--rt-font-size-sm, 0.875rem);
|
|
423
|
+
font-weight: var(--rt-font-weight-bold, 700);
|
|
424
|
+
line-height: 1.35;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
[data-roottale-cms] :where(.rt-archive-related__list) {
|
|
428
|
+
display: grid;
|
|
429
|
+
gap: var(--rt-spacing-2, 0.5rem);
|
|
430
|
+
padding: 0;
|
|
431
|
+
margin: 0;
|
|
432
|
+
list-style: none;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
[data-roottale-cms] :where(.rt-archive-related__link) {
|
|
436
|
+
display: grid;
|
|
437
|
+
gap: 0.125rem;
|
|
438
|
+
padding: var(--rt-spacing-3, 0.75rem);
|
|
439
|
+
border: 1px solid color-mix(in srgb, var(--rt-color-border, currentColor) 70%, transparent);
|
|
440
|
+
border-radius: var(--rt-radius-sm, 0.25rem);
|
|
441
|
+
background: color-mix(in srgb, var(--rt-color-background, Canvas) 82%, transparent);
|
|
442
|
+
color: inherit;
|
|
443
|
+
text-decoration: none;
|
|
444
|
+
transition:
|
|
445
|
+
border-color 160ms ease,
|
|
446
|
+
background-color 160ms ease;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
[data-roottale-cms] :where(.rt-archive-related__link:hover),
|
|
450
|
+
[data-roottale-cms] :where(.rt-archive-related__link:focus-visible) {
|
|
451
|
+
border-color: color-mix(in srgb, var(--rt-color-primary, currentColor) 48%, var(--rt-color-border, currentColor));
|
|
452
|
+
background: var(--rt-color-background, Canvas);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
[data-roottale-cms] :where(.rt-archive-related__item-title) {
|
|
456
|
+
overflow-wrap: anywhere;
|
|
457
|
+
color: var(--rt-color-foreground, CanvasText);
|
|
458
|
+
font-size: var(--rt-font-size-base, 1rem);
|
|
459
|
+
font-weight: var(--rt-font-weight-semibold, 600);
|
|
460
|
+
line-height: 1.35;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
[data-roottale-cms] :where(.rt-archive-related__item-excerpt) {
|
|
464
|
+
display: -webkit-box;
|
|
465
|
+
overflow: hidden;
|
|
466
|
+
color: var(--rt-color-muted-foreground, currentColor);
|
|
467
|
+
font-size: var(--rt-font-size-sm, 0.875rem);
|
|
468
|
+
line-height: 1.5;
|
|
469
|
+
-webkit-box-orient: vertical;
|
|
470
|
+
-webkit-line-clamp: 2;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] p) {
|
|
356
474
|
margin: 0;
|
|
357
475
|
line-height: 1.7;
|
|
358
476
|
}
|
|
@@ -404,6 +522,23 @@
|
|
|
404
522
|
margin: 0;
|
|
405
523
|
}
|
|
406
524
|
|
|
525
|
+
/* 고객 사이트의 CSS 리셋(Preflight 등)이 list-style 을 지워도 불릿/번호 유지. */
|
|
526
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] ul) {
|
|
527
|
+
list-style: disc;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] ol) {
|
|
531
|
+
list-style: decimal;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] ul ul) {
|
|
535
|
+
list-style: circle;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] ul ul ul) {
|
|
539
|
+
list-style: square;
|
|
540
|
+
}
|
|
541
|
+
|
|
407
542
|
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] li > p) {
|
|
408
543
|
margin: 0;
|
|
409
544
|
}
|
|
@@ -454,6 +589,9 @@
|
|
|
454
589
|
padding: var(--rt-spacing-3, 0.75rem);
|
|
455
590
|
font-family: var(--rt-font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
|
|
456
591
|
font-size: 0.9em;
|
|
592
|
+
/* 코드는 어절 조판 대상 아님 — 가로 스크롤로 원문 유지. */
|
|
593
|
+
word-break: normal;
|
|
594
|
+
overflow-wrap: normal;
|
|
457
595
|
}
|
|
458
596
|
|
|
459
597
|
[data-roottale-cms] :where(.rt-cms-article [data-roottale-cms="body"] pre code) {
|
|
@@ -538,6 +676,107 @@
|
|
|
538
676
|
opacity: 0.92;
|
|
539
677
|
}
|
|
540
678
|
|
|
679
|
+
/* FAQ 블록 (roottale/faq) — <details> 아코디언. 0 JS. */
|
|
680
|
+
[data-roottale-cms] :where(.rt-cms-faq) {
|
|
681
|
+
border: 1px solid color-mix(in srgb, var(--rt-color-border, #dceef3) 88%, transparent);
|
|
682
|
+
border-radius: min(var(--rt-radius-md, 0.5rem), 0.75rem);
|
|
683
|
+
overflow: hidden;
|
|
684
|
+
margin: var(--rt-spacing-6, 1.5rem) 0;
|
|
685
|
+
}
|
|
686
|
+
[data-roottale-cms] :where(.rt-cms-faq-item) {
|
|
687
|
+
border-block-start: 1px solid color-mix(in srgb, var(--rt-color-border, #dceef3) 88%, transparent);
|
|
688
|
+
}
|
|
689
|
+
[data-roottale-cms] :where(.rt-cms-faq-item:first-child) {
|
|
690
|
+
border-block-start: 0;
|
|
691
|
+
}
|
|
692
|
+
[data-roottale-cms] :where(.rt-cms-faq-q) {
|
|
693
|
+
cursor: pointer;
|
|
694
|
+
list-style: none;
|
|
695
|
+
display: flex;
|
|
696
|
+
gap: 0.5rem;
|
|
697
|
+
align-items: flex-start;
|
|
698
|
+
padding: 1rem 1.125rem;
|
|
699
|
+
font-weight: var(--rt-font-weight-semibold, 600);
|
|
700
|
+
line-height: 1.5;
|
|
701
|
+
background: color-mix(in srgb, var(--rt-color-muted, #f7f8f7) 30%, white);
|
|
702
|
+
}
|
|
703
|
+
[data-roottale-cms] :where(.rt-cms-faq-q)::-webkit-details-marker {
|
|
704
|
+
display: none;
|
|
705
|
+
}
|
|
706
|
+
[data-roottale-cms] :where(.rt-cms-faq-q)::before {
|
|
707
|
+
content: "Q";
|
|
708
|
+
flex: none;
|
|
709
|
+
font-weight: 700;
|
|
710
|
+
color: var(--rt-color-primary, #06162f);
|
|
711
|
+
}
|
|
712
|
+
[data-roottale-cms] :where(.rt-cms-faq-q)::after {
|
|
713
|
+
content: "";
|
|
714
|
+
flex: none;
|
|
715
|
+
margin-inline-start: auto;
|
|
716
|
+
inline-size: 0.6rem;
|
|
717
|
+
block-size: 0.6rem;
|
|
718
|
+
border-inline-end: 2px solid currentColor;
|
|
719
|
+
border-block-end: 2px solid currentColor;
|
|
720
|
+
transform: rotate(45deg);
|
|
721
|
+
transition: transform 0.18s ease;
|
|
722
|
+
opacity: 0.5;
|
|
723
|
+
}
|
|
724
|
+
[data-roottale-cms] :where(.rt-cms-faq-item[open] .rt-cms-faq-q)::after {
|
|
725
|
+
transform: rotate(-135deg);
|
|
726
|
+
}
|
|
727
|
+
[data-roottale-cms] :where(.rt-cms-faq-a) {
|
|
728
|
+
padding: 0.25rem 1.125rem 1.125rem;
|
|
729
|
+
}
|
|
730
|
+
[data-roottale-cms] :where(.rt-cms-faq-a p) {
|
|
731
|
+
max-inline-size: none;
|
|
732
|
+
margin: 0;
|
|
733
|
+
}
|
|
734
|
+
[data-roottale-cms] :where(.rt-cms-faq-a > * + *) {
|
|
735
|
+
margin-block-start: 0.5rem;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/* 관련 글 (rt-cms-related) — 글 하단 같은 카테고리 최근 글. */
|
|
739
|
+
[data-roottale-cms] :where(.rt-cms-related) {
|
|
740
|
+
margin: var(--rt-spacing-8, 2.5rem) 0 var(--rt-spacing-6, 1.5rem);
|
|
741
|
+
}
|
|
742
|
+
[data-roottale-cms] :where(.rt-cms-related-title) {
|
|
743
|
+
font-size: var(--rt-font-size-lg, 1.125rem);
|
|
744
|
+
font-weight: var(--rt-font-weight-bold, 700);
|
|
745
|
+
margin: 0 0 var(--rt-spacing-3, 0.75rem);
|
|
746
|
+
}
|
|
747
|
+
[data-roottale-cms] :where(.rt-cms-related-list) {
|
|
748
|
+
list-style: none;
|
|
749
|
+
margin: 0;
|
|
750
|
+
padding: 0;
|
|
751
|
+
display: grid;
|
|
752
|
+
gap: 0.5rem;
|
|
753
|
+
}
|
|
754
|
+
[data-roottale-cms] :where(.rt-cms-related-link) {
|
|
755
|
+
display: flex;
|
|
756
|
+
align-items: center;
|
|
757
|
+
gap: 0.75rem;
|
|
758
|
+
padding: 0.625rem 0.75rem;
|
|
759
|
+
border: 1px solid color-mix(in srgb, var(--rt-color-border, #dceef3) 88%, transparent);
|
|
760
|
+
border-radius: min(var(--rt-radius-md, 0.5rem), 0.625rem);
|
|
761
|
+
color: inherit;
|
|
762
|
+
text-decoration: none;
|
|
763
|
+
transition: background 0.15s ease;
|
|
764
|
+
}
|
|
765
|
+
[data-roottale-cms] :where(.rt-cms-related-link:hover) {
|
|
766
|
+
background: color-mix(in srgb, var(--rt-color-muted, #f7f8f7) 50%, white);
|
|
767
|
+
}
|
|
768
|
+
[data-roottale-cms] :where(.rt-cms-related-thumb) {
|
|
769
|
+
flex: none;
|
|
770
|
+
inline-size: 3rem;
|
|
771
|
+
block-size: 3rem;
|
|
772
|
+
object-fit: cover;
|
|
773
|
+
border-radius: min(var(--rt-radius-sm, 0.375rem), 0.5rem);
|
|
774
|
+
}
|
|
775
|
+
[data-roottale-cms] :where(.rt-cms-related-post-title) {
|
|
776
|
+
font-weight: var(--rt-font-weight-medium, 500);
|
|
777
|
+
line-height: 1.45;
|
|
778
|
+
}
|
|
779
|
+
|
|
541
780
|
/* RootTalePostCard */
|
|
542
781
|
[data-roottale-cms] :where(.rt-cms-card) {
|
|
543
782
|
display: block;
|
|
@@ -546,6 +785,8 @@
|
|
|
546
785
|
/* RootTaleTableOfContents */
|
|
547
786
|
[data-roottale-cms] :where(.rt-cms-toc) {
|
|
548
787
|
position: relative;
|
|
788
|
+
/* 본문 measure 와 동일 폭으로 정렬(본문 위 인라인 배치). */
|
|
789
|
+
max-inline-size: 72ch;
|
|
549
790
|
background: color-mix(in srgb, var(--rt-color-muted, #f7f8f7) 34%, white);
|
|
550
791
|
border: 1px solid color-mix(in srgb, var(--rt-color-border, #dceef3) 86%, transparent);
|
|
551
792
|
border-radius: min(var(--rt-radius-md, 0.5rem), 0.5rem);
|
package/dist/routes.d.ts
CHANGED
|
@@ -177,8 +177,19 @@ interface RevalidateRouteConfig {
|
|
|
177
177
|
/** `rtlk_cust_*` — 블로그 조회/webhook 검증 공통 키. */
|
|
178
178
|
apiKey: string;
|
|
179
179
|
apiBase?: string;
|
|
180
|
-
/**
|
|
181
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Next 의 `revalidatePath` (또는 동등 콜백) 주입. 2번째 인자(`"layout"`)로
|
|
182
|
+
* 레이아웃 전역 무효화를 지원 — `theme.updated` 시 정적 페이지까지 반영하려면
|
|
183
|
+
* 필요(next `revalidatePath(path, "layout")` 시그니처와 호환).
|
|
184
|
+
*/
|
|
185
|
+
revalidate: (path: string, type?: "layout" | "page") => void | Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Next 의 `revalidateTag` (또는 동등 콜백) 주입 — `theme.updated`(W0-4) 시
|
|
188
|
+
* 테마 fetch 캐시 tag 를 무효화한다. 미지정 시 tag 무효화 생략(layout 만).
|
|
189
|
+
*/
|
|
190
|
+
revalidateTag?: (tag: string) => void | Promise<void>;
|
|
191
|
+
/** 테마 fetch 캐시 tag 이름. 기본 "rt-theme"(cms-client `THEME_CACHE_TAG`). */
|
|
192
|
+
themeTag?: string;
|
|
182
193
|
/** 블로그 base path. 기본 "/blog". */
|
|
183
194
|
blogBasePath?: string;
|
|
184
195
|
/**
|
package/dist/routes.js
CHANGED
|
@@ -307,6 +307,17 @@ function createRevalidateRoute(config) {
|
|
|
307
307
|
if (!result.ok) {
|
|
308
308
|
return Response.json({ ok: false, reason: result.reason }, { status: 401 });
|
|
309
309
|
}
|
|
310
|
+
if (result.event === "theme.updated") {
|
|
311
|
+
const themeTag = config.themeTag ?? "rt-theme";
|
|
312
|
+
if (config.revalidateTag) await config.revalidateTag(themeTag);
|
|
313
|
+
await config.revalidate("/", "layout");
|
|
314
|
+
return Response.json({
|
|
315
|
+
ok: true,
|
|
316
|
+
event: result.event,
|
|
317
|
+
deliveryId: result.deliveryId,
|
|
318
|
+
revalidated: { tag: config.revalidateTag ? themeTag : null, layout: "/" }
|
|
319
|
+
});
|
|
320
|
+
}
|
|
310
321
|
const INVARIANTS = [
|
|
311
322
|
"/feed.xml",
|
|
312
323
|
"/llms.txt",
|
|
@@ -612,7 +623,7 @@ function createFleetInfoRoute(config = {}) {
|
|
|
612
623
|
site: config.site ?? env.VERCEL_PROJECT_PRODUCTION_URL ?? null,
|
|
613
624
|
framework: "next",
|
|
614
625
|
cms_client: CMS_CLIENT_VERSION,
|
|
615
|
-
cms_renderer: true ? "0.
|
|
626
|
+
cms_renderer: true ? "0.41.0" : "dev",
|
|
616
627
|
build_sha: env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7) ?? null
|
|
617
628
|
},
|
|
618
629
|
{ headers: { "cache-control": "public, max-age=300" } }
|