@leviosa-ai/detail-page-editor 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 (190) hide show
  1. package/components/cardnews/color-input.tsx +380 -0
  2. package/components/detail-page/ai-generate-panel.tsx +763 -0
  3. package/components/detail-page/annotation-canvas.tsx +860 -0
  4. package/components/detail-page/annotation-dialog.tsx +397 -0
  5. package/components/detail-page/bubble-tail-overlay.tsx +209 -0
  6. package/components/detail-page/canvas-context-menu.tsx +234 -0
  7. package/components/detail-page/canvas-observer.tsx +68 -0
  8. package/components/detail-page/canvas-overlay-host.tsx +44 -0
  9. package/components/detail-page/canvas-store-facade.ts +51 -0
  10. package/components/detail-page/chart-inspector.tsx +531 -0
  11. package/components/detail-page/design-reference-picker.tsx +383 -0
  12. package/components/detail-page/detail-page-basic-shapes.tsx +180 -0
  13. package/components/detail-page/detail-page-brand-gifs-panel.tsx +261 -0
  14. package/components/detail-page/detail-page-brand-kit-panel.tsx +77 -0
  15. package/components/detail-page/detail-page-brand-panel-header.tsx +44 -0
  16. package/components/detail-page/detail-page-charts-panel.tsx +104 -0
  17. package/components/detail-page/detail-page-decorations-panel.tsx +125 -0
  18. package/components/detail-page/detail-page-download-dialog.tsx +524 -0
  19. package/components/detail-page/detail-page-editor.tsx +974 -0
  20. package/components/detail-page/detail-page-elements-panel.tsx +117 -0
  21. package/components/detail-page/detail-page-font-picker.tsx +231 -0
  22. package/components/detail-page/detail-page-history-buttons.tsx +87 -0
  23. package/components/detail-page/detail-page-host-context.tsx +465 -0
  24. package/components/detail-page/detail-page-icons-panel.tsx +251 -0
  25. package/components/detail-page/detail-page-konva-json-preview.tsx +474 -0
  26. package/components/detail-page/detail-page-layers-panel.tsx +618 -0
  27. package/components/detail-page/detail-page-my-images-panel.tsx +179 -0
  28. package/components/detail-page/detail-page-my-shapes-panel.tsx +174 -0
  29. package/components/detail-page/detail-page-page-toolbar.tsx +171 -0
  30. package/components/detail-page/detail-page-pages-panel.tsx +299 -0
  31. package/components/detail-page/detail-page-pages-timeline.tsx +79 -0
  32. package/components/detail-page/detail-page-photos-panel.tsx +257 -0
  33. package/components/detail-page/detail-page-properties-panel.tsx +2680 -0
  34. package/components/detail-page/detail-page-qr-panel.tsx +137 -0
  35. package/components/detail-page/detail-page-references-panel.tsx +166 -0
  36. package/components/detail-page/detail-page-selection.ts +44 -0
  37. package/components/detail-page/detail-page-shapes-panel.tsx +16 -0
  38. package/components/detail-page/detail-page-sidebar-sections.tsx +324 -0
  39. package/components/detail-page/detail-page-tables-panel.tsx +106 -0
  40. package/components/detail-page/detail-page-text-panel.tsx +179 -0
  41. package/components/detail-page/detail-page-thumbnail-bus.ts +42 -0
  42. package/components/detail-page/edit-quota-ui.tsx +150 -0
  43. package/components/detail-page/editor-history.ts +56 -0
  44. package/components/detail-page/editor-hotkeys.tsx +165 -0
  45. package/components/detail-page/element-recents-strip.tsx +105 -0
  46. package/components/detail-page/element-rects.ts +82 -0
  47. package/components/detail-page/fill-control.tsx +261 -0
  48. package/components/detail-page/find-replace-panel.tsx +252 -0
  49. package/components/detail-page/generation-progress-fill.tsx +57 -0
  50. package/components/detail-page/gif-animator.tsx +184 -0
  51. package/components/detail-page/gif-effect-picker.tsx +136 -0
  52. package/components/detail-page/group-drill-in.tsx +219 -0
  53. package/components/detail-page/group-prompt-edit-panel.tsx +154 -0
  54. package/components/detail-page/hover-highlight-overlay.tsx +111 -0
  55. package/components/detail-page/hovered-layer.ts +34 -0
  56. package/components/detail-page/inspector-controls.tsx +175 -0
  57. package/components/detail-page/leviosa-canvas-workspace.tsx +313 -0
  58. package/components/detail-page/nested-selection-overlay.tsx +364 -0
  59. package/components/detail-page/panel-search-input.tsx +49 -0
  60. package/components/detail-page/prompt-edit-panel.tsx +146 -0
  61. package/components/detail-page/section-height-handle.tsx +276 -0
  62. package/components/detail-page/section-reauthor-controller.tsx +210 -0
  63. package/components/detail-page/spec-prompt-edit-panel.tsx +178 -0
  64. package/components/detail-page/spec-resize-absorber.tsx +149 -0
  65. package/components/detail-page/svg-prompt-edit-panel.tsx +139 -0
  66. package/components/detail-page/table-canvas-overlay.tsx +707 -0
  67. package/components/detail-page/table-inspector.tsx +586 -0
  68. package/components/ui/button.tsx +64 -0
  69. package/components/ui/command.tsx +175 -0
  70. package/components/ui/dialog.tsx +167 -0
  71. package/components/ui/label.tsx +24 -0
  72. package/components/ui/select.tsx +190 -0
  73. package/components/ui/slider.tsx +63 -0
  74. package/components/ui/switch.tsx +35 -0
  75. package/components/ui/tooltip.tsx +57 -0
  76. package/config/detail-page-bundle-font-tags.json +39 -0
  77. package/config/detail-page-fonts.json +373 -0
  78. package/index.ts +24 -0
  79. package/lib/cardnews/font-loader.ts +306 -0
  80. package/lib/detail-page/animation-sniff.ts +101 -0
  81. package/lib/detail-page/asset-bytes-url.ts +70 -0
  82. package/lib/detail-page/basic-shapes.ts +603 -0
  83. package/lib/detail-page/canvas-menu.ts +182 -0
  84. package/lib/detail-page/chart/defaults.ts +283 -0
  85. package/lib/detail-page/chart/format.ts +66 -0
  86. package/lib/detail-page/chart/normalize.ts +149 -0
  87. package/lib/detail-page/chart/palette.ts +85 -0
  88. package/lib/detail-page/chart/paste.ts +82 -0
  89. package/lib/detail-page/chart/preview.ts +78 -0
  90. package/lib/detail-page/chart/render.ts +188 -0
  91. package/lib/detail-page/chart/renderers/bar-h.ts +103 -0
  92. package/lib/detail-page/chart/renderers/bar-v.ts +213 -0
  93. package/lib/detail-page/chart/renderers/donut.ts +186 -0
  94. package/lib/detail-page/chart/renderers/gauge.ts +96 -0
  95. package/lib/detail-page/chart/renderers/line.ts +143 -0
  96. package/lib/detail-page/chart/renderers/shared.ts +263 -0
  97. package/lib/detail-page/chart/renderers/stack.ts +121 -0
  98. package/lib/detail-page/chart/sync.ts +92 -0
  99. package/lib/detail-page/chart/types.ts +145 -0
  100. package/lib/detail-page/css-color.ts +34 -0
  101. package/lib/detail-page/data-gif-payload.ts +167 -0
  102. package/lib/detail-page/design-reference.ts +244 -0
  103. package/lib/detail-page/distribute.ts +89 -0
  104. package/lib/detail-page/editor-routes.ts +10 -0
  105. package/lib/detail-page/element-recents.ts +167 -0
  106. package/lib/detail-page/find-replace.ts +120 -0
  107. package/lib/detail-page/format-painter.ts +159 -0
  108. package/lib/detail-page/gif-frames.ts +216 -0
  109. package/lib/detail-page/gif-timeline.ts +65 -0
  110. package/lib/detail-page/group-action.ts +62 -0
  111. package/lib/detail-page/hangul-roman.ts +58 -0
  112. package/lib/detail-page/icon-curated.ts +290 -0
  113. package/lib/detail-page/icon-fuzzy.ts +250 -0
  114. package/lib/detail-page/icon-keywords.generated.ts +3766 -0
  115. package/lib/detail-page/icon-keywords.ko.ts +524 -0
  116. package/lib/detail-page/icon-search.ts +257 -0
  117. package/lib/detail-page/icons.ts +67 -0
  118. package/lib/detail-page/image-credit.ts +121 -0
  119. package/lib/detail-page/image-data-uri.ts +29 -0
  120. package/lib/detail-page/insert-image.ts +75 -0
  121. package/lib/detail-page/insert-shape.ts +49 -0
  122. package/lib/detail-page/korean-dictionary.ts +187 -0
  123. package/lib/detail-page/layer-move.ts +257 -0
  124. package/lib/detail-page/new-product-draft-store.ts +105 -0
  125. package/lib/detail-page/new-product-draft.ts +283 -0
  126. package/lib/detail-page/personal-images-refresh.ts +30 -0
  127. package/lib/detail-page/product-source.ts +587 -0
  128. package/lib/detail-page/qr-code.ts +214 -0
  129. package/lib/detail-page/reference-image.ts +103 -0
  130. package/lib/detail-page/reference-upload.ts +181 -0
  131. package/lib/detail-page/replace-page.ts +35 -0
  132. package/lib/detail-page/replace-with-gif.ts +251 -0
  133. package/lib/detail-page/section-height.ts +131 -0
  134. package/lib/detail-page/section-reauthor-bus.ts +61 -0
  135. package/lib/detail-page/shape-to-image.ts +219 -0
  136. package/lib/detail-page/spec-group/clipboard.ts +35 -0
  137. package/lib/detail-page/spec-group/sync.ts +377 -0
  138. package/lib/detail-page/spec-group/text-metrics.ts +48 -0
  139. package/lib/detail-page/stock-photos.ts +88 -0
  140. package/lib/detail-page/svg-colors.ts +74 -0
  141. package/lib/detail-page/table/defaults.ts +344 -0
  142. package/lib/detail-page/table/edit.ts +312 -0
  143. package/lib/detail-page/table/harvest.ts +103 -0
  144. package/lib/detail-page/table/normalize.ts +125 -0
  145. package/lib/detail-page/table/paste.ts +49 -0
  146. package/lib/detail-page/table/preview.ts +66 -0
  147. package/lib/detail-page/table/render.ts +388 -0
  148. package/lib/detail-page/table/sync.ts +107 -0
  149. package/lib/detail-page/table/types.ts +102 -0
  150. package/lib/detail-page/text-gif-layout.ts +245 -0
  151. package/lib/detail-page/text-gif-payload.ts +67 -0
  152. package/lib/detail-page/text-presets.ts +230 -0
  153. package/lib/detail-page/use-fake-progress.ts +67 -0
  154. package/lib/detail-page/z-order.ts +82 -0
  155. package/lib/detail-page-canvas/bubble-path.ts +190 -0
  156. package/lib/detail-page-canvas/canvas-key.ts +22 -0
  157. package/lib/detail-page-canvas/clip-rect.ts +65 -0
  158. package/lib/detail-page-canvas/custom-props-adapter.ts +219 -0
  159. package/lib/detail-page-canvas/editor-fonts.ts +161 -0
  160. package/lib/detail-page-canvas/export/ai.ts +413 -0
  161. package/lib/detail-page-canvas/export/color.ts +35 -0
  162. package/lib/detail-page-canvas/export/document-model.ts +78 -0
  163. package/lib/detail-page-canvas/export/export-files.ts +290 -0
  164. package/lib/detail-page-canvas/export/font-subset.ts +264 -0
  165. package/lib/detail-page-canvas/export/gif-export.ts +310 -0
  166. package/lib/detail-page-canvas/export/gif-plan.ts +95 -0
  167. package/lib/detail-page-canvas/export/mp4-encode.ts +158 -0
  168. package/lib/detail-page-canvas/export/mp4-support.ts +22 -0
  169. package/lib/detail-page-canvas/export/pdf/geometry.ts +309 -0
  170. package/lib/detail-page-canvas/export/pdf/inline-svg.ts +399 -0
  171. package/lib/detail-page-canvas/export/pdf/resources.ts +443 -0
  172. package/lib/detail-page-canvas/export/pdf/text-ops.ts +81 -0
  173. package/lib/detail-page-canvas/export/pdf/writer.ts +109 -0
  174. package/lib/detail-page-canvas/export/psd.ts +354 -0
  175. package/lib/detail-page-canvas/export/raster.ts +255 -0
  176. package/lib/detail-page-canvas/export/svg.ts +479 -0
  177. package/lib/detail-page-canvas/export/text-layout.ts +142 -0
  178. package/lib/detail-page-canvas/font-catalog.ts +177 -0
  179. package/lib/detail-page-canvas/font-tags.ts +47 -0
  180. package/lib/detail-page-canvas/gif-web-fonts.ts +90 -0
  181. package/lib/detail-page-canvas/konva-fallback.ts +477 -0
  182. package/lib/detail-page-canvas/text-background-plan.ts +124 -0
  183. package/lib/detail-page-canvas/text-highlight-bands.ts +127 -0
  184. package/lib/utils.ts +10 -0
  185. package/package.json +68 -0
  186. package/types/commerce.ts +171 -0
  187. package/types/detail-page-api.ts +200 -0
  188. package/types/detail-page-canvas.ts +20 -0
  189. package/types/fontkit.d.ts +42 -0
  190. package/types/gifenc.d.ts +51 -0
@@ -0,0 +1,524 @@
1
+ /**
2
+ * 아이콘 검색용 한국어 → 영어 키워드 사전.
3
+ *
4
+ * 아이콘 제공처(Iconify)는 **아이콘 이름과 별칭이 전부 영어**다. 스톡 사진은 `locale=ko-KR`을
5
+ * 넘길 데라도 있었지만(`src/app/api/stock-photos/route.ts`) 여기엔 그런 것이 없다. 그래서
6
+ * "배송"을 그대로 넘기면 0건이다 — 이 사전이 없으면 기능 자체가 안 쓰인다.
7
+ *
8
+ * 값은 **순서 있는 배열**이다. 앞이 1순위고, 결과가 모자랄 때만 뒤를 덧댄다(프록시가
9
+ * 그렇게 쓴다). 그래서 "가장 그럴듯한 하나"를 맨 앞에 둔다.
10
+ *
11
+ * 수록 기준은 **상세페이지에서 실제로 찍히는 말**이다. 일반 한국어 사전이 아니다.
12
+ * 여기 없는 말은 자동 생성분(`icon-keywords.generated.ts`)이 받고, 그것도 없으면
13
+ * 국립국어원에 한 번 물어본다(`korean-dictionary.ts`).
14
+ */
15
+
16
+ import { ICON_KEYWORDS_GENERATED } from "./icon-keywords.generated";
17
+
18
+ export const ICON_KEYWORDS_KO: Readonly<Record<string, readonly string[]>> = {
19
+ // ── 배송·물류 ───────────────────────────────────────────────────────────────
20
+ 배송: ["truck", "package", "delivery"],
21
+ 무료배송: ["truck", "gift", "package"],
22
+ 당일발송: ["truck-fast", "clock", "package"],
23
+ 익일배송: ["truck-fast", "calendar", "package"],
24
+ 로켓배송: ["rocket", "truck-fast"],
25
+ 택배: ["package", "truck", "box"],
26
+ 발송: ["send", "package", "truck"],
27
+ 출고: ["package-export", "truck", "box"],
28
+ 입고: ["package-import", "box", "warehouse"],
29
+ 포장: ["package", "box", "gift"],
30
+ 박스: ["box", "package"],
31
+ 운송장: ["receipt", "barcode", "package"],
32
+ 물류: ["warehouse", "truck", "forklift"],
33
+ 창고: ["warehouse", "building-warehouse", "box"],
34
+ 해외배송: ["world", "plane", "truck"],
35
+ 항공: ["plane", "airplane"],
36
+ 선박: ["ship", "boat"],
37
+ 추적: ["map-pin", "route", "search"],
38
+ 도착: ["map-pin", "flag", "check"],
39
+
40
+ // ── 주문·결제 ───────────────────────────────────────────────────────────────
41
+ 주문: ["shopping-cart", "clipboard-list", "receipt"],
42
+ 장바구니: ["shopping-cart", "shopping-bag"],
43
+ 구매: ["shopping-bag", "credit-card", "shopping-cart"],
44
+ 결제: ["credit-card", "wallet", "cash"],
45
+ 카드: ["credit-card", "wallet"],
46
+ 현금: ["cash", "coins", "banknote"],
47
+ 계좌이체: ["building-bank", "arrows-exchange", "cash"],
48
+ 간편결제: ["device-mobile", "credit-card", "wallet"],
49
+ 영수증: ["receipt", "file-invoice"],
50
+ 세금계산서: ["file-invoice", "receipt-tax"],
51
+ 가격: ["tag", "currency-won", "price-tag"],
52
+ 할인: ["discount", "tag", "percentage"],
53
+ 세일: ["discount", "tag", "flame"],
54
+ 특가: ["flame", "discount", "tag"],
55
+ 쿠폰: ["ticket", "coupon", "tag"],
56
+ 적립: ["coin", "gift", "star"],
57
+ 포인트: ["coin", "star", "diamond"],
58
+ 마일리지: ["coin", "award", "star"],
59
+ 무이자: ["percentage", "credit-card", "calendar"],
60
+ 할부: ["calendar", "credit-card", "divide"],
61
+ 환불: ["arrow-back-up", "receipt-refund", "cash"],
62
+ 반품: ["arrow-back-up", "package", "truck-return"],
63
+ 교환: ["arrows-exchange", "refresh", "repeat"],
64
+ 취소: ["x", "circle-x", "ban"],
65
+
66
+ // ── 신뢰·인증 ───────────────────────────────────────────────────────────────
67
+ 정품: ["certificate", "rosette-discount-check", "shield-check"],
68
+ 인증: ["certificate", "badge-check", "shield-check"],
69
+ 검증: ["shield-check", "check", "search"],
70
+ 보증: ["shield", "certificate", "umbrella"],
71
+ 품질보증: ["shield-check", "award", "certificate"],
72
+ 특허: ["certificate", "award", "bulb"],
73
+ 상표: ["trademark", "copyright", "certificate"],
74
+ 공식: ["badge-check", "certificate", "building"],
75
+ 검사: ["microscope", "clipboard-check", "search"],
76
+ 시험성적서: ["file-certificate", "clipboard-check", "flask"],
77
+ 안전: ["shield-check", "helmet", "lock"],
78
+ 안심: ["shield-check", "heart-handshake", "lock"],
79
+ 신뢰: ["heart-handshake", "shield-check", "thumb-up"],
80
+ 수상: ["award", "trophy", "medal"],
81
+ "1위": ["trophy", "medal", "crown"],
82
+ 베스트: ["trophy", "star", "flame"],
83
+ 추천: ["thumb-up", "star", "heart"],
84
+ 인기: ["flame", "trending-up", "heart"],
85
+ 신상품: ["sparkles", "star", "tag"],
86
+ 한정: ["clock", "flame", "lock"],
87
+ 품절: ["package-off", "x", "alert-circle"],
88
+ 재입고: ["refresh", "package", "bell"],
89
+
90
+ // ── 제품 속성 ───────────────────────────────────────────────────────────────
91
+ 방수: ["droplet-off", "umbrella", "shield"],
92
+ 생활방수: ["droplet", "shield", "umbrella"],
93
+ 방진: ["wind", "shield", "filter"],
94
+ 방염: ["flame-off", "shield", "fire-extinguisher"],
95
+ 내구성: ["shield", "hammer", "armchair"],
96
+ 경량: ["feather", "wind", "scale"],
97
+ 초경량: ["feather", "wind"],
98
+ 가벼움: ["feather", "wind", "scale"],
99
+ 튼튼: ["shield", "hammer", "muscle"],
100
+ 통기성: ["wind", "air-conditioning", "grid-dots"],
101
+ 흡수: ["droplet", "sponge", "arrow-down"],
102
+ 건조: ["sun", "wind", "wash-dry"],
103
+ 세탁: ["wash-machine", "bubble", "shirt"],
104
+ 손세탁: ["hand-stop", "bubble", "wash"],
105
+ 드라이클리닝: ["wash-dry-clean", "shirt", "hanger"],
106
+ 다림질: ["ironing", "shirt"],
107
+ 소재: ["texture", "layers", "shirt"],
108
+ 원단: ["texture", "shirt", "layers"],
109
+ 면: ["plant", "shirt", "texture"],
110
+ 가죽: ["shirt", "texture", "bag"],
111
+ 스테인리스: ["tools-kitchen", "shine", "shield"],
112
+ 실리콘: ["droplet", "texture", "shield"],
113
+ 친환경: ["leaf", "recycle", "plant"],
114
+ 재활용: ["recycle", "refresh", "leaf"],
115
+ 무독성: ["leaf", "shield-check", "flask-off"],
116
+ 무향: ["wind", "flask-off"],
117
+ 저자극: ["feather", "heart", "shield"],
118
+ 국내생산: ["flag", "building-factory", "map-pin"],
119
+ 수입: ["world", "plane", "package-import"],
120
+ 원산지: ["map-pin", "world", "flag"],
121
+ 제조: ["building-factory", "tools", "settings"],
122
+ 성분: ["flask", "list-details", "leaf"],
123
+ 함량: ["chart-pie", "flask", "percentage"],
124
+ 용량: ["bottle", "ruler", "database"],
125
+ 중량: ["scale", "weight"],
126
+ 사이즈: ["ruler", "resize", "arrows-maximize"],
127
+ 치수: ["ruler", "dimensions", "resize"],
128
+ 규격: ["ruler", "dimensions", "clipboard-list"],
129
+ 색상: ["palette", "color-swatch", "droplet"],
130
+ 옵션: ["adjustments", "list", "settings"],
131
+ 구성품: ["box", "list", "package"],
132
+ 세트: ["boxes", "package", "layers"],
133
+ 유통기한: ["calendar-time", "clock", "hourglass"],
134
+ 제조일자: ["calendar", "building-factory"],
135
+ 보관: ["fridge", "box", "temperature"],
136
+ 냉장: ["fridge", "snowflake", "temperature"],
137
+ 냉동: ["snowflake", "fridge"],
138
+ 실온: ["temperature", "home", "sun"],
139
+
140
+ // ── 사용·기능 ───────────────────────────────────────────────────────────────
141
+ 사용법: ["book", "list-numbers", "help"],
142
+ 설명서: ["book", "file-text", "help"],
143
+ 주의사항: ["alert-triangle", "info-circle", "exclamation-mark"],
144
+ 경고: ["alert-triangle", "alert-circle"],
145
+ 금지: ["ban", "circle-x", "hand-stop"],
146
+ 필수: ["asterisk", "alert-circle", "star"],
147
+ 간편: ["click", "wand", "bolt"],
148
+ 원터치: ["click", "hand-finger", "bolt"],
149
+ 자동: ["settings-automation", "robot", "bolt"],
150
+ 수동: ["hand-finger", "adjustments", "tool"],
151
+ 조절: ["adjustments", "settings", "sliders"],
152
+ 분리형: ["separator", "unlink", "layers-difference"],
153
+ 접이식: ["fold", "arrows-minimize", "layers"],
154
+ 휴대: ["briefcase", "backpack", "device-mobile"],
155
+ 거치: ["device-desktop", "stand", "anchor"],
156
+ 충전: ["battery-charging", "plug", "bolt"],
157
+ 배터리: ["battery", "battery-charging"],
158
+ 무선: ["wifi", "bluetooth", "antenna"],
159
+ 블루투스: ["bluetooth", "device-mobile"],
160
+ 방수등급: ["droplet-off", "shield", "certificate"],
161
+ 소음: ["volume", "wave-square", "ear"],
162
+ 저소음: ["volume-off", "moon", "ear-off"],
163
+ 절전: ["bolt-off", "leaf", "plug-off"],
164
+ 전력: ["bolt", "plug", "battery"],
165
+ 온도: ["temperature", "thermometer", "sun"],
166
+ 타이머: ["clock", "hourglass", "alarm"],
167
+ 방향: ["arrows-move", "compass", "navigation"],
168
+
169
+ // ── 고객·응대 ───────────────────────────────────────────────────────────────
170
+ 고객센터: ["headset", "phone", "message-circle"],
171
+ 상담: ["message-circle", "headset", "user-question"],
172
+ 문의: ["message-question", "help-circle", "mail"],
173
+ 전화: ["phone", "phone-call"],
174
+ 카톡: ["message-circle", "brand-kakao"],
175
+ 카카오톡: ["message-circle", "brand-kakao"],
176
+ 채팅: ["message-circle", "messages"],
177
+ 이메일: ["mail", "at", "send"],
178
+ 운영시간: ["clock", "calendar", "building-store"],
179
+ 휴무: ["calendar-off", "moon", "door"],
180
+ 공지: ["speakerphone", "bell", "info-circle"],
181
+ 안내: ["info-circle", "help-circle", "book"],
182
+ 후기: ["star", "message-circle", "thumb-up"],
183
+ 리뷰: ["star", "message-circle", "pencil"],
184
+ 별점: ["star", "stars", "star-half"],
185
+ 평점: ["star", "chart-bar", "award"],
186
+ 질문: ["help-circle", "message-question", "question-mark"],
187
+ 답변: ["message-reply", "check", "message-circle"],
188
+
189
+ // ── 브랜드·마케팅 ───────────────────────────────────────────────────────────
190
+ 브랜드: ["award", "diamond", "crown"],
191
+ 로고: ["copyright", "diamond", "award"],
192
+ 이벤트: ["confetti", "gift", "calendar-event"],
193
+ 증정: ["gift", "present", "sparkles"],
194
+ 선물: ["gift", "present", "heart"],
195
+ 사은품: ["gift", "package", "sparkles"],
196
+ 체험: ["hand-finger", "flask", "eye"],
197
+ 구독: ["repeat", "calendar", "bell"],
198
+ 회원: ["user", "id-badge", "users"],
199
+ 등급: ["award", "crown", "stairs"],
200
+ 신규: ["sparkles", "plus", "star"],
201
+ 재구매: ["repeat", "shopping-cart", "refresh"],
202
+ 공유: ["share", "send", "users"],
203
+ 링크: ["link", "external-link"],
204
+ 홈페이지: ["world", "home", "browser"],
205
+ 쇼핑몰: ["building-store", "shopping-bag", "world"],
206
+ 매장: ["building-store", "map-pin", "door"],
207
+ 지도: ["map", "map-pin", "navigation"],
208
+ 위치: ["map-pin", "navigation", "target"],
209
+
210
+ // ── 수치·비교 ───────────────────────────────────────────────────────────────
211
+ 비교: ["arrows-left-right", "columns", "scale"],
212
+ 차이: ["arrows-diff", "minus", "chart-bar"],
213
+ 증가: ["trending-up", "arrow-up", "chart-line"],
214
+ 감소: ["trending-down", "arrow-down", "chart-line"],
215
+ 성장: ["trending-up", "plant", "chart-line"],
216
+ 통계: ["chart-bar", "chart-pie", "report-analytics"],
217
+ 그래프: ["chart-line", "chart-bar", "chart-area"],
218
+ 퍼센트: ["percentage", "chart-pie"],
219
+ 순위: ["trophy", "list-numbers", "medal"],
220
+ 누적: ["stack", "layers", "chart-bar"],
221
+ 판매량: ["chart-bar", "shopping-cart", "trending-up"],
222
+ 재고: ["package", "boxes", "database"],
223
+
224
+ // ── 시간·일정 ───────────────────────────────────────────────────────────────
225
+ 시간: ["clock", "hourglass"],
226
+ 날짜: ["calendar", "calendar-event"],
227
+ 기간: ["calendar", "hourglass", "clock"],
228
+ 마감: ["clock", "hourglass-off", "flag"],
229
+ 즉시: ["bolt", "flash", "clock"],
230
+ 빠름: ["bolt", "rocket", "truck-fast"],
231
+ "1년": ["calendar", "clock", "shield"],
232
+ "1개월": ["calendar-month", "clock"],
233
+ "24시간": ["clock-24", "clock", "moon"],
234
+ 연중무휴: ["calendar", "infinity", "clock"],
235
+
236
+ // ── 사람·신체 ───────────────────────────────────────────────────────────────
237
+ 사람: ["user", "users", "mood-smile"],
238
+ 아기: ["baby-carriage", "mood-kid", "heart"],
239
+ 어린이: ["mood-kid", "school", "baby-carriage"],
240
+ 여성: ["gender-female", "user", "woman"],
241
+ 남성: ["gender-male", "user", "man"],
242
+ 가족: ["users", "home", "heart"],
243
+ 반려동물: ["dog", "cat", "paw"],
244
+ 강아지: ["dog", "paw"],
245
+ 고양이: ["cat", "paw"],
246
+ 피부: ["hand", "sparkles", "droplet"],
247
+ 머리카락: ["scissors", "user", "wind"],
248
+ 건강: ["heart", "heartbeat", "activity"],
249
+ 운동: ["barbell", "run", "activity"],
250
+ 수면: ["moon", "bed", "zzz"],
251
+ 다이어트: ["scale", "run", "salad"],
252
+
253
+ // ── 생활·카테고리 ───────────────────────────────────────────────────────────
254
+ 주방: ["tools-kitchen", "chef-hat", "cooker"],
255
+ 요리: ["chef-hat", "cooker", "tools-kitchen"],
256
+ 음식: ["bowl", "meat", "salad"],
257
+ 음료: ["cup", "bottle", "glass"],
258
+ 커피: ["coffee", "cup"],
259
+ 침실: ["bed", "moon", "lamp"],
260
+ 욕실: ["bath", "shower", "droplet"],
261
+ 청소: ["spray", "brush", "trash"],
262
+ 수납: ["box", "archive", "layout-grid"],
263
+ 가구: ["armchair", "sofa", "table"],
264
+ 조명: ["bulb", "lamp", "sun"],
265
+ 전자제품: ["device-laptop", "plug", "cpu"],
266
+ 의류: ["shirt", "hanger", "jacket"],
267
+ 신발: ["shoe", "footprint"],
268
+ 가방: ["backpack", "briefcase", "shopping-bag"],
269
+ 화장품: ["perfume", "droplet", "sparkles"],
270
+ 문구: ["pencil", "notebook", "paperclip"],
271
+ 자동차: ["car", "steering-wheel"],
272
+ 캠핑: ["tent", "campfire", "mountain"],
273
+ 여행: ["plane", "luggage", "map"],
274
+ 사무실: ["building", "device-desktop", "briefcase"],
275
+
276
+ // ── 식품·식자재 ─────────────────────────────────────────────────────────────
277
+ // 상세페이지의 절반이 먹는 것이다. 여기가 비어 있어서 "사과"가 0건이었다 —
278
+ // 영어로 `apple`을 치면 나오는데 한글로 치면 안 나오는 것은 그냥 고장이다.
279
+ 사과: ["apple"],
280
+ 바나나: ["banana"],
281
+ 딸기: ["strawberry", "cherry"],
282
+ 포도: ["grapes", "grape"],
283
+ 수박: ["watermelon", "melon"],
284
+ 오렌지: ["orange"],
285
+ 레몬: ["lemon"],
286
+ 복숭아: ["peach"],
287
+ 체리: ["cherry"],
288
+ 블루베리: ["blueberry", "cherry"],
289
+ 아보카도: ["avocado"],
290
+ 과일: ["apple", "cherry", "orange"],
291
+ 채소: ["broccoli", "carrot", "plant"],
292
+ 당근: ["carrot"],
293
+ 토마토: ["tomato"],
294
+ 감자: ["potato"],
295
+ 고구마: ["potato", "plant"],
296
+ 양파: ["onion"],
297
+ 마늘: ["garlic"],
298
+ 브로콜리: ["broccoli"],
299
+ 옥수수: ["corn"],
300
+ 고추: ["pepper", "chili"],
301
+ 버섯: ["mushroom"],
302
+ 콩: ["bean", "grain"],
303
+ 견과: ["nut", "peanut"],
304
+ 쌀: ["grain", "rice", "bowl"],
305
+ 곡물: ["grain", "wheat"],
306
+ 계란: ["egg"],
307
+ 빵: ["bread", "baguette"],
308
+ 치즈: ["cheese"],
309
+ 버터: ["butter"],
310
+ 고기: ["meat", "beef"],
311
+ 삼겹살: ["meat", "bacon"],
312
+ 닭고기: ["meat", "egg-fried"],
313
+ 생선: ["fish"],
314
+ 새우: ["shrimp", "fish"],
315
+ 해산물: ["fish", "shrimp"],
316
+ 우유: ["milk"],
317
+ 요거트: ["yogurt", "milk"],
318
+ 꿀: ["honey", "beehive"],
319
+ 국수: ["noodles", "bowl"],
320
+ 라면: ["noodles", "bowl"],
321
+ 피자: ["pizza"],
322
+ 햄버거: ["burger", "hamburger"],
323
+ 샐러드: ["salad", "bowl"],
324
+ 케이크: ["cake"],
325
+ 아이스크림: ["ice-cream"],
326
+ 쿠키: ["cookie"],
327
+ 사탕: ["candy"],
328
+ 초콜릿: ["chocolate", "candy"],
329
+ 와인: ["wine"],
330
+ 맥주: ["beer"],
331
+ 소주: ["bottle", "glass"],
332
+ 주스: ["juice", "cup"],
333
+ 물: ["droplet", "glass-water"],
334
+ 소금: ["salt", "shaker"],
335
+ 설탕: ["sugar", "candy"],
336
+ 기름: ["bottle", "droplet"],
337
+ 간식: ["cookie", "candy"],
338
+ 도시락: ["lunchbox", "bowl"],
339
+ 반려간식: ["bone", "cookie"],
340
+
341
+ // ── 가전·가구(외래어가 많은 자리) ───────────────────────────────────────────
342
+ 냉장고: ["fridge", "refrigerator"],
343
+ 세탁기: ["wash-machine", "washing-machine"],
344
+ 건조기: ["wash-dry", "wind"],
345
+ 전자레인지: ["microwave"],
346
+ 에어컨: ["air-conditioning", "snowflake"],
347
+ 선풍기: ["fan", "propeller"],
348
+ 공기청정기: ["air-conditioning", "wind"],
349
+ 청소기: ["vacuum-cleaner"],
350
+ 정수기: ["glass-water", "droplet"],
351
+ 노트북: ["device-laptop", "laptop"],
352
+ 스마트폰: ["device-mobile", "phone"],
353
+ 태블릿: ["device-tablet", "tablet"],
354
+ 모니터: ["device-desktop", "monitor"],
355
+ 키보드: ["keyboard"],
356
+ 마우스: ["mouse"],
357
+ 이어폰: ["headphones", "earbuds"],
358
+ 헤드폰: ["headphones"],
359
+ 충전기: ["battery-charging", "plug"],
360
+ 텔레비전: ["device-tv", "television"],
361
+ 소파: ["sofa", "armchair"],
362
+ 침대: ["bed"],
363
+ 책상: ["desk", "table"],
364
+ 의자: ["armchair", "chair"],
365
+ 옷장: ["hanger", "door"],
366
+ 선반: ["shelf", "archive"],
367
+ 거울: ["mirror"],
368
+ 카펫: ["rug", "carpet"],
369
+ 커튼: ["curtains", "blinds"],
370
+ 화분: ["potted-plant", "plant"],
371
+
372
+ // ── 범용 기호 ───────────────────────────────────────────────────────────────
373
+ 체크: ["check", "circle-check", "square-check"],
374
+ 확인: ["check", "circle-check", "eye"],
375
+ 완료: ["circle-check", "check", "flag"],
376
+ 별: ["star", "stars"],
377
+ 하트: ["heart", "heart-filled"],
378
+ 화살표: ["arrow-right", "arrow-narrow-right", "chevron-right"],
379
+ 플러스: ["plus", "circle-plus"],
380
+ 마이너스: ["minus", "circle-minus"],
381
+ 느낌표: ["exclamation-mark", "alert-circle"],
382
+ 물음표: ["question-mark", "help-circle"],
383
+ 전구: ["bulb", "bulb-filled"],
384
+ 자물쇠: ["lock", "lock-open"],
385
+ 검색: ["search", "zoom"],
386
+ 설정: ["settings", "adjustments"],
387
+ 다운로드: ["download", "arrow-down-circle"],
388
+ 업로드: ["upload", "arrow-up-circle"],
389
+ 인쇄: ["printer", "file"],
390
+ 카메라: ["camera", "photo"],
391
+ 동영상: ["video", "player-play"],
392
+ 음악: ["music", "headphones"],
393
+ 알림: ["bell", "bell-ringing"],
394
+ 달력: ["calendar", "calendar-event"],
395
+ 폴더: ["folder", "folder-open"],
396
+ 파일: ["file", "file-text"],
397
+ 휴지통: ["trash", "trash-x"],
398
+ 손: ["hand-finger", "hand-stop", "click"],
399
+ 눈: ["eye", "eye-off"],
400
+ 불꽃: ["flame", "fire"],
401
+ 물방울: ["droplet", "droplets"],
402
+ 나뭇잎: ["leaf", "plant"],
403
+ 태양: ["sun", "sunrise"],
404
+ 달: ["moon", "moon-stars"],
405
+ 구름: ["cloud", "cloud-rain"],
406
+ 눈송이: ["snowflake", "snowman"],
407
+ 지구: ["world", "globe"],
408
+ 왕관: ["crown", "trophy"],
409
+ 다이아몬드: ["diamond", "gem"],
410
+ 방패: ["shield", "shield-check"],
411
+ 깃발: ["flag", "flag-filled"],
412
+ 메달: ["medal", "award"],
413
+ 트로피: ["trophy", "award"],
414
+ 선물상자: ["gift", "package"],
415
+ 전화기: ["phone", "device-mobile"],
416
+ 컴퓨터: ["device-desktop", "device-laptop"],
417
+ 휴대폰: ["device-mobile", "phone"],
418
+ 프린터: ["printer"],
419
+ 마이크: ["microphone", "headset"],
420
+ 스피커: ["speakerphone", "volume"],
421
+ 시계: ["clock", "alarm"],
422
+ 열쇠: ["key", "lock"],
423
+ 톱니바퀴: ["settings", "gear"],
424
+ 망치: ["hammer", "tools"],
425
+ 가위: ["scissors", "cut"],
426
+ 자: ["ruler", "ruler-measure"],
427
+ 연필: ["pencil", "edit"],
428
+ 붓: ["brush", "palette"],
429
+ 책: ["book", "notebook"],
430
+ 지폐: ["banknote", "cash"],
431
+ 동전: ["coin", "coins"],
432
+ 저울: ["scale", "balance"],
433
+ 온도계: ["thermometer", "temperature"],
434
+ 플러그: ["plug", "bolt"],
435
+ 로켓: ["rocket", "trending-up"],
436
+ 로봇: ["robot", "cpu"],
437
+ 자석: ["magnet"],
438
+ 퍼즐: ["puzzle", "components"],
439
+ 타겟: ["target", "crosshair"],
440
+ 무한: ["infinity", "repeat"],
441
+ } as const;
442
+
443
+ /** 한글이 하나라도 있으면 사전을 태운다. */
444
+ const HANGUL = /[가-힣]/;
445
+
446
+ /** 조사·공백·기호를 털어 낸다. "배송은" "무료 배송!" 이 같은 자리로 가야 한다. */
447
+ const PARTICLES =
448
+ /(?:은|는|이|가|을|를|에|에서|으로|로|와|과|의|도|만|까지|부터)$/;
449
+
450
+ function strip(token: string): string {
451
+ return token.replace(/[^0-9A-Za-z가-힣]/g, "");
452
+ }
453
+
454
+ /**
455
+ * 표제어 하나를 찾는다. **손으로 쓴 것이 언제나 먼저다.**
456
+ *
457
+ * 자동 생성분은 아이콘 이름에 실제로 있는 낱말만 담고 있어 넓지만, 어느 것이 1순위인지는
458
+ * 모른다("배송"의 `truck`이 `delivery`보다 앞이라는 것은 사람만 안다). 그래서 사람이
459
+ * 정해 둔 자리를 자동 생성이 밀어내지 못하게 순서를 고정한다.
460
+ */
461
+ function lookupKeyword(key: string): readonly string[] | undefined {
462
+ return ICON_KEYWORDS_KO[key] ?? ICON_KEYWORDS_GENERATED[key];
463
+ }
464
+
465
+ /** 부분일치용 키 목록. 긴 것부터, 손으로 쓴 것부터. 한 번만 만든다. */
466
+ let partialKeys: string[] | null = null;
467
+
468
+ function keysForPartialMatch(): string[] {
469
+ if (partialKeys) return partialKeys;
470
+ const byLength = (a: string, b: string) => b.length - a.length;
471
+ // 한 글자 키는 뺀다 — "화면"이 "면"으로, "숫자"가 "자"로 걸린다.
472
+ const hand = Object.keys(ICON_KEYWORDS_KO).filter((key) => key.length > 1).sort(byLength);
473
+ const baked = Object.keys(ICON_KEYWORDS_GENERATED)
474
+ .filter((key) => key.length > 1 && !ICON_KEYWORDS_KO[key])
475
+ .sort(byLength);
476
+ partialKeys = [...hand, ...baked];
477
+ return partialKeys;
478
+ }
479
+
480
+ /**
481
+ * 한국어 질의를 영어 키워드 배열로 편다. 앞이 1순위다.
482
+ *
483
+ * 세 겹으로 찾는다.
484
+ * 1. 질의 전체가 사전에 있으면 그것(가장 정확하다).
485
+ * 2. 없으면 어절 단위로 — 조사를 떼고 다시 본다.
486
+ * 3. 그래도 없으면 사전 키가 질의 안에 들어 있는지 본다("무료배송" → "배송").
487
+ * 긴 키부터 봐서 "무료배송"이 "배송"보다 먼저 걸리게 한다.
488
+ *
489
+ * 한글이 없으면 원문을 그대로 돌려준다 — 영어로 치는 사람을 막지 않는다.
490
+ */
491
+ export function expandKoreanQuery(query: string): string[] {
492
+ const text = query.trim();
493
+ if (!text) return [];
494
+ if (!HANGUL.test(text)) return [text];
495
+
496
+ const out: string[] = [];
497
+ const push = (words: readonly string[]) => {
498
+ for (const word of words) if (!out.includes(word)) out.push(word);
499
+ };
500
+
501
+ const whole = strip(text);
502
+ const exact = lookupKeyword(whole);
503
+ if (exact) push(exact);
504
+
505
+ if (!out.length) {
506
+ for (const token of text.split(/\s+/)) {
507
+ const bare = strip(token);
508
+ const hit = lookupKeyword(bare) ?? lookupKeyword(bare.replace(PARTICLES, ""));
509
+ if (hit) push(hit);
510
+ }
511
+ }
512
+
513
+ if (!out.length) {
514
+ for (const key of keysForPartialMatch()) {
515
+ if (whole.includes(key)) {
516
+ push(lookupKeyword(key) ?? []);
517
+ break;
518
+ }
519
+ }
520
+ }
521
+
522
+ // 하나도 못 찾으면 원문이라도 넘긴다. 제공처가 별칭으로 걸러 줄 수도 있다.
523
+ return out.length ? out : [text];
524
+ }