@roottale/cms-renderer-next 0.2.0 → 0.3.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 CHANGED
@@ -1,5 +1,85 @@
1
1
  # @roottale/cms-renderer-next
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1e738fd: Add design token auto-application for external sites.
8
+
9
+ admin `mysite.roottale.com → 디자인` 에서 저장한 색·글꼴·둥글기를 외부 사이트의
10
+ RootTale 블로그 컴포넌트가 자동으로 가져와 적용한다.
11
+
12
+ **`@roottale/cms-client`** — 신규 `fetchTheme()` + `RootTaleTheme` 타입 export.
13
+ `GET /v1/cms/public/theme` 엔드포인트 호출 (Bearer rtlk*cust*\*).
14
+
15
+ **`@roottale/cms-renderer-next`** — `<RootTaleBlogList>` / `<RootTaleBlogPost>` 가
16
+ 신규 `theme` prop 지원. 동시에 `<RootTaleThemeProvider>` 컴포넌트로 부모에서 1회
17
+ fetch 한 뒤 자식 컴포넌트가 변수를 상속하는 패턴도 지원.
18
+
19
+ ```tsx
20
+ // 자동 fetch (기본) — 별도 설정 없이 admin 토큰 적용
21
+ <RootTaleBlogList apiKey={process.env.ROOTTALE_API_KEY!} />
22
+
23
+ // 명시 override
24
+ <RootTaleBlogList
25
+ apiKey={process.env.ROOTTALE_API_KEY!}
26
+ theme={{ colors: { primary: "#0070f3" } }}
27
+ />
28
+
29
+ // 부모 1회 fetch → 자식들 상속 (다중 컴포넌트 페이지에서 호출 1회로 축소)
30
+ const theme = await fetchTheme({ apiKey });
31
+ <RootTaleThemeProvider theme={theme}>
32
+ <RootTaleBlogList apiKey={apiKey} theme={null} />
33
+ <RootTaleBlogPost apiKey={apiKey} slugOrId="..." theme={null} />
34
+ </RootTaleThemeProvider>
35
+ ```
36
+
37
+ **`@roottale/cms-renderer-astro`** — `renderBlogList` / `renderBlogPost` 에 동일
38
+ 의미의 `theme` 옵션. style="..." 속성으로 CSS 변수 주입 (서버 측 escape 보강).
39
+
40
+ `theme` prop semantics:
41
+
42
+ - `undefined` → 자동 fetch
43
+ - `RootTaleTheme` → 호출자 override
44
+ - `null` → 자동 fetch 건너뜀 (부모 `RootTaleThemeProvider` 활용 시)
45
+
46
+ fetch 실패는 비치명적 — CSS fallback 으로 graceful degrade.
47
+
48
+ ### Patch Changes
49
+
50
+ - Updated dependencies [a7444e2]
51
+ - Updated dependencies [1e738fd]
52
+ - @roottale/cms-client@0.2.0
53
+
54
+ ## 0.2.1
55
+
56
+ ### Patch Changes
57
+
58
+ - dist build 도입 — `.ts` 소스 직접 publish 폐기.
59
+
60
+ 기존 (0.1.0/0.2.0): `exports` 가 `./src/server.ts` 가리킴 → 외부 Next.js
61
+ Turbopack/Webpack 이 npm 모듈에서 TS 자동 컴파일 안 해 build 실패 (customer
62
+ site 가 `transpilePackages` 명시해야 했음).
63
+
64
+ 수정: `tsup` 으로 `dist/*.js` + `dist/*.d.ts` (ESM) 출력. `exports` 가 dist
65
+ 가리킴. customer site 측 `transpilePackages` 불필요.
66
+
67
+ - `cms-client@0.1.1` — ESM `dist/server.js` + types
68
+ - `cms-core@0.2.1` — ESM `dist/index.js` + types
69
+ - `cms-renderer-next@0.2.1` — ESM `dist/{server,index}.js` + types + `dist/cms-public.css`
70
+ - `server.tsx` 의 `import "./styles/cms-public.css"` 제거 — customer 가
71
+ `@roottale/cms-renderer-next/styles` 로 명시 import (README 정합).
72
+ - `cms-renderer-astro@0.2.1` — ESM `dist/index.js` + types
73
+
74
+ 후속 (customer site PR):
75
+
76
+ - roottale-web / kjmtax / theoneulsan 의 `next.config` 의 `transpilePackages`
77
+ 에서 `@roottale/cms-*` 제거. `pnpm update @roottale/cms-client @roottale/cms-renderer-next` 로 patch 적용.
78
+
79
+ - Updated dependencies
80
+ - @roottale/cms-core@0.2.1
81
+ - @roottale/cms-client@0.1.1
82
+
3
83
  ## 0.2.0
4
84
 
5
85
  ### Minor Changes
@@ -9,6 +89,7 @@
9
89
  ADR-0029 §0 amend (publish-only dormant) 는 design system 5 패키지 (tokens, ui-css, ui-react, ui-astro, ui-admin) 에 한정. cms-\* 는 별도 정책 — 실행 로직 + 5-20 외부 customer site 직접 의존 + schema 호환 + 보안 경계. Codex consult verdict (session `019e6703…`) 정합.
10
90
 
11
91
  변경:
92
+
12
93
  - 4 패키지 `private: true` 해제 + `publishConfig.access: "public"`
13
94
  - `@roottale/cms-renderer-next` 에서 `@roottale/tokens/tokens.css` import 제거 — 모든 `--rt-*` 변수에 static fallback 으로 self-contained. tokens dormant 와 무관하게 동작.
14
95
  - `RootTaleLeadForm` RSC 추가 — 외부 사이트 진단 폼 (`vertical`/`redirectUrl` props, medical 국외이전 동의 자동).
@@ -17,6 +98,7 @@
17
98
  - `cms-renderer-astro` 도 동등 surface 유지를 위해 동시 publish
18
99
 
19
100
  후속:
101
+
20
102
  - ADR 신규 — cms-\* publish 정책 (별 PR)
21
103
  - Astro 측 LeadForm 컴포넌트 (현재 `@roottale/ui-astro` 위치, `cms-renderer-astro` 로 이동 검토)
22
104
 
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,228 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ import { CmsPostContent, CmsPostType, RootTaleTheme } from '@roottale/cms-client/server';
3
+ export { RootTaleTheme } from '@roottale/cms-client/server';
4
+ import { Block } from '@roottale/cms-core';
5
+
6
+ /**
7
+ * `<RootTalePostCard>` — server-only summary card for a single CmsPostContent.
8
+ *
9
+ * Building block for `<RootTaleBlogList>` (and customer-side custom grids).
10
+ * Pure SSR — anchor + title + meta + excerpt. Featured image is left out for
11
+ * now: `CmsPostContent` only carries `featuredMediaId`, not a public URL.
12
+ * Media URL resolution belongs in a separate slice once `cms-client` exposes
13
+ * it (ADR-0034 §4 media followup — CF Images variants).
14
+ */
15
+
16
+ interface RootTalePostCardProps {
17
+ post: CmsPostContent;
18
+ /** Customer-side URL builder. Defaults to `/blog/${slug}`. */
19
+ href?: (post: CmsPostContent) => string;
20
+ }
21
+ declare function RootTalePostCard(props: RootTalePostCardProps): ReactElement;
22
+
23
+ /**
24
+ * `@roottale/cms-renderer/toc` — Tiptap doc → TOC entries + heading id injection.
25
+ *
26
+ * Server-only utility. Walks a Tiptap JSON doc, extracts H2/H3 headings, and
27
+ * derives stable kebab-case ids (Korean-safe). The companion `attachHeadingIds`
28
+ * mutates a shallow clone of the doc so renderers can emit `<h2 id="...">`
29
+ * anchors that match what `<RootTaleTableOfContents>` links to.
30
+ *
31
+ * Internal ancestor: `roottale-internal/packages/cms-renderer/src/toc.ts`
32
+ * (HTML-string based, vendored snapshot). Ported to Tiptap-JSON because the
33
+ * platform renderer no longer round-trips through HTML before render.
34
+ */
35
+ interface TocEntry {
36
+ level: 2 | 3;
37
+ text: string;
38
+ id: string;
39
+ }
40
+ declare function headingToId(text: string): string;
41
+ declare function extractToc(doc: Record<string, unknown>): TocEntry[];
42
+ /**
43
+ * Returns a shallow-cloned Tiptap doc where each H2/H3 carries the matching
44
+ * `attrs.id` from `entries` (same order as `extractToc` returns). Existing
45
+ * `id` attrs are overwritten so renderer + TOC always agree on anchors.
46
+ */
47
+ declare function attachHeadingIds(doc: Record<string, unknown>, entries: TocEntry[]): Record<string, unknown>;
48
+
49
+ /**
50
+ * `<RootTaleTableOfContents>` — server-only anchor list for blog posts.
51
+ *
52
+ * Pure SSR. Renders `<nav>` + ordered anchors that link to `#<id>` slots
53
+ * emitted by `attachHeadingIds`. No scroll-spy here — that requires an
54
+ * `IntersectionObserver` which only exists in the browser; a client-island
55
+ * variant can layer on top later without changing the data contract.
56
+ *
57
+ * Internal ancestor: `roottale-internal/packages/cms-renderer/src/TableOfContents.tsx`
58
+ * (had `'use client'` + IntersectionObserver). Stripped to server-safe form per
59
+ * the platform renderer contract (no `'use client'` boundary — ADR-0023 §5.3).
60
+ */
61
+
62
+ interface RootTaleTableOfContentsProps {
63
+ entries: TocEntry[];
64
+ title?: string;
65
+ }
66
+ declare function RootTaleTableOfContents(props: RootTaleTableOfContentsProps): ReactElement | null;
67
+
68
+ /**
69
+ * `<RootTaleFloatingCta>` — fixed-position call-to-action stack.
70
+ *
71
+ * Server-only (anchors only, no state). External targets (`kakao` / `custom`)
72
+ * open in a new tab with `rel="noopener noreferrer"`; tel/contact stay in the
73
+ * same tab so iOS/Android can trigger the dialer / mailto handler.
74
+ *
75
+ * Internal ancestor: `roottale-internal/packages/cms-renderer/src/FloatingCta.tsx`.
76
+ */
77
+
78
+ type CtaButtonType = "phone" | "contact" | "kakao" | "custom";
79
+ interface CtaButton {
80
+ type: CtaButtonType;
81
+ label: string;
82
+ href: string;
83
+ /** Optional override icon (emoji or short text). */
84
+ icon?: string;
85
+ }
86
+ type FloatingCtaPosition = "bottom-right" | "bottom-left" | "bottom-center";
87
+ interface RootTaleFloatingCtaProps {
88
+ buttons: CtaButton[];
89
+ position?: FloatingCtaPosition;
90
+ }
91
+ declare function RootTaleFloatingCta(props: RootTaleFloatingCtaProps): ReactElement | null;
92
+
93
+ /**
94
+ * `@roottale/cms-renderer-next` — RootTaleLeadForm RSC.
95
+ *
96
+ * 외부 고객 사이트(roottale.com, 고객사 외주)의 진단 신청 폼. HTML form POST →
97
+ * tenant-api `/v1/public/inquiries`. cross-origin POST 는 CORS 불필요 (HTML form
98
+ * submission). PII 는 server 측에서 암호화 후 `inquiries` insert, 302 redirect.
99
+ *
100
+ * vertical prop:
101
+ * - 미지정 = "분야 선택" select 노출 (consulting/medical/tax/legal)
102
+ * - 지정 = hidden input + 표시 라벨 + vertical 별 추가 필드
103
+ * (medical = 국외이전 동의 — ADR-0018)
104
+ *
105
+ * redirectPath prop:
106
+ * - 외부 사이트가 폼 제출 후 돌아갈 path (API 가 allowlist 검증, ?ok=1 / ?err=*)
107
+ *
108
+ * 0 JS RSC. customer-facing className 전체 `.rt-cms-*` prefix, scoped via
109
+ * `[data-roottale-cms]` (cms-public.css). customer override 자유.
110
+ */
111
+
112
+ type LeadFormVertical = "consulting" | "medical" | "tax" | "legal";
113
+ interface RootTaleLeadFormProps {
114
+ /** lead intake endpoint. 예: `https://api.roottale.com/v1/public/inquiries` */
115
+ action: string;
116
+ /**
117
+ * vertical 고정. 미지정 시 select 노출.
118
+ * `medical` 일 때 국외이전 동의 체크박스 추가 (ADR-0018).
119
+ */
120
+ vertical?: LeadFormVertical;
121
+ /**
122
+ * 폼 제출 후 API가 돌려보낼 절대 URL.
123
+ * tenant-api `LEAD_INTAKE_ALLOWED_ORIGINS` allowlist 검증. fail = fallback.
124
+ * 미지정 시 tenant-api env의 LEAD_INTAKE_REDIRECT_BASE 사용.
125
+ */
126
+ redirectUrl?: string;
127
+ heading?: string;
128
+ description?: string;
129
+ submitLabel?: string;
130
+ /** 추가 클래스 (customer 디자인 hook). */
131
+ className?: string;
132
+ }
133
+ declare function RootTaleLeadForm(props: RootTaleLeadFormProps): ReactElement;
134
+
135
+ /**
136
+ * 단일 block 을 ReactElement 로 변환. `block-to-html.ts` 와 동일 분기.
137
+ */
138
+ declare function renderBlock(block: Block, key?: number): ReactElement;
139
+ /** Block tree 전체를 React element 배열로 직렬화. */
140
+ declare function renderBlocks(blocks: readonly Block[]): ReactElement[];
141
+
142
+ /**
143
+ * `@roottale/cms-renderer-next/server` — RootTale CMS public-render React Server Components.
144
+ *
145
+ * Drop into any RSC-capable framework (Next.js App Router, Astro server islands,
146
+ * React Router server). Single-line import, SSR-only, no `'use client'` boundary.
147
+ *
148
+ * ```tsx
149
+ * import { RootTaleBlogList } from "@roottale/cms-renderer-next/server";
150
+ *
151
+ * export default function BlogPage() {
152
+ * return <RootTaleBlogList apiKey={process.env.ROOTTALE_API_KEY!} />;
153
+ * }
154
+ * ```
155
+ *
156
+ * Customer site MUST keep `apiKey` server-side. Browser bundle ships zero
157
+ * RootTale credentials (ADR-0023 §5.1 #15).
158
+ *
159
+ * Design tokens auto-apply via the CSS import below. Customers can override
160
+ * any class with their own CSS or Tailwind utilities — scoped selectors use
161
+ * `:where()` to keep specificity at zero (ADR-0023 §5.1 #10).
162
+ */
163
+
164
+ /**
165
+ * RootTaleBlogList/Post 가 받는 theme prop 의 평가 결과:
166
+ * - `RootTaleTheme` 객체 → 그대로 CSS 변수로 주입
167
+ * - `null` → override 없음 (cms-public.css fallback 그대로)
168
+ * - `undefined` → API key 로 자동 fetch (mysite.roottale.com 의 admin /design)
169
+ */
170
+ type RootTaleThemeInput = RootTaleTheme | null | undefined;
171
+ /**
172
+ * RootTale 디자인 토큰을 CSS 변수로 자식 트리에 주입한다. 외부 사이트가
173
+ * 여러 RootTale 컴포넌트를 한 레이아웃에 묶을 때, 컴포넌트마다 같은 토큰을
174
+ * fetch 하지 않도록 부모에서 한 번만 fetch 해서 전달하는 용도.
175
+ *
176
+ * ```tsx
177
+ * const theme = await fetchTheme({ apiKey });
178
+ * <RootTaleThemeProvider theme={theme}>
179
+ * <RootTaleBlogList apiKey={apiKey} theme={null} />
180
+ * <RootTaleBlogPost apiKey={apiKey} slugOrId="..." theme={null} />
181
+ * </RootTaleThemeProvider>
182
+ * ```
183
+ *
184
+ * 자식 RootTale 컴포넌트가 `theme={null}` 을 받으면 자체 주입을 건너뛰고
185
+ * 부모의 변수를 그대로 상속. 자식이 `theme` 을 지정하면 자식 변수가 우선.
186
+ */
187
+ declare function RootTaleThemeProvider(props: {
188
+ theme: RootTaleTheme | null | undefined;
189
+ children: ReactNode;
190
+ }): ReactElement;
191
+ type RootTaleBlogListProps = {
192
+ apiKey: string;
193
+ baseUrl?: string;
194
+ limit?: number;
195
+ /** Filter by post type (`post` or `page`). Default = `post` only (blog list 의도). */
196
+ type?: CmsPostType;
197
+ /** Build a customer-side URL for a single post. Defaults to `/blog/${slug}`. */
198
+ postHref?: (post: CmsPostContent) => string;
199
+ /** Rendered when the tenant has no published posts yet. */
200
+ emptyMessage?: string;
201
+ /**
202
+ * RootTale 디자인 토큰. 생략 시 admin /design 에서 저장한 값을 자동 fetch.
203
+ * `null` 을 명시하면 자동 fetch 도 건너뛰고 CSS fallback 사용 (부모
204
+ * `<RootTaleThemeProvider>` 가 변수를 이미 설정한 경우 유용).
205
+ */
206
+ theme?: RootTaleThemeInput;
207
+ };
208
+ declare function RootTaleBlogList(props: RootTaleBlogListProps): Promise<ReactElement>;
209
+ type RootTaleBlogPostProps = {
210
+ apiKey: string;
211
+ slugOrId: string;
212
+ baseUrl?: string;
213
+ /** Rendered when the slug is not found or not published. */
214
+ notFoundElement?: ReactElement;
215
+ /**
216
+ * When true, prepends an auto-derived `<RootTaleTableOfContents>` before
217
+ * the article body (H2/H3 headings only). Default: false — keep markup
218
+ * stable for customers who already control their own layout.
219
+ */
220
+ showTableOfContents?: boolean;
221
+ /** Title shown above the TOC (only when `showTableOfContents`). */
222
+ tableOfContentsTitle?: string;
223
+ /** See `RootTaleBlogListProps['theme']`. */
224
+ theme?: RootTaleThemeInput;
225
+ };
226
+ declare function RootTaleBlogPost(props: RootTaleBlogPostProps): Promise<ReactElement>;
227
+
228
+ export { type CtaButton, type CtaButtonType, type FloatingCtaPosition, type LeadFormVertical, RootTaleBlogList, type RootTaleBlogListProps, RootTaleBlogPost, type RootTaleBlogPostProps, RootTaleFloatingCta, type RootTaleFloatingCtaProps, RootTaleLeadForm, type RootTaleLeadFormProps, RootTalePostCard, type RootTalePostCardProps, RootTaleTableOfContents, type RootTaleTableOfContentsProps, type RootTaleThemeInput, RootTaleThemeProvider, type TocEntry, attachHeadingIds, extractToc, headingToId, renderBlock, renderBlocks };