@conscience-technology/ctds 0.4.6 → 0.4.7

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.
@@ -10,6 +10,9 @@
10
10
  * (예: solar에 자동차 아이콘이 없음 → truck/car가 같은 Truck으로 수렴).
11
11
  * 이건 라이브러리 한계라는 사실을 받아들이는 거지, 설계 결함이 아님.
12
12
  * - 매핑 자체가 아예 없는 경우에만 family 전용 placeholder("빗금 사각형")를 렌더.
13
+ * - 일부 editor UI 아이콘(text-align, flex align, bold/italic 등)은 solar/lucide에
14
+ * 형상이 명확한 등가물이 없어 축과 무관하게 phosphor로 고정 노출한다
15
+ * (forcePhosphorFamily). 유저에겐 조용히 처리.
13
16
  *
14
17
  * weight(regular/fill) 반응:
15
18
  * phosphor → weight prop으로 반응
@@ -222,6 +225,26 @@ import {
222
225
  Flower as PhFlower,
223
226
  Leaf as PhLeaf,
224
227
  Tree as PhTree,
228
+ // Editor UI — forced to phosphor regardless of family axis (see forcePhosphorFamily)
229
+ TextAlignLeft as PhTextAlignLeft,
230
+ TextAlignCenter as PhTextAlignCenter,
231
+ TextAlignRight as PhTextAlignRight,
232
+ TextAlignJustify as PhTextAlignJustify,
233
+ TextB as PhTextB,
234
+ TextItalic as PhTextItalic,
235
+ TextUnderline as PhTextUnderline,
236
+ TextStrikethrough as PhTextStrikethrough,
237
+ TextIndent as PhTextIndent,
238
+ TextOutdent as PhTextOutdent,
239
+ AlignLeft as PhAlignLeft,
240
+ AlignCenterHorizontal as PhAlignCenterHorizontal,
241
+ AlignRight as PhAlignRight,
242
+ AlignTop as PhAlignTop,
243
+ AlignCenterVertical as PhAlignCenterVertical,
244
+ AlignBottom as PhAlignBottom,
245
+ Code as PhCode,
246
+ CodeBlock as PhCodeBlock,
247
+ Quotes as PhQuotes,
225
248
  } from "@phosphor-icons/react";
226
249
 
227
250
  // Solar (Linear = regular, Bold = fill) ───────────────────────────
@@ -836,6 +859,26 @@ const phosphorSet: Partial<Record<IconName, AnyComp>> = {
836
859
  flower: PhFlower,
837
860
  leaf: PhLeaf,
838
861
  tree: PhTree,
862
+ // Editor UI — forced phosphor (see forcePhosphorFamily)
863
+ "text-align-left": PhTextAlignLeft,
864
+ "text-align-center": PhTextAlignCenter,
865
+ "text-align-right": PhTextAlignRight,
866
+ "text-align-justify": PhTextAlignJustify,
867
+ "text-b": PhTextB,
868
+ "text-italic": PhTextItalic,
869
+ "text-underline": PhTextUnderline,
870
+ "text-strikethrough": PhTextStrikethrough,
871
+ "text-indent": PhTextIndent,
872
+ "text-outdent": PhTextOutdent,
873
+ "align-left": PhAlignLeft,
874
+ "align-center-horizontal": PhAlignCenterHorizontal,
875
+ "align-right": PhAlignRight,
876
+ "align-top": PhAlignTop,
877
+ "align-center-vertical": PhAlignCenterVertical,
878
+ "align-bottom": PhAlignBottom,
879
+ code: PhCode,
880
+ "code-block": PhCodeBlock,
881
+ quotes: PhQuotes,
839
882
  };
840
883
 
841
884
  /* solar 세트 — phosphor 이름 → solar의 가장 자연스러운 대응.
@@ -1310,6 +1353,36 @@ function ArrowsDownUpIcon({
1310
1353
  );
1311
1354
  }
1312
1355
 
1356
+ /* 축과 무관하게 phosphor로 고정 노출할 아이콘.
1357
+ solar/lucide에 형상이 명확한 등가물이 없거나 기존 매핑이 의미상 틀린 자리. */
1358
+ const forcePhosphorFamily = new Set<IconName>([
1359
+ "text-align-left",
1360
+ "text-align-center",
1361
+ "text-align-right",
1362
+ "text-align-justify",
1363
+ "text-b",
1364
+ "text-italic",
1365
+ "text-underline",
1366
+ "text-strikethrough",
1367
+ "text-indent",
1368
+ "text-outdent",
1369
+ "align-left",
1370
+ "align-center-horizontal",
1371
+ "align-right",
1372
+ "align-top",
1373
+ "align-center-vertical",
1374
+ "align-bottom",
1375
+ "code",
1376
+ "code-block",
1377
+ "quotes",
1378
+ // 기존 매핑이 solar/lucide에서 의미상 틀린 자리
1379
+ "square-half",
1380
+ "toggle-left",
1381
+ "toggle-right",
1382
+ "line-segments",
1383
+ "textbox",
1384
+ ]);
1385
+
1313
1386
  export interface IconProps extends React.SVGAttributes<SVGSVGElement> {
1314
1387
  name: IconName;
1315
1388
  /** 축 기본값(html data-icon-family) 오버라이드 */
@@ -1332,7 +1405,8 @@ export function Icon({
1332
1405
  ...rest
1333
1406
  }: IconProps) {
1334
1407
  const axis = useIconAxis();
1335
- const family = familyProp ?? axis.family;
1408
+ const requestedFamily = familyProp ?? axis.family;
1409
+ const family = forcePhosphorFamily.has(name) ? "phosphor" : requestedFamily;
1336
1410
  const weight = weightProp ?? axis.weight;
1337
1411
 
1338
1412
  /* family와 무관하게 늘 같은 모양으로 나와야 하는 커스텀 아이콘 분기. */
@@ -212,12 +212,14 @@
212
212
  display: none;
213
213
  }
214
214
 
215
- /* pinned 경계 hairline과 tick(cellSlot 좌측 세로선) 겹침 방지.
215
+ /* pinned/select 경계 hairline과 tick(cellSlot 좌측 세로선) 겹침 방지.
216
216
  - pinned-left 셀의 바로 다음 cellSlot: pinned의 border-right와 다음 셀의
217
217
  tick이 바로 붙어 두 줄처럼 보이는 현상 제거.
218
- - pinned-right 셀 자신: border-left와 자기 tick이 같은 지점에 겹침. */
218
+ - pinned-right 셀 자신: border-left와 자기 tick이 같은 지점에 겹침.
219
+ - selectCell 바로 다음 cellSlot: selectCell의 border-right와 겹침 제거. */
219
220
  .cellSlot[data-pinned="left"] + .cellSlot .tick,
220
- .cellSlot[data-pinned="right"] .tick {
221
+ .cellSlot[data-pinned="right"] .tick,
222
+ .selectCell + .cellSlot .tick {
221
223
  display: none;
222
224
  }
223
225
 
@@ -51,7 +51,11 @@
51
51
  padding-inline-end: 0;
52
52
  }
53
53
 
54
- /* ── 세로 구분선 ────────────────────────────────────────── */
54
+ /* ── 세로 구분선 ──────────────────────────────────────────
55
+ pinned/select 경계는 cellSlot/selectCell box-shadow가 담당. JS에서 skip하지만
56
+ 렌더 지연·외부 조립 등 어떤 경로로든 data-divider가 붙어도 CSS로 보강:
57
+ selectCell 바로 다음 cellSlot, pinned-left 바로 다음 cellSlot, pinned-right
58
+ 자신의 셀 ::before는 숨겨 두 줄 겹침 방지. */
55
59
  .editCell[data-divider]::before,
56
60
  .readonlyCell[data-divider]::before {
57
61
  content: "";
@@ -63,6 +67,15 @@
63
67
  pointer-events: none;
64
68
  }
65
69
 
70
+ [data-edit-cell="select"] + * .editCell::before,
71
+ [data-edit-cell="select"] + * .readonlyCell::before,
72
+ [data-pinned="left"] + * .editCell::before,
73
+ [data-pinned="left"] + * .readonlyCell::before,
74
+ [data-pinned="right"] .editCell::before,
75
+ [data-pinned="right"] .readonlyCell::before {
76
+ display: none;
77
+ }
78
+
66
79
 
67
80
  /* ── Edit cell ────────────────────────────────────────────
68
81
  클릭/포커스 타겟. 활성 상태엔 input/select가 셀을 꽉 채움. */
@@ -179,12 +179,14 @@ function TableRowEditInner<T>(
179
179
  /* 세로 구분선은 editable 여부와 무관하게 모든 셀 사이에 균일하게.
180
180
  엑셀 톤 "셀 그리드" 신호는 일관성이 핵심 — read-only 셀만 선이 빠지면
181
181
  리듬이 깨져 보임. 첫 데이터 셀은 왼쪽에 선 없음(테이블 외곽).
182
- selectable이면 체크박스 ↔ 첫 데이터 셀 사이에도 선.
183
- pinned 경계는 cellSlot box-shadow가 담당 — pinned-right 셀 자신과
184
- pinned-left 바로 다음 셀의 왼쪽 divider는 그으면 두 줄로 겹쳐 보임. */
182
+ pinned/select 경계는 cellSlot/selectCell box-shadow가 담당 —
183
+ pinned-right 셀 자신, pinned-left 바로 다음 셀, selectable일 때
184
+ 첫 데이터 셀의 왼쪽 divider는 그으면 두 줄로 겹쳐 보임. */
185
185
  const prevCol = idx > 0 ? columnMap.get(orderedKeys[idx - 1]) : undefined;
186
186
  const skipDivider =
187
- col.pinned === "right" || prevCol?.pinned === "left";
187
+ col.pinned === "right" ||
188
+ prevCol?.pinned === "left" ||
189
+ (idx === 0 && selectable);
188
190
  const showLeftDivider = (idx > 0 || selectable) && !skipDivider;
189
191
  return (
190
192
  <div
@@ -73,10 +73,11 @@ description: 와이어프레임·목업·기존 페이지를 CTDS로 번역·교
73
73
 
74
74
  1. **CTDS 벤더 컴포넌트가 그대로 맞음** → 그대로 import해서 사용. 끝.
75
75
  2. **살짝 다름** (라벨 하나 더, trailing 영역 하나 추가, 특정 variant 미지원 등) → **로컬 파생 컴포넌트** 생성:
76
- - 가장 비슷한 CTDS 벤더 컴포넌트 코드(JSX + CSS Module)를 **그대로 복사해서 소비 프로젝트 로컬 경로**(예: `web/app/_components/` 또는 `app/components/<page>/_parts/`)에 **다른 이름**으로 둠.
76
+ - 가장 비슷한 CTDS 벤더 컴포넌트 코드(JSX + CSS Module)를 **그대로 복사해서 로컬 경로에 다른 이름으로 둠**. 벤더가 설치된 경로(`components/ctds/` 또는 `src/components/ctds/`) **바깥**이어야 함.
77
+ - 로컬 경로는 소비 프로젝트 관례에 맞춤 — Next.js app router 플젝이면 라우트 옆 `_components/`·`_parts/`, 공용은 `components/local/`·`src/components/local/` 등. 관례가 애매하면 사용자에게 한 번 묻고 그 뒤로는 그 자리 재사용.
77
78
  - 거기서 **디자인만 살짝** 조정. 토큰·height·padding·gap·radius 기본값은 **건드리지 말고 유지** — 조정은 추가 요소/위치/variant 수준.
78
- - **CTDS 벤더 패키지는 절대 수정 금지** ([[feedback_no_vendor_modification]]).
79
- 3. **벤더에 비슷한 것조차 없음** → `prompts/recipes/<자리>.md` 반드시 확인 ([[ASSEMBLY]]). 레시피에도 없으면 **수제가 아니라 사용자에게 보고** — "이 자리 벤더/레시피 둘 다 없음, 어떻게 할지" 물어본다. 임의로 div+span 수제 조립 금지.
79
+ - **CTDS 벤더(설치된 `components/ctds/` 전체 + 레포 작업 중이면 `packages/ctds-cli/assets/`)는 절대 수정 금지** ([[feedback_no_vendor_modification]]).
80
+ 3. **벤더에 비슷한 것조차 없음** → `prompts/recipes/<자리>.md` 반드시 확인 ([[ASSEMBLY]]). 레시피 위치는 레포 루트 `prompts/recipes/` 또는 `ctds init`으로 설치된 소비 프로젝트 루트 `prompts/recipes/` 둘 다 유효. 레시피에도 없으면 **수제가 아니라 사용자에게 보고** — "이 자리 벤더/레시피 둘 다 없음, 어떻게 할지" 물어본다. 임의로 div+span 수제 조립 금지.
80
81
 
81
82
  **왜 로컬 파생이 수제보다 통일성이 좋은가**:
82
83
  - 수제 조립은 토큰 선택부터 틀어진다. padding `12px`·gap `8px` 같은 직관에서 뽑은 값들이 CTDS space scale(--space-2=8, --space-3=12 등)과 미묘하게 어긋나거나, 심지어 하드코딩됨 ([[feedback_no_hardcoded_values]]).
@@ -85,7 +86,7 @@ description: 와이어프레임·목업·기존 페이지를 CTDS로 번역·교
85
86
 
86
87
  **로컬 파생 결정 시 확인**:
87
88
  - 어떤 벤더 컴포넌트에서 베낄지 (파일 경로 명시)
88
- - 어디에 둘지 (`_components/` or `_parts/`)
89
+ - 어디에 둘지 (소비 프로젝트 관례의 로컬 폴더 — 벤더 경로 바깥)
89
90
  - 무엇을 바꿀지 (추가 slot, 삭제한 variant, 추가 라벨 — "디자인 기본값 건드리지 않음" 명시)
90
91
  - 베끼면서 토큰 하드코딩 금지, 기존 토큰 그대로 유지
91
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conscience-technology/ctds",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "CTDS — 하이브리드 디자인 시스템 설치 · 온보딩 CLI (shadcn식 코드 생성기)",
5
5
  "type": "module",
6
6
  "sideEffects": [