@conscience-technology/ctds 0.0.0-stage → 0.1.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.
Files changed (289) hide show
  1. package/README.md +128 -2
  2. package/assets/components/AppFrame/AppFrame.module.css +177 -0
  3. package/assets/components/AppFrame/AppFrame.tsx +93 -0
  4. package/assets/components/AppFrame/index.ts +6 -0
  5. package/assets/components/AttachmentChip/AttachmentChip.module.css +71 -0
  6. package/assets/components/AttachmentChip/AttachmentChip.tsx +74 -0
  7. package/assets/components/AttachmentChip/index.ts +2 -0
  8. package/assets/components/AttachmentThumb/AttachmentThumb.module.css +107 -0
  9. package/assets/components/AttachmentThumb/AttachmentThumb.tsx +91 -0
  10. package/assets/components/AttachmentThumb/index.ts +2 -0
  11. package/assets/components/Avatar/Avatar.module.css +167 -0
  12. package/assets/components/Avatar/Avatar.tsx +228 -0
  13. package/assets/components/Avatar/index.ts +2 -0
  14. package/assets/components/AvatarUpload/AvatarUpload.module.css +130 -0
  15. package/assets/components/AvatarUpload/AvatarUpload.tsx +139 -0
  16. package/assets/components/AvatarUpload/index.ts +2 -0
  17. package/assets/components/Badge/Badge.module.css +76 -0
  18. package/assets/components/Badge/Badge.tsx +68 -0
  19. package/assets/components/Badge/index.ts +2 -0
  20. package/assets/components/Breadcrumb/Breadcrumb.module.css +213 -0
  21. package/assets/components/Breadcrumb/Breadcrumb.tsx +279 -0
  22. package/assets/components/Breadcrumb/index.ts +8 -0
  23. package/assets/components/Button/Button.module.css +216 -0
  24. package/assets/components/Button/Button.tsx +149 -0
  25. package/assets/components/Button/index.ts +2 -0
  26. package/assets/components/Callout/Callout.module.css +146 -0
  27. package/assets/components/Callout/Callout.tsx +138 -0
  28. package/assets/components/Callout/index.ts +2 -0
  29. package/assets/components/ChatInput/ChatInput.module.css +241 -0
  30. package/assets/components/ChatInput/ChatInput.tsx +322 -0
  31. package/assets/components/ChatInput/ModelSelector.tsx +40 -0
  32. package/assets/components/ChatInput/SendButton.tsx +41 -0
  33. package/assets/components/ChatInput/index.ts +10 -0
  34. package/assets/components/Checkbox/Checkbox.module.css +66 -0
  35. package/assets/components/Checkbox/Checkbox.tsx +36 -0
  36. package/assets/components/Checkbox/index.ts +2 -0
  37. package/assets/components/Chip/Chip.module.css +128 -0
  38. package/assets/components/Chip/Chip.tsx +103 -0
  39. package/assets/components/Chip/index.ts +2 -0
  40. package/assets/components/ChoiceCard/ChoiceCard.module.css +142 -0
  41. package/assets/components/ChoiceCard/ChoiceCard.tsx +132 -0
  42. package/assets/components/ChoiceCard/index.ts +2 -0
  43. package/assets/components/ContextMenu/ContextMenu.module.css +102 -0
  44. package/assets/components/ContextMenu/ContextMenu.tsx +392 -0
  45. package/assets/components/ContextMenu/index.ts +9 -0
  46. package/assets/components/DatePicker/Calendar.tsx +234 -0
  47. package/assets/components/DatePicker/DESIGN_NOTES.md +106 -0
  48. package/assets/components/DatePicker/DESIGN_NOTES_TIME.md +125 -0
  49. package/assets/components/DatePicker/DatePicker.module.css +295 -0
  50. package/assets/components/DatePicker/DatePicker.tsx +387 -0
  51. package/assets/components/DatePicker/TimeSelector.tsx +69 -0
  52. package/assets/components/DatePicker/index.ts +8 -0
  53. package/assets/components/DatePicker/presets.ts +80 -0
  54. package/assets/components/DatePicker/utils.ts +148 -0
  55. package/assets/components/Disclosure/Disclosure.module.css +73 -0
  56. package/assets/components/Disclosure/Disclosure.tsx +129 -0
  57. package/assets/components/Disclosure/index.ts +6 -0
  58. package/assets/components/Drawer/Drawer.module.css +257 -0
  59. package/assets/components/Drawer/Drawer.tsx +126 -0
  60. package/assets/components/Drawer/index.ts +7 -0
  61. package/assets/components/EmptyState/EmptyState.module.css +102 -0
  62. package/assets/components/EmptyState/EmptyState.tsx +80 -0
  63. package/assets/components/EmptyState/index.ts +2 -0
  64. package/assets/components/FileIcon/FileIcon.module.css +66 -0
  65. package/assets/components/FileIcon/FileIcon.tsx +84 -0
  66. package/assets/components/FileIcon/fileExtMap.ts +112 -0
  67. package/assets/components/FileIcon/index.ts +9 -0
  68. package/assets/components/FilterButton/FilterButton.module.css +130 -0
  69. package/assets/components/FilterButton/FilterButton.tsx +94 -0
  70. package/assets/components/FilterButton/index.ts +2 -0
  71. package/assets/components/GalleryItem/GalleryItem.module.css +220 -0
  72. package/assets/components/GalleryItem/GalleryItem.tsx +163 -0
  73. package/assets/components/GalleryItem/index.ts +7 -0
  74. package/assets/components/Icon/Icon.tsx +616 -0
  75. package/assets/components/Icon/context.tsx +39 -0
  76. package/assets/components/Icon/index.ts +4 -0
  77. package/assets/components/IconButton/IconButton.module.css +109 -0
  78. package/assets/components/IconButton/IconButton.tsx +110 -0
  79. package/assets/components/IconButton/index.ts +2 -0
  80. package/assets/components/Input/Input.module.css +338 -0
  81. package/assets/components/Input/Input.tsx +371 -0
  82. package/assets/components/Input/index.ts +2 -0
  83. package/assets/components/ListItem/ListItem.module.css +318 -0
  84. package/assets/components/ListItem/ListItem.tsx +262 -0
  85. package/assets/components/ListItem/index.ts +9 -0
  86. package/assets/components/MenuItem/MenuItem.module.css +437 -0
  87. package/assets/components/MenuItem/MenuItem.tsx +529 -0
  88. package/assets/components/MenuItem/index.ts +8 -0
  89. package/assets/components/MetricCard/MetricCard.module.css +153 -0
  90. package/assets/components/MetricCard/MetricCard.tsx +140 -0
  91. package/assets/components/MetricCard/index.ts +2 -0
  92. package/assets/components/Modal/Modal.module.css +175 -0
  93. package/assets/components/Modal/Modal.tsx +98 -0
  94. package/assets/components/Modal/index.ts +2 -0
  95. package/assets/components/Onboarding/DocsIndex.tsx +82 -0
  96. package/assets/components/Onboarding/LayoutBuilder.tsx +160 -0
  97. package/assets/components/Onboarding/Onboarding.module.css +1394 -0
  98. package/assets/components/Onboarding/OnboardingAxisPanel.tsx +515 -0
  99. package/assets/components/Onboarding/OnboardingPage.tsx +1193 -0
  100. package/assets/components/Onboarding/PresetsGallery.tsx +65 -0
  101. package/assets/components/Onboarding/actions.ts +250 -0
  102. package/assets/components/Onboarding/index.ts +8 -0
  103. package/assets/components/Onboarding/palette.css +40 -0
  104. package/assets/components/Onboarding/wizardData.ts +265 -0
  105. package/assets/components/Pagination/Pagination.module.css +82 -0
  106. package/assets/components/Pagination/Pagination.tsx +248 -0
  107. package/assets/components/Pagination/index.ts +6 -0
  108. package/assets/components/ProgressBar/ProgressBar.module.css +64 -0
  109. package/assets/components/ProgressBar/ProgressBar.tsx +54 -0
  110. package/assets/components/ProgressBar/index.ts +2 -0
  111. package/assets/components/QuotationBar/QuotationBar.module.css +61 -0
  112. package/assets/components/QuotationBar/QuotationBar.tsx +48 -0
  113. package/assets/components/QuotationBar/index.ts +2 -0
  114. package/assets/components/Radio/Radio.module.css +63 -0
  115. package/assets/components/Radio/Radio.tsx +33 -0
  116. package/assets/components/Radio/index.ts +2 -0
  117. package/assets/components/Select/Select.module.css +151 -0
  118. package/assets/components/Select/Select.tsx +425 -0
  119. package/assets/components/Select/index.ts +10 -0
  120. package/assets/components/SelectableCard/SelectableCard.module.css +177 -0
  121. package/assets/components/SelectableCard/SelectableCard.tsx +83 -0
  122. package/assets/components/SelectableCard/index.ts +2 -0
  123. package/assets/components/Shortcut/Shortcut.module.css +56 -0
  124. package/assets/components/Shortcut/Shortcut.tsx +156 -0
  125. package/assets/components/Shortcut/index.ts +2 -0
  126. package/assets/components/Sidebar/Sidebar.module.css +242 -0
  127. package/assets/components/Sidebar/Sidebar.tsx +264 -0
  128. package/assets/components/Sidebar/index.ts +7 -0
  129. package/assets/components/SidebarSplit/SidebarSplit.module.css +126 -0
  130. package/assets/components/SidebarSplit/SidebarSplit.tsx +222 -0
  131. package/assets/components/SidebarSplit/index.ts +6 -0
  132. package/assets/components/Slider/Slider.module.css +120 -0
  133. package/assets/components/Slider/Slider.tsx +88 -0
  134. package/assets/components/Slider/index.ts +2 -0
  135. package/assets/components/SplitButton/SplitButton.module.css +190 -0
  136. package/assets/components/SplitButton/SplitButton.tsx +145 -0
  137. package/assets/components/SplitButton/index.ts +6 -0
  138. package/assets/components/Stepper/Stepper.module.css +182 -0
  139. package/assets/components/Stepper/Stepper.tsx +185 -0
  140. package/assets/components/Stepper/index.ts +2 -0
  141. package/assets/components/Switch/Switch.module.css +116 -0
  142. package/assets/components/Switch/Switch.tsx +58 -0
  143. package/assets/components/Switch/index.ts +2 -0
  144. package/assets/components/TableCell/TableCell.module.css +137 -0
  145. package/assets/components/TableCell/TableCellAvatar.tsx +42 -0
  146. package/assets/components/TableCell/TableCellBadge.tsx +23 -0
  147. package/assets/components/TableCell/TableCellIcon.tsx +39 -0
  148. package/assets/components/TableCell/TableCellImage.tsx +49 -0
  149. package/assets/components/TableCell/TableCellLink.tsx +33 -0
  150. package/assets/components/TableCell/TableCellMeter.tsx +55 -0
  151. package/assets/components/TableCell/TableCellNumber.tsx +43 -0
  152. package/assets/components/TableCell/TableCellText.tsx +30 -0
  153. package/assets/components/TableCell/index.ts +23 -0
  154. package/assets/components/TableHeader/TableHeader.module.css +287 -0
  155. package/assets/components/TableHeader/TableHeader.tsx +590 -0
  156. package/assets/components/TableHeader/index.ts +9 -0
  157. package/assets/components/TableRow/TableRow.module.css +290 -0
  158. package/assets/components/TableRow/TableRow.tsx +253 -0
  159. package/assets/components/TableRow/index.ts +8 -0
  160. package/assets/components/TableRowEdit/TableRowEdit.module.css +126 -0
  161. package/assets/components/TableRowEdit/TableRowEdit.tsx +247 -0
  162. package/assets/components/TableRowEdit/index.ts +2 -0
  163. package/assets/components/TableRowSelect/TableRowSelect.module.css +23 -0
  164. package/assets/components/TableRowSelect/TableRowSelect.tsx +175 -0
  165. package/assets/components/TableRowSelect/index.ts +2 -0
  166. package/assets/components/Tabs/Tabs.module.css +234 -0
  167. package/assets/components/Tabs/Tabs.tsx +426 -0
  168. package/assets/components/Tabs/index.ts +9 -0
  169. package/assets/components/Textarea/Textarea.module.css +318 -0
  170. package/assets/components/Textarea/Textarea.tsx +426 -0
  171. package/assets/components/Textarea/index.ts +7 -0
  172. package/assets/components/ThemeController.tsx +271 -0
  173. package/assets/components/TimePicker/TimePicker.module.css +317 -0
  174. package/assets/components/TimePicker/TimePicker.tsx +821 -0
  175. package/assets/components/TimePicker/index.ts +8 -0
  176. package/assets/components/TimePicker/utils.ts +115 -0
  177. package/assets/components/Toast/Toast.module.css +264 -0
  178. package/assets/components/Toast/Toast.tsx +243 -0
  179. package/assets/components/Toast/index.ts +15 -0
  180. package/assets/components/Toolbar/Toolbar.module.css +72 -0
  181. package/assets/components/Toolbar/Toolbar.tsx +75 -0
  182. package/assets/components/Toolbar/index.ts +2 -0
  183. package/assets/components/Tooltip/Tooltip.module.css +42 -0
  184. package/assets/components/Tooltip/Tooltip.tsx +332 -0
  185. package/assets/components/Tooltip/index.ts +2 -0
  186. package/assets/components/Topbar/Topbar.module.css +26 -0
  187. package/assets/components/Topbar/Topbar.tsx +47 -0
  188. package/assets/components/Topbar/index.ts +2 -0
  189. package/assets/components/_cn.ts +3 -0
  190. package/assets/components/_color.ts +48 -0
  191. package/assets/components/_data.ts +659 -0
  192. package/assets/components/_utils.css +19 -0
  193. package/assets/components/axisOptions.ts +24 -0
  194. package/assets/components/tokens.css +742 -0
  195. package/assets/prompts/GUIDE.md +251 -0
  196. package/assets/prompts/OVERRIDE.md +170 -0
  197. package/assets/prompts/PRINCIPLES.md +130 -0
  198. package/assets/prompts/axes/density.md +38 -0
  199. package/assets/prompts/axes/font.md +36 -0
  200. package/assets/prompts/axes/hue.md +36 -0
  201. package/assets/prompts/axes/icon.md +48 -0
  202. package/assets/prompts/axes/mode.md +50 -0
  203. package/assets/prompts/axes/motion.md +77 -0
  204. package/assets/prompts/axes/palette.md +35 -0
  205. package/assets/prompts/axes/shape.md +33 -0
  206. package/assets/prompts/axes/voice.md +54 -0
  207. package/assets/prompts/components/README.md +84 -0
  208. package/assets/prompts/components/app-frame.md +94 -0
  209. package/assets/prompts/components/attachment-chip.md +46 -0
  210. package/assets/prompts/components/attachment-thumb.md +49 -0
  211. package/assets/prompts/components/avatar.md +86 -0
  212. package/assets/prompts/components/badge.md +104 -0
  213. package/assets/prompts/components/breadcrumb.md +77 -0
  214. package/assets/prompts/components/button.md +99 -0
  215. package/assets/prompts/components/callout.md +81 -0
  216. package/assets/prompts/components/chat-input.md +84 -0
  217. package/assets/prompts/components/checkbox.md +46 -0
  218. package/assets/prompts/components/chip.md +84 -0
  219. package/assets/prompts/components/choice-card.md +127 -0
  220. package/assets/prompts/components/context-menu.md +145 -0
  221. package/assets/prompts/components/date-picker.md +104 -0
  222. package/assets/prompts/components/drawer.md +152 -0
  223. package/assets/prompts/components/empty-state.md +69 -0
  224. package/assets/prompts/components/file-icon.md +73 -0
  225. package/assets/prompts/components/filter-button.md +42 -0
  226. package/assets/prompts/components/form-field.md +74 -0
  227. package/assets/prompts/components/gallery-item.md +158 -0
  228. package/assets/prompts/components/icon-button.md +82 -0
  229. package/assets/prompts/components/icon.md +68 -0
  230. package/assets/prompts/components/input.md +95 -0
  231. package/assets/prompts/components/list-item.md +235 -0
  232. package/assets/prompts/components/menu-item.md +234 -0
  233. package/assets/prompts/components/metric-card.md +132 -0
  234. package/assets/prompts/components/modal.md +131 -0
  235. package/assets/prompts/components/pagination.md +92 -0
  236. package/assets/prompts/components/progress-bar.md +61 -0
  237. package/assets/prompts/components/quotation-bar.md +39 -0
  238. package/assets/prompts/components/radio.md +41 -0
  239. package/assets/prompts/components/select.md +104 -0
  240. package/assets/prompts/components/selectable-card.md +67 -0
  241. package/assets/prompts/components/shortcut.md +76 -0
  242. package/assets/prompts/components/sidebar.md +107 -0
  243. package/assets/prompts/components/slider.md +89 -0
  244. package/assets/prompts/components/split-button.md +71 -0
  245. package/assets/prompts/components/stepper.md +93 -0
  246. package/assets/prompts/components/switch.md +86 -0
  247. package/assets/prompts/components/table-cell.md +87 -0
  248. package/assets/prompts/components/table-header.md +157 -0
  249. package/assets/prompts/components/table-row-edit.md +129 -0
  250. package/assets/prompts/components/table-row-select.md +82 -0
  251. package/assets/prompts/components/table-row.md +156 -0
  252. package/assets/prompts/components/tabs.md +122 -0
  253. package/assets/prompts/components/textarea.md +105 -0
  254. package/assets/prompts/components/toast.md +149 -0
  255. package/assets/prompts/components/tooltip.md +71 -0
  256. package/assets/prompts/components/topbar.md +148 -0
  257. package/assets/prompts/primitives/emerge.md +43 -0
  258. package/assets/prompts/primitives/fade.md +31 -0
  259. package/assets/prompts/primitives/scale.md +32 -0
  260. package/assets/prompts/primitives/scroll.md +75 -0
  261. package/assets/prompts/primitives/slide.md +31 -0
  262. package/assets/prompts/themes/_SCHEMA.md +135 -0
  263. package/assets/prompts/themes/_TEMPLATE.md +73 -0
  264. package/assets/prompts/themes/expressive.md +118 -0
  265. package/assets/prompts/themes/pure.md +87 -0
  266. package/assets/prompts/themes/spare.md +73 -0
  267. package/assets/prompts/tokens/breakpoint.md +29 -0
  268. package/assets/prompts/tokens/color.md +78 -0
  269. package/assets/prompts/tokens/radius.md +33 -0
  270. package/assets/prompts/tokens/shadow.md +35 -0
  271. package/assets/prompts/tokens/spacing.md +86 -0
  272. package/assets/prompts/tokens/typography.md +76 -0
  273. package/assets/prompts/tokens/z-index.md +35 -0
  274. package/assets/public/expressive.png +0 -0
  275. package/assets/public/pure.png +0 -0
  276. package/assets/public/spare.png +0 -0
  277. package/assets/skill/SKILL.md +36 -0
  278. package/bin/ctds.js +76 -0
  279. package/package.json +35 -4
  280. package/src/commands/init.js +215 -0
  281. package/src/commands/update.js +275 -0
  282. package/src/utils/assets.js +78 -0
  283. package/src/utils/deps.js +51 -0
  284. package/src/utils/detect.js +47 -0
  285. package/src/utils/diff.js +52 -0
  286. package/src/utils/hash.js +16 -0
  287. package/src/utils/log.js +41 -0
  288. package/src/utils/meta.js +73 -0
  289. package/src/utils/prompt.js +28 -0
@@ -0,0 +1,437 @@
1
+ /* Base ─────────────────────────────────────────────────────── */
2
+ /* MenuItem은 "메뉴/리스트 안 개별 행"의 최소 단위.
3
+ Dropdown · Context Menu · Sidebar · Command Palette · List 아이템이 이 컴포넌트를 공유한다.
4
+ root는 <div role="menuitem"> — 자식으로 <button>(IconButton 등)을 자유롭게 넣기 위해서
5
+ nested button 금지를 피한 것. Enter/Space 키보드 핸들링은 컴포넌트가 직접 처리.
6
+
7
+ ▸ 색상 훅 (부모 컨테이너에서 오버라이드 가능)
8
+ --menu-fg : 기본 라벨 색 (default: --fg)
9
+ --menu-active-bg : active 상태 배경 (default: --hover, fill 스타일에만 적용)
10
+ --menu-active-fg : active 상태 라벨·아이콘 색 (default: --fg-strong)
11
+
12
+ 예) 사이드바 전체를 연한 라벨 + primary active로 만들려면
13
+ .my-nav { --menu-fg: var(--fg-muted); --menu-active-bg: var(--primary); --menu-active-fg: var(--bg); } */
14
+ .root {
15
+ display: grid;
16
+ grid-template-columns: auto 1fr auto;
17
+ align-items: center;
18
+ width: 100%;
19
+ gap: var(--space-2);
20
+ font-family: var(--label-font);
21
+ font-weight: var(--font-weight-regular);
22
+ line-height: var(--lh-tight);
23
+ letter-spacing: var(--label-tracking);
24
+ color: var(--menu-fg, var(--fg));
25
+ background: transparent;
26
+ border: 1px solid transparent;
27
+ cursor: pointer;
28
+ user-select: none;
29
+ text-align: start;
30
+ white-space: nowrap;
31
+ transition: var(--transition-colors), var(--transition-ring);
32
+ }
33
+
34
+ /* leading 슬롯이 없을 땐 그리드를 2열로 축소 — 라벨이 왼쪽 끝까지 흐름 */
35
+ .root:not(.hasLeading) {
36
+ grid-template-columns: 1fr auto;
37
+ }
38
+
39
+ /* Collapsed (icon-only 정사각) ─────────────────────────────
40
+ Sidebar rail 등에서 접힐 때. leading 아이콘 하나만 남기고 정사각 히트박스로 축소.
41
+ - grid → inline-flex center: 라벨/trailing이 사라진 상태에서 아이콘 정중앙 정렬
42
+ - width = min-height: 완전한 정사각. 시각 폭이 rail 폭과 정합.
43
+ - padding-inline·gap 제거: 정사각 안에 아이콘 하나만 놓이도록.
44
+ - transition: width는 부모(Sidebar) 애니메이션과 짝지어져 자연스럽게. */
45
+ .root[data-collapsed] {
46
+ display: inline-flex;
47
+ justify-content: center;
48
+ gap: 0;
49
+ padding-inline: 0;
50
+ width: var(--menu-item-h-md);
51
+ }
52
+ .sm.root[data-collapsed] {
53
+ width: var(--menu-item-h-sm);
54
+ }
55
+
56
+ /* 첫 글자 fallback — 아이콘이 없을 때 라벨 첫 글자를 아이콘 크기 자리에 얹는다.
57
+ 폰트 굵기·크기를 라벨 톤 그대로 유지(uppercase 금지 규약). */
58
+ .collapsedChar {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ width: var(--menu-icon-size);
63
+ height: var(--menu-icon-size);
64
+ font-size: var(--menu-icon-size);
65
+ line-height: 1;
66
+ font-weight: var(--font-weight-medium);
67
+ }
68
+
69
+ /* focus/hover: 키보드 이동(:focus-visible) + Radix [data-highlighted] + 마우스 hover 모두 커버 */
70
+ .root:focus-visible,
71
+ .root[data-highlighted]:not([data-disabled]) {
72
+ outline: none;
73
+ background: var(--hover);
74
+ color: var(--fg-strong);
75
+ }
76
+
77
+ .root:hover:not([aria-disabled="true"]) {
78
+ background: var(--hover);
79
+ color: var(--fg-strong);
80
+ }
81
+
82
+ .root:active:not([aria-disabled="true"]) {
83
+ background: var(--hover);
84
+ }
85
+
86
+ .root[aria-disabled="true"] {
87
+ cursor: not-allowed;
88
+ color: var(--fg-disabled);
89
+ }
90
+
91
+ /* Sizes ──────────────────────────────────────────────────────── */
92
+ /* size별로 4개 변수 정의:
93
+ --menu-icon-size : 아이콘 표시 크기 (Icon만 있을 때의 시각 폭)
94
+ --menu-hitbox-size : IconButton의 정사각 크기. IconButton 기본 size와 무관하게
95
+ MenuItem 안에선 이 값으로 강제 (아이콘 폭 + 약간의 hover 여유).
96
+ hover 배경이 라벨 라인을 크게 벗어나지 않도록 좁게 유지.
97
+ --menu-slot-gap : leading/trailing 슬롯 내부 아이템 간 간격
98
+ --menu-hover-ease : hover-only 액션 등장 트랜지션 duration */
99
+ .sm {
100
+ min-height: var(--menu-item-h-sm);
101
+ padding-inline: var(--space-2);
102
+ gap: var(--space-1_5);
103
+ font-size: var(--label-sm-size);
104
+ letter-spacing: var(--label-tracking);
105
+ font-weight: var(--font-weight-regular);
106
+ border-radius: var(--radius-xs);
107
+ --menu-icon-size: 14px;
108
+ --menu-hitbox-size: var(--control-h-xs);
109
+ --menu-slot-gap: var(--space-1_5);
110
+ }
111
+ .md {
112
+ min-height: var(--menu-item-h-md);
113
+ padding-inline: var(--space-2);
114
+ gap: var(--space-2);
115
+ font-size: var(--label-md-size);
116
+ letter-spacing: var(--label-tracking);
117
+ border-radius: var(--radius-sm);
118
+ --menu-icon-size: 16px;
119
+ --menu-hitbox-size: var(--control-h-xs);
120
+ --menu-slot-gap: var(--space-1_5);
121
+ }
122
+
123
+ /* Slots ──────────────────────────────────────────────────────── */
124
+ /* leading/trailing 컨테이너: 개별 아이템의 margin-inline-start로 간격 관리.
125
+ gap을 쓰지 않는 이유는 hover-only 아이템의 width 전환 시 gap이 collapse되지 않아
126
+ 자연스럽지 않기 때문. */
127
+ .leading,
128
+ .trailing {
129
+ display: inline-flex;
130
+ align-items: center;
131
+ flex-shrink: 0;
132
+ }
133
+ .leading {
134
+ color: var(--fg-muted);
135
+ }
136
+ .trailing {
137
+ color: var(--fg-subtle);
138
+ /* trailing 자체는 라벨과 자연스러운 여백을 두되, 내부 첫 아이템은 margin 없음 */
139
+ margin-inline-start: var(--space-4);
140
+ min-width: 0;
141
+ }
142
+
143
+ /* 슬롯 아이템 기본: 콘텐츠 크기 그대로 (배지·메타 텍스트·kbd 등 자유 콘텐츠 대응).
144
+ 아이콘·IconButton만 정사각 정렬 필요 → 아래 :has() 규칙으로 강제. */
145
+ .leadingItem,
146
+ .trailingItem {
147
+ --half-overflow: calc((var(--menu-hitbox-size) - var(--menu-icon-size)) / 2);
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ flex-shrink: 0;
152
+ }
153
+
154
+ /* 아이콘(svg 직접 자식) 또는 IconButton(button 자식)이 들어온 슬롯 → 아이콘 크기 정사각.
155
+ → Icon만 넣은 슬롯과 IconButton 슬롯의 아이콘 중심선이 자동 정렬됨. */
156
+ .leadingItem:has(> svg),
157
+ .trailingItem:has(> svg),
158
+ .leadingItem:has(> button),
159
+ .trailingItem:has(> button) {
160
+ width: var(--menu-icon-size);
161
+ height: var(--menu-icon-size);
162
+ }
163
+
164
+ /* IconButton(button)은 --menu-hitbox-size 크기로 삐져나가되 negative margin으로
165
+ 부모 슬롯의 시각 폭은 --menu-icon-size로 유지, hitbox만 되찾음.
166
+ IconButton 자체가 --control-h-sm(28) 크기로 오는 걸 MenuItem 안에선 hitbox 토큰으로
167
+ 조여준다 — hover 배경이 라벨 라인을 너무 벗어나지 않도록. */
168
+ .leadingItem > button,
169
+ .trailingItem > button {
170
+ width: var(--menu-hitbox-size);
171
+ height: var(--menu-hitbox-size);
172
+ margin: calc(var(--half-overflow) * -1);
173
+ }
174
+
175
+ /* 아이템 사이 간격 — 두 번째 아이템부터 적용.
176
+ IconButton hitbox가 이웃과 겹치지 않도록 gap은 half-overflow 이상으로 확장. */
177
+ .leadingItem + .leadingItem,
178
+ .trailingItem + .trailingItem {
179
+ margin-inline-start: max(var(--menu-slot-gap), var(--half-overflow));
180
+ }
181
+
182
+ /* label */
183
+ .label {
184
+ display: inline-flex;
185
+ align-items: center;
186
+ min-width: 0;
187
+ overflow: hidden;
188
+ text-overflow: ellipsis;
189
+ }
190
+
191
+ /* Shortcut ────────────────────────────────────────────────────
192
+ trailing 슬롯에 shortcut prop이 넘어오면 별도 Shortcut 컴포넌트가 렌더링됨.
193
+ 슬롯 정사각 규칙(.trailingItem:has(> svg | > button))에 걸리지 않으므로
194
+ Shortcut의 콘텐츠 폭 그대로 자연스럽게 배치. */
195
+
196
+ /* hoverOnly ─────────────────────────────────────────────────── */
197
+ /* 기본 상태: width 0 + margin 0 + opacity 0 → 자리를 아예 안 차지.
198
+ MenuItem hover/focus/focus-within 시 width가 --menu-icon-size로 열리며 밀려나옴.
199
+ width 트랜지션이 자연스러움을 담당하고, opacity가 페이드 인 담당.
200
+
201
+ ⚠️ specificity 주의: `.leadingItem:has(> button)` 같은 형제 규칙(0,2,0)이 width를
202
+ --menu-icon-size로 강제하려 하므로, hoverOnly 규칙도 같은 specificity 이상이 되도록
203
+ `.leadingItem.hoverOnly` / `.trailingItem.hoverOnly` 조합 셀렉터로 작성 (0,2,0). */
204
+ .leadingItem.hoverOnly,
205
+ .trailingItem.hoverOnly {
206
+ width: 0;
207
+ margin-inline-start: 0;
208
+ opacity: 0;
209
+ pointer-events: none;
210
+ overflow: hidden;
211
+ transition:
212
+ width var(--duration-fast) var(--ease-out),
213
+ margin-inline-start var(--duration-fast) var(--ease-out),
214
+ opacity var(--duration-fast) var(--ease-out);
215
+ }
216
+
217
+ .root:hover .leadingItem.hoverOnly,
218
+ .root:hover .trailingItem.hoverOnly,
219
+ .root:focus-visible .leadingItem.hoverOnly,
220
+ .root:focus-visible .trailingItem.hoverOnly,
221
+ .root:focus-within .leadingItem.hoverOnly,
222
+ .root:focus-within .trailingItem.hoverOnly {
223
+ width: var(--menu-icon-size);
224
+ margin-inline-start: max(var(--menu-slot-gap), var(--half-overflow));
225
+ opacity: 1;
226
+ pointer-events: auto;
227
+ }
228
+
229
+ /* 슬롯 안 첫 번째 hoverOnly 아이템은 이웃과의 gap이 없어야 하므로 margin 초기화 대상.
230
+ .leadingItem + .leadingItem 규칙이 이미 첫 아이템은 제외하지만,
231
+ 두 번째 이상의 아이템이 hoverOnly일 때는 hover 시 margin이 살아나야 함.
232
+ 위 :hover 셀렉터가 이걸 처리. */
233
+
234
+ /* 터치 디바이스: hover 없음 → 항상 노출 (접근성 폴백) */
235
+ @media (hover: none) {
236
+ .leadingItem.hoverOnly,
237
+ .trailingItem.hoverOnly {
238
+ width: var(--menu-icon-size);
239
+ margin-inline-start: max(var(--menu-slot-gap), var(--half-overflow));
240
+ opacity: 1;
241
+ pointer-events: auto;
242
+ }
243
+ }
244
+
245
+ /* Variants ───────────────────────────────────────────────────── */
246
+ .default {
247
+ /* no-op — base가 곧 default */
248
+ }
249
+
250
+ /* destructive — 평상시 fg (위험을 감추다가) hover 시 빨강 톤.
251
+ Button의 destructive-ghost와 같은 원리. */
252
+ .destructive .leading,
253
+ .destructive .trailing {
254
+ color: var(--fg-muted);
255
+ }
256
+ .destructive:hover:not([aria-disabled="true"]),
257
+ .destructive:focus-visible,
258
+ .destructive[data-highlighted]:not([data-disabled]) {
259
+ background: var(--destructive-subtle);
260
+ color: var(--destructive);
261
+ }
262
+ .destructive:hover:not([aria-disabled="true"]) .leading,
263
+ .destructive:hover:not([aria-disabled="true"]) .trailing,
264
+ .destructive:focus-visible .leading,
265
+ .destructive:focus-visible .trailing,
266
+ .destructive[data-highlighted]:not([data-disabled]) .leading,
267
+ .destructive[data-highlighted]:not([data-disabled]) .trailing {
268
+ color: var(--destructive);
269
+ }
270
+
271
+ /* section — 그룹 헤더 라벨. 본문 아이템보다 한 단계 작고 더 연하게.
272
+ 사이드바·컨텍스트 메뉴·드롭다운의 섹션 제목 자리에 공통.
273
+ role="button"(collapsible)인 경우엔 hover/press가 살아난다.
274
+
275
+ - font-size: sm→--text-xs(13), md→--text-sm(14). 각 사이즈에서 본문 대비 한 스텝 아래.
276
+ - color: --fg-subtle (본문 --fg 대비 뒤로 물러남).
277
+ - margin: 위 space-2, 아래 space-0_5 — 다음 아이템과 살짝만 떨어져 그룹 결속 유지. */
278
+ .section {
279
+ cursor: default;
280
+ background: transparent;
281
+ color: var(--fg-subtle);
282
+ font-weight: var(--font-weight-medium);
283
+ min-height: 0;
284
+ margin-top: var(--space-2);
285
+ margin-bottom: var(--space-1);
286
+ user-select: none;
287
+ }
288
+
289
+ /* first-child가 아닐 때는 이전 아이템과의 시각 분리를 위해 상단 마진을 +space-0_5(2px) 더 얹는다.
290
+ 섹션 헤더가 리스트 첫 자리면 컨테이너 padding으로 충분하므로 기본값(space-2) 유지. */
291
+ .section:not(:first-child) {
292
+ margin-top: calc(var(--space-2) + var(--space-0_5));
293
+ }
294
+
295
+ /* size별 폰트 사이즈 override — .sm/.md 규칙보다 뒤에 오므로 특수성 동일에서 승리.
296
+ .sm.section / .md.section 조합으로 명시성을 한 단계 올려 안정성 확보. */
297
+ .sm.section {
298
+ font-size: var(--text-xs);
299
+ }
300
+ .md.section {
301
+ font-size: var(--label-sm-size);
302
+ letter-spacing: var(--label-tracking);
303
+ }
304
+
305
+ /* 정적(presentation) 섹션: hover/focus에서도 변화 없음.
306
+ base .section의 color 값과 완전히 동일해야 hover 시 톤이 진해지지 않음. */
307
+ .section[role="presentation"]:hover,
308
+ .section[role="presentation"]:focus-visible {
309
+ background: transparent;
310
+ color: var(--fg-subtle);
311
+ cursor: default;
312
+ }
313
+
314
+ /* Collapsible 섹션: role="button". 배경/링 효과 없이 텍스트 색만 한 단계 진해진다 —
315
+ 섹션 헤더는 계층 라벨이라 아이템처럼 표면(배경/보더)이 들어오면 시각이 과해진다.
316
+ base .root:hover의 background/color도 여기서 덮어 무효화. */
317
+ .section[role="button"] {
318
+ cursor: pointer;
319
+ }
320
+
321
+ .section[role="button"]:hover:not([aria-disabled="true"]),
322
+ .section[role="button"]:focus-visible,
323
+ .section[role="button"]:active:not([aria-disabled="true"]) {
324
+ background: transparent;
325
+ color: var(--fg);
326
+ }
327
+
328
+ /* caret 회전 — open 상태면 down(0deg), 닫힌 상태면 -90deg.
329
+ label 텍스트 바로 오른쪽에 인라인으로 붙는다.
330
+ duration은 자식 패널과 동일한 --duration-base로 맞춰 caret과 콘텐츠가 한 몸으로 움직인다. */
331
+ .sectionCaret {
332
+ margin-inline-start: var(--space-1);
333
+ transition: transform var(--duration-base) var(--ease-emphasized);
334
+ transform: rotate(-90deg);
335
+ flex-shrink: 0;
336
+ }
337
+
338
+ .sectionCaret[data-open] {
339
+ transform: rotate(0deg);
340
+ }
341
+
342
+ /* Section panel ─────────────────────────────────────────
343
+ MenuItem.Section이 감싸는 자식 컨테이너. grid-template-rows 0fr↔1fr 트릭으로
344
+ height:auto를 애니메이션한다 (JS 높이 측정 불필요).
345
+ - 바깥(.sectionPanel): grid + overflow hidden + rows 트랜지션 담당.
346
+ - 안쪽(.sectionPanelInner): min-height 0으로 grid row에 갇혀 실제 높이를 결정.
347
+ opacity는 짧은 fade로 콘텐츠 등장/사라짐을 부드럽게. reduced-motion은
348
+ globals.css에서 transition-duration을 강제로 지워 즉시 상태 전환. */
349
+ .sectionPanel {
350
+ display: grid;
351
+ grid-template-rows: 0fr;
352
+ overflow: hidden;
353
+ transition:
354
+ grid-template-rows var(--duration-base) var(--ease-emphasized),
355
+ opacity var(--duration-fast) var(--ease-out);
356
+ opacity: 0;
357
+ }
358
+
359
+ .sectionPanel[data-open] {
360
+ grid-template-rows: 1fr;
361
+ opacity: 1;
362
+ }
363
+
364
+ .sectionPanelInner {
365
+ min-height: 0;
366
+ }
367
+
368
+ /* Active — Sidebar 등에서 현재 페이지/선택 아이템.
369
+ 두 가지 강조 스타일 지원 (Shell 프리셋 축):
370
+ - fill (기본): --menu-active-bg 배경 + --menu-active-fg 텍스트. Notion·Docs.
371
+ - accent-bar: 배경 투명 + 좌측 세로 바(--menu-active-fg) + --menu-active-fg 텍스트. Linear·Slack.
372
+ 결정 우선순위: MenuItem의 activeStyle prop > 부모 컨테이너의 --menu-active-style > fill.
373
+ 컨테이너 단위 통일이 원칙이라 대부분 부모에서 --menu-active-style만 지정해도 충분.
374
+ accent-bar 스타일은 원래 primary가 기본이었으므로 --menu-active-fg가 없으면 --primary로 폴백. */
375
+
376
+ /* 공통: strong text + medium weight */
377
+ .active,
378
+ .root[data-active] {
379
+ color: var(--menu-active-fg, var(--fg-strong));
380
+ font-weight: var(--font-weight-medium);
381
+ position: relative;
382
+ }
383
+ .active .leading,
384
+ .root[data-active] .leading {
385
+ color: var(--menu-active-fg, var(--fg-strong));
386
+ }
387
+
388
+ /* FILL 스타일 — 기본 (아이템에 activeStyle 미지정 + 컨테이너에도 지정 없음) */
389
+ .root[data-active]:not([data-active-style]),
390
+ .root[data-active][data-active-style="fill"] {
391
+ background: var(--menu-active-bg, var(--hover));
392
+ }
393
+
394
+ /* ACCENT-BAR 스타일 — 좌측 세로 바(primary) + 강조 텍스트.
395
+ ::before로 바를 그린다. border-radius가 있는 부모라 overflow 잘림 방지 위해
396
+ 바가 아이템 padding 안쪽에 붙되 살짝 튀어나오게 좌측 좌표를 음수로.
397
+ accent-bar 기본 강조색은 primary (fill과 폴백 값이 다름). */
398
+ .root[data-active][data-active-style="accent-bar"] {
399
+ background: transparent;
400
+ color: var(--menu-active-fg, var(--primary));
401
+ }
402
+ .root[data-active][data-active-style="accent-bar"] .leading {
403
+ color: var(--menu-active-fg, var(--primary));
404
+ }
405
+ .root[data-active][data-active-style="accent-bar"]::before {
406
+ content: "";
407
+ position: absolute;
408
+ left: 0;
409
+ top: 50%;
410
+ transform: translateY(-50%);
411
+ width: var(--space-0_5);
412
+ height: 60%;
413
+ background: var(--menu-active-fg, var(--primary));
414
+ border-radius: var(--radius-full);
415
+ }
416
+
417
+ /* Checked (Radix CheckboxItem / RadioItem 대응) ────────────── */
418
+ /* leading 자리 맨 앞에 checkmark가 이미 삽입되어 있으므로 색만 강조. */
419
+ .root[data-checked] .leading {
420
+ color: var(--menu-active-fg, var(--fg-strong));
421
+ }
422
+
423
+ /* Selected (Radix Select.Item) ────────────────────────────────
424
+ Radix Select.Item이 선택된 상태에 data-state="checked"를 부여 → asChild로 MenuItem
425
+ 루트에 전파. Select는 아이템 안에 checkmark를 얹지 않고 배경 하이라이트로만
426
+ 선택을 표시하는 규약이라, data-active(fill 스타일)와 동일한 시각을 재사용.
427
+ role="option"으로 스코프해 ContextMenu의 CheckboxItem/RadioItem(role="menuitemcheckbox"/
428
+ "menuitemradio") 시각과 섞이지 않게 한다 — 그쪽은 checkmark/dot으로 이미 선택을 표시하니
429
+ 추가 배경이 얹히면 시각이 과해진다. */
430
+ .root[role="option"][data-state="checked"] {
431
+ background: var(--menu-active-bg, var(--hover));
432
+ color: var(--menu-active-fg, var(--fg-strong));
433
+ font-weight: var(--font-weight-medium);
434
+ }
435
+ .root[role="option"][data-state="checked"] .leading {
436
+ color: var(--menu-active-fg, var(--fg-strong));
437
+ }