@reopt-ai/opt-ui 1.10.0 → 1.12.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 (73) hide show
  1. package/COMPONENT_CATALOG.md +522 -218
  2. package/README.md +61 -7
  3. package/dist/core/index.cjs +10 -8
  4. package/dist/core/index.d.cts +3 -3
  5. package/dist/core/index.d.ts +3 -3
  6. package/dist/core/index.js +3 -3
  7. package/dist/docs/02-components/01-core.md +213 -96
  8. package/dist/docs/02-components/02-visuals.md +1 -1
  9. package/dist/docs/02-components/03-shells.md +334 -123
  10. package/dist/docs/02-components/04-surfaces.md +1 -1
  11. package/dist/docs/02-components/index.md +5 -5
  12. package/dist/docs/03-recipes/02-dashboards.md +126 -10
  13. package/dist/{field-sidebar-B6Ndr8HE.cjs → field-sidebar-BYgEmdy0.cjs} +2128 -1558
  14. package/dist/{field-sidebar-BXjvV7Cz.js → field-sidebar-BuO37l4c.js} +2111 -1561
  15. package/dist/{field-sidebar-nwcE9LC4.d.cts → field-sidebar-CrYlRZew.d.ts} +375 -154
  16. package/dist/{field-sidebar-DWfJAJlx.d.ts → field-sidebar-uXmoyvd7.d.cts} +375 -154
  17. package/dist/id-registry.cjs +124 -12
  18. package/dist/id-registry.d.cts +1 -2
  19. package/dist/id-registry.d.ts +1 -2
  20. package/dist/id-registry.js +124 -14
  21. package/dist/id-registry.json +265 -31
  22. package/dist/index.cjs +1162 -253
  23. package/dist/index.d.cts +573 -60
  24. package/dist/index.d.ts +573 -60
  25. package/dist/index.js +1134 -242
  26. package/dist/{key-pad-menu-DfZbeXhG.d.cts → key-pad-menu-CwcY_Vps.d.cts} +151 -7
  27. package/dist/{key-pad-menu-BQunU20e.js → key-pad-menu-Di2aRGbM.js} +388 -461
  28. package/dist/{key-pad-menu-D5mQcS5v.cjs → key-pad-menu-SItoHPLu.cjs} +490 -609
  29. package/dist/{key-pad-menu-BsgJMjD4.d.ts → key-pad-menu-WBJShCJn.d.ts} +152 -8
  30. package/dist/meta.cjs +1304 -168
  31. package/dist/meta.d.cts +12 -36
  32. package/dist/meta.d.ts +12 -36
  33. package/dist/meta.js +1304 -170
  34. package/dist/query.d.cts +1 -2
  35. package/dist/query.d.ts +1 -2
  36. package/dist/query.js +0 -2
  37. package/dist/shells/index.cjs +4 -1
  38. package/dist/shells/index.d.cts +2 -2
  39. package/dist/shells/index.d.ts +2 -2
  40. package/dist/shells/index.js +2 -2
  41. package/dist/{text-truncate-BGr7bB_8.d.cts → text-truncate-CQyfUZvq.d.cts} +48 -5
  42. package/dist/{text-truncate-BGr7bB_8.d.ts → text-truncate-CQyfUZvq.d.ts} +48 -5
  43. package/dist/{text-truncate-j6X3z7Pv.js → text-truncate-DSuZECy0.js} +446 -94
  44. package/dist/{text-truncate-CxdzAnhS.cjs → text-truncate-I1XLWmRa.cjs} +522 -90
  45. package/dist/theme/server.d.cts +1 -2
  46. package/dist/theme/server.d.ts +1 -2
  47. package/dist/theme/server.js +0 -2
  48. package/dist/visuals/index.js +0 -1
  49. package/package.json +4 -3
  50. package/dist/field-sidebar-BXjvV7Cz.js.map +0 -1
  51. package/dist/field-sidebar-DWfJAJlx.d.ts.map +0 -1
  52. package/dist/field-sidebar-nwcE9LC4.d.cts.map +0 -1
  53. package/dist/id-registry.d.cts.map +0 -1
  54. package/dist/id-registry.d.ts.map +0 -1
  55. package/dist/id-registry.js.map +0 -1
  56. package/dist/index.d.cts.map +0 -1
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/key-pad-menu-BQunU20e.js.map +0 -1
  60. package/dist/key-pad-menu-BsgJMjD4.d.ts.map +0 -1
  61. package/dist/key-pad-menu-DfZbeXhG.d.cts.map +0 -1
  62. package/dist/meta.d.cts.map +0 -1
  63. package/dist/meta.d.ts.map +0 -1
  64. package/dist/meta.js.map +0 -1
  65. package/dist/query.d.cts.map +0 -1
  66. package/dist/query.d.ts.map +0 -1
  67. package/dist/query.js.map +0 -1
  68. package/dist/text-truncate-BGr7bB_8.d.cts.map +0 -1
  69. package/dist/text-truncate-BGr7bB_8.d.ts.map +0 -1
  70. package/dist/text-truncate-j6X3z7Pv.js.map +0 -1
  71. package/dist/theme/server.d.cts.map +0 -1
  72. package/dist/theme/server.d.ts.map +0 -1
  73. package/dist/theme/server.js.map +0 -1
package/dist/meta.js CHANGED
@@ -720,7 +720,7 @@ const coreMetas = [
720
720
  },
721
721
  {
722
722
  name: "variant",
723
- type: "\"heading-1\" \"heading-6\" | \"display-1\" | \"display-2\" | \"display-3\"",
723
+ type: "\"heading-1\" | \"heading-2\" | \"heading-3\" | \"heading-4\" | \"heading-5\" | \"heading-6\" | \"display-1\" | \"display-2\" | \"display-3\"",
724
724
  description: "시각적 제목 역할"
725
725
  },
726
726
  {
@@ -1049,7 +1049,7 @@ const coreMetas = [
1049
1049
  {
1050
1050
  name: "StatCard",
1051
1051
  category: "core",
1052
- description: "트렌드 방향과 변화량을 표시하는 통계 카드.",
1052
+ description: "단일 지표를 값·변화량·보조 설명으로 표시하는 통계 카드. 비교 기준이 없으면 변화량 줄을 렌더하지 않는다.",
1053
1053
  slug: "stat-card",
1054
1054
  exports: ["StatCard"],
1055
1055
  props: { StatCard: [
@@ -1068,14 +1068,61 @@ const coreMetas = [
1068
1068
  {
1069
1069
  name: "change",
1070
1070
  type: "string",
1071
- required: true,
1072
- description: "변화량 텍스트 (예: '+12.5%')"
1071
+ description: "변화량 텍스트 (예: '+12.5%'). 비교 기준이 없으면 생략 — trend와 함께 있을 때만 렌더된다"
1073
1072
  },
1074
1073
  {
1075
1074
  name: "trend",
1076
1075
  type: "\"up\" | \"down\" | \"neutral\"",
1077
- required: true,
1078
- description: "트렌드 방향. 색상과 아이콘 결정"
1076
+ description: "변화 방향. change와 함께 있을 때만 의미를 갖는다"
1077
+ },
1078
+ {
1079
+ name: "polarity",
1080
+ type: "\"positive\" | \"negative\"",
1081
+ default: "\"positive\"",
1082
+ description: "상승이 좋은 지표인지. 이탈률·에러 수처럼 반대인 지표는 negative로 색을 뒤집는다"
1083
+ },
1084
+ {
1085
+ name: "description",
1086
+ type: "string",
1087
+ description: "값 아래 한 줄 보조 설명 (집계 대상·기간 등)"
1088
+ },
1089
+ {
1090
+ name: "hint",
1091
+ type: "string",
1092
+ description: "산출 근거 등 긴 설명. 툴팁과 aria-describedby로 노출"
1093
+ },
1094
+ {
1095
+ name: "size",
1096
+ type: "\"sm\" | \"md\"",
1097
+ default: "\"md\"",
1098
+ description: "밀도 프리셋. 요약 행에는 sm"
1099
+ },
1100
+ {
1101
+ name: "loading",
1102
+ type: "boolean",
1103
+ default: "false",
1104
+ description: "값 로딩 중 스켈레톤 표시"
1105
+ },
1106
+ {
1107
+ name: "href",
1108
+ type: "string",
1109
+ description: "드릴다운 링크. 지정 시 카드가 앵커로 렌더된다"
1110
+ },
1111
+ {
1112
+ name: "onSelect",
1113
+ type: "() => void",
1114
+ description: "드릴다운 핸들러. 지정 시 카드가 버튼으로 렌더된다"
1115
+ },
1116
+ {
1117
+ name: "selected",
1118
+ type: "boolean",
1119
+ default: "false",
1120
+ description: "선택 가능한 카드 행에서 현재 선택 상태"
1121
+ },
1122
+ {
1123
+ name: "sparklineData",
1124
+ type: "number[]",
1125
+ description: "값 옆에 그릴 미니 추세선 데이터"
1079
1126
  }
1080
1127
  ] },
1081
1128
  ariaRole: "presentation",
@@ -2678,10 +2725,19 @@ const store = useFormStore({ defaultValues: { name: "" } });
2678
2725
  type: "string",
2679
2726
  description: "스위치 레이블"
2680
2727
  },
2728
+ {
2729
+ name: "hint",
2730
+ type: "string",
2731
+ description: "레이블 아래 보조 설명 텍스트"
2732
+ },
2681
2733
  {
2682
2734
  name: "description",
2683
2735
  type: "string",
2684
- description: "레이블 아래 설명 텍스트"
2736
+ description: "레이블 아래 설명 텍스트",
2737
+ deprecated: {
2738
+ since: "1.9.0",
2739
+ replacement: "hint"
2740
+ }
2685
2741
  },
2686
2742
  {
2687
2743
  name: "checked",
@@ -3102,12 +3158,180 @@ const store = useFormStore({ defaultValues: { name: "" } });
3102
3158
  },
3103
3159
  {
3104
3160
  name: "actions",
3105
- type: "EmptyStateAction[]",
3106
- description: "액션 버튼 배열. { label, onClick, variant? }"
3161
+ type: "EmptyStateAction[] | ReactNode",
3162
+ description: "액션 버튼 배열 { label, onClick, variant? } 또는 직접 렌더할 노드"
3163
+ },
3164
+ {
3165
+ name: "body",
3166
+ type: "ReactNode",
3167
+ description: "description 대신 넣는 리치 본문 (문단·목록·코드 스니펫)"
3168
+ },
3169
+ {
3170
+ name: "footer",
3171
+ type: "ReactNode",
3172
+ description: "구분선 아래 보조 콘텐츠. 문서 링크·단축키 안내 등"
3173
+ },
3174
+ {
3175
+ name: "layout",
3176
+ type: "\"vertical\" | \"horizontal\"",
3177
+ default: "\"vertical\"",
3178
+ description: "horizontal은 아이콘을 텍스트 옆으로 옮긴다 (전체 페이지용)"
3179
+ },
3180
+ {
3181
+ name: "titleSize",
3182
+ type: "\"sm\" | \"md\" | \"lg\"",
3183
+ default: "\"md\"",
3184
+ description: "제목 시각 크기. headingLevel과 독립"
3185
+ },
3186
+ {
3187
+ name: "hasBorder",
3188
+ type: "boolean",
3189
+ default: "false",
3190
+ description: "테두리 있는 패널로 렌더"
3191
+ },
3192
+ {
3193
+ name: "tone",
3194
+ type: "\"default\" | \"subdued\" | \"error\"",
3195
+ default: "\"default\"",
3196
+ description: "error는 실패를 알리고 role=alert로 렌더. subdued는 아직 데이터가 없음을 뜻한다"
3197
+ },
3198
+ {
3199
+ name: "iconSize",
3200
+ type: "\"sm\" | \"md\" | \"lg\"",
3201
+ default: "\"md\"",
3202
+ description: "아이콘 크기 프리셋"
3203
+ },
3204
+ {
3205
+ name: "headingLevel",
3206
+ type: "\"h2\" | \"h3\" | \"h4\"",
3207
+ default: "\"h3\"",
3208
+ description: "문서 구조상 제목 레벨"
3107
3209
  }
3108
3210
  ] },
3109
3211
  testDescribe: "EmptyState"
3110
3212
  },
3213
+ {
3214
+ name: "DescriptionList",
3215
+ category: "core",
3216
+ functionalCategory: "data-display",
3217
+ maturity: "stable",
3218
+ keywords: [
3219
+ "detail",
3220
+ "definition",
3221
+ "key-value",
3222
+ "metadata",
3223
+ "dl"
3224
+ ],
3225
+ description: "한 대상의 라벨 있는 사실들을 시맨틱 <dl>로 표시한다. 상세 패널의 필드 나열을 표준화하고 식별자 복사를 제공한다.",
3226
+ slug: "description-list",
3227
+ exports: ["DescriptionList"],
3228
+ props: { DescriptionList: [
3229
+ {
3230
+ name: "items",
3231
+ type: "DescriptionListItem[]",
3232
+ required: true,
3233
+ description: "term/description 쌍 배열. { id, term, description, copyValue?, hint?, wrap? }"
3234
+ },
3235
+ {
3236
+ name: "layout",
3237
+ type: "\"grid\" | \"stack\" | \"inline\"",
3238
+ default: "\"grid\"",
3239
+ description: "grid는 각 쌍을 박스로 감싼다(필드가 많은 상세 패널). stack은 평범한 정의 목록, inline은 헤더 스트립용 한 줄 배치"
3240
+ },
3241
+ {
3242
+ name: "columns",
3243
+ type: "1 | 2 | 3 | 4",
3244
+ default: "2",
3245
+ description: "grid/stack 레이아웃의 컬럼 수"
3246
+ },
3247
+ {
3248
+ name: "compressed",
3249
+ type: "boolean",
3250
+ default: "false",
3251
+ description: "밀도 축소"
3252
+ },
3253
+ {
3254
+ name: "labels",
3255
+ type: "DescriptionListLabels",
3256
+ description: "복사/복사됨/빈 값 문구"
3257
+ }
3258
+ ] },
3259
+ usage: {
3260
+ bestPractices: ["세션 ID·프로필 ID처럼 다른 화면에 붙여넣게 되는 값에는 copyValue를 준다.", "값이 비면 빈 셀 대신 labels.empty가 렌더되므로 로딩 중과 구분된다."],
3261
+ avoid: ["표 형태로 비교해야 하는 다건 데이터에는 쓰지 않는다 — 그건 DataTable의 일이다."]
3262
+ },
3263
+ testDescribe: "DescriptionList"
3264
+ },
3265
+ {
3266
+ name: "Identity",
3267
+ category: "core",
3268
+ functionalCategory: "data-display",
3269
+ maturity: "stable",
3270
+ keywords: [
3271
+ "user",
3272
+ "profile",
3273
+ "avatar",
3274
+ "person",
3275
+ "anonymous"
3276
+ ],
3277
+ description: "사람(또는 익명 디바이스)을 목록 셀 한 칸으로 표현한다. 이름→이메일→ID 폴백과 익명 표식을 한 곳에서 처리한다.",
3278
+ slug: "identity",
3279
+ exports: ["Identity"],
3280
+ props: { Identity: [
3281
+ {
3282
+ name: "name",
3283
+ type: "string | null",
3284
+ description: "표시 이름. 없으면 secondary, 그다음 id로 폴백한다"
3285
+ },
3286
+ {
3287
+ name: "secondary",
3288
+ type: "string | null",
3289
+ description: "보통 이메일. 이름이 없으면 주 라벨로 승격된다"
3290
+ },
3291
+ {
3292
+ name: "id",
3293
+ type: "string",
3294
+ description: "최후 폴백 라벨이자 hover 시 title"
3295
+ },
3296
+ {
3297
+ name: "avatarSrc",
3298
+ type: "string | null",
3299
+ description: "아바타 이미지 URL"
3300
+ },
3301
+ {
3302
+ name: "state",
3303
+ type: "\"identified\" | \"anonymous\"",
3304
+ description: "익명 여부. 디바이스를 사람으로 오독하지 않게 배지로 표시한다"
3305
+ },
3306
+ {
3307
+ name: "badge",
3308
+ type: "ReactNode",
3309
+ description: "state 배지 대신 표시할 내용 (라이프사이클 단계 등)"
3310
+ },
3311
+ {
3312
+ name: "size",
3313
+ type: "\"sm\" | \"md\"",
3314
+ default: "\"md\"",
3315
+ description: "밀도 프리셋"
3316
+ },
3317
+ {
3318
+ name: "hideAvatar",
3319
+ type: "boolean",
3320
+ default: "false",
3321
+ description: "이미 밀도가 높은 행에서 아바타 제거"
3322
+ },
3323
+ {
3324
+ name: "labels",
3325
+ type: "{ identified?: string; anonymous?: string }",
3326
+ description: "식별/익명 배지 문구"
3327
+ }
3328
+ ] },
3329
+ usage: {
3330
+ bestPractices: ["사용자·세션·이벤트 표에서 주체를 나타내는 컬럼에 일관되게 사용한다."],
3331
+ avoid: ["아바타만 필요하면 Avatar를 직접 쓴다."]
3332
+ },
3333
+ testDescribe: "Identity"
3334
+ },
3111
3335
  {
3112
3336
  name: "Card",
3113
3337
  category: "core",
@@ -3187,10 +3411,19 @@ const store = useFormStore({ defaultValues: { name: "" } });
3187
3411
  type: "string",
3188
3412
  description: "체크박스 레이블"
3189
3413
  },
3414
+ {
3415
+ name: "hint",
3416
+ type: "string",
3417
+ description: "레이블 아래 보조 설명 텍스트"
3418
+ },
3190
3419
  {
3191
3420
  name: "description",
3192
3421
  type: "string",
3193
- description: "레이블 아래 설명 텍스트"
3422
+ description: "레이블 아래 설명 텍스트",
3423
+ deprecated: {
3424
+ since: "1.9.0",
3425
+ replacement: "hint"
3426
+ }
3194
3427
  },
3195
3428
  {
3196
3429
  name: "checked",
@@ -3324,10 +3557,19 @@ const store = useFormStore({ defaultValues: { name: "" } });
3324
3557
  type: "string",
3325
3558
  description: "라디오 레이블"
3326
3559
  },
3560
+ {
3561
+ name: "hint",
3562
+ type: "string",
3563
+ description: "레이블 아래 보조 설명 텍스트"
3564
+ },
3327
3565
  {
3328
3566
  name: "description",
3329
3567
  type: "string",
3330
- description: "레이블 아래 설명 텍스트"
3568
+ description: "레이블 아래 설명 텍스트",
3569
+ deprecated: {
3570
+ since: "1.9.0",
3571
+ replacement: "hint"
3572
+ }
3331
3573
  },
3332
3574
  {
3333
3575
  name: "size",
@@ -3398,7 +3640,7 @@ const store = useFormStore({ defaultValues: { name: "" } });
3398
3640
  },
3399
3641
  {
3400
3642
  name: "onComparisonChange",
3401
- type: "(comparison: boolean, mode: ComparisonMode) => void",
3643
+ type: "(mode: ComparisonMode | null) => void",
3402
3644
  description: "비교 모드 변경 콜백"
3403
3645
  },
3404
3646
  {
@@ -4086,7 +4328,12 @@ const store = useFormStore({ defaultValues: { name: "" } });
4086
4328
  {
4087
4329
  name: "autoSaveId",
4088
4330
  type: "string",
4089
- description: "id의 deprecated 별칭"
4331
+ description: "id의 deprecated 별칭",
4332
+ deprecated: {
4333
+ since: "1.6.0",
4334
+ replacement: "id",
4335
+ reason: "id + defaultLayout + onLayoutChanged 조합으로 대체되었습니다."
4336
+ }
4090
4337
  },
4091
4338
  {
4092
4339
  name: "className",
@@ -4733,82 +4980,273 @@ const MyDashboard = createBlock<MyDashboardProps>(
4733
4980
  }
4734
4981
  ] },
4735
4982
  testDescribe: "ShaderSurface"
4736
- }
4737
- ];
4738
- //#endregion
4739
- //#region src/visuals/_meta.ts
4740
- /** @deprecated Chart metadata moved to `@reopt-ai/opt-charts/meta`. */
4741
- const visualMetas = [];
4742
- const shellMetas = [
4743
- {
4744
- name: "FaqAccordion",
4745
- category: "shell",
4746
- description: "FAQ 질문/답변 아코디언. Composite + Disclosure 조합.",
4747
- slug: "faq-accordion",
4748
- exports: ["FaqAccordion"],
4749
- props: { FaqAccordion: [{
4750
- name: "items",
4751
- type: "FaqItem[]",
4752
- required: true,
4753
- description: "FAQ 항목 배열. { id, question, answer }"
4754
- }] },
4755
- dependencies: [{
4756
- slug: "disclosure",
4757
- category: "core"
4758
- }],
4759
- testDescribe: "FaqAccordion"
4760
4983
  },
4761
4984
  {
4762
- name: "PageAside",
4763
- category: "shell",
4764
- description: "Sticky 페이지 사이드 aside chrome. 고정 너비 + header/body/footer 3-slot.",
4765
- slug: "page-aside",
4766
- exports: ["PageAside"],
4767
- props: { PageAside: [
4985
+ name: "Callout",
4986
+ category: "core",
4987
+ description: "페이지에 머무는 인페이지 고지. Alert와 달리 dismiss가 없고, 조건이 참인 동안 레이아웃의 일부로 남습니다.",
4988
+ slug: "callout",
4989
+ exports: ["Callout"],
4990
+ props: { Callout: [
4768
4991
  {
4769
- name: "header",
4992
+ name: "tone",
4993
+ type: "\"info\" | \"success\" | \"warning\" | \"danger\" | \"neutral\"",
4994
+ default: "\"info\"",
4995
+ description: "고지의 성격"
4996
+ },
4997
+ {
4998
+ name: "title",
4770
4999
  type: "ReactNode",
4771
- description: "상단 고정 슬롯 (예: package switcher)."
5000
+ description: " 제목"
5001
+ },
5002
+ {
5003
+ name: "icon",
5004
+ type: "ReactNode",
5005
+ description: "tone 기본 글리프 대체. null이면 글리프 없음"
5006
+ },
5007
+ {
5008
+ name: "action",
5009
+ type: "ReactNode",
5010
+ description: "다음 한 걸음. 둘 이상이면 고지가 아니라 폼"
5011
+ },
5012
+ {
5013
+ name: "size",
5014
+ type: "\"sm\" | \"md\"",
5015
+ default: "\"md\"",
5016
+ description: "밀도"
4772
5017
  },
4773
5018
  {
4774
5019
  name: "children",
4775
5020
  type: "ReactNode",
4776
- required: true,
4777
- description: "스크롤 가능한 네비게이션 영역."
5021
+ description: "본문"
5022
+ }
5023
+ ] },
5024
+ usage: {
5025
+ bestPractices: ["조건이 참인 동안 계속 보여야 하는 안내에 사용합니다 (정리할 신규 N개, 표본 기반 수치)."],
5026
+ avoid: ["방금 일어난 일을 알릴 때는 Alert를 씁니다. Callout에는 dismiss가 없습니다."]
5027
+ },
5028
+ testDescribe: "Callout"
5029
+ },
5030
+ {
5031
+ name: "StatusDot",
5032
+ category: "core",
5033
+ description: "점 + 텍스트로 상태를 표현합니다. 라벨이 필수라 색만으로 상태를 말할 수 없습니다.",
5034
+ slug: "status-dot",
5035
+ exports: ["StatusDot"],
5036
+ props: { StatusDot: [
5037
+ {
5038
+ name: "tone",
5039
+ type: "\"neutral\" | \"muted\" | \"info\" | \"success\" | \"warning\" | \"danger\"",
5040
+ default: "\"neutral\"",
5041
+ description: "점 색상"
4778
5042
  },
4779
5043
  {
4780
- name: "footer",
5044
+ name: "size",
5045
+ type: "\"sm\" | \"md\"",
5046
+ default: "\"md\"",
5047
+ description: "점과 글자 크기"
5048
+ },
5049
+ {
5050
+ name: "children",
4781
5051
  type: "ReactNode",
4782
- description: "하단 고정 슬롯 (예: version label)."
5052
+ required: true,
5053
+ description: "상태 라벨. 필수 — 색은 상태가 아닙니다"
4783
5054
  },
4784
5055
  {
4785
- name: "widthClassName",
5056
+ name: "hint",
4786
5057
  type: "string",
4787
- description: "너비 Tailwind 클래스. 기본값 `w-60`."
4788
- },
5058
+ description: "판정 기준 툴팁"
5059
+ }
5060
+ ] },
5061
+ usage: {
5062
+ bestPractices: ["표의 상태 컬럼처럼 행마다 반복되는 자리에 씁니다. 점은 장식이고 의미는 텍스트가 담습니다."],
5063
+ avoid: ["사람이 붙인 라벨(태그, 카테고리)에는 Badge를 씁니다. StatusDot은 관측된 상태용입니다."]
5064
+ },
5065
+ testDescribe: "StatusDot"
5066
+ },
5067
+ {
5068
+ name: "CopyButton",
5069
+ category: "core",
5070
+ description: "값을 클립보드에 올리고 성공 여부를 알립니다. 클립보드 거부를 삼키지 않습니다.",
5071
+ slug: "copy-button",
5072
+ exports: ["CopyButton"],
5073
+ props: { CopyButton: [
4789
5074
  {
4790
- name: "label",
5075
+ name: "value",
4791
5076
  type: "string",
4792
- description: "`<aside>`의 aria-label."
5077
+ required: true,
5078
+ description: "복사할 텍스트"
4793
5079
  },
4794
5080
  {
4795
- name: "hideOnMobile",
4796
- type: "boolean",
4797
- description: "md 아래에서 숨김. 기본값 true."
5081
+ name: "variant",
5082
+ type: "\"icon\" | \"text\"",
5083
+ default: "\"icon\"",
5084
+ description: "글리프만 / 글리프 + 라벨"
5085
+ },
5086
+ {
5087
+ name: "size",
5088
+ type: "\"sm\" | \"md\"",
5089
+ default: "\"md\"",
5090
+ description: "버튼 크기"
5091
+ },
5092
+ {
5093
+ name: "feedbackDuration",
5094
+ type: "number",
5095
+ default: "1500",
5096
+ description: "확인 상태 유지 시간 (ms)"
5097
+ },
5098
+ {
5099
+ name: "onCopy",
5100
+ type: "(ok: boolean) => void",
5101
+ description: "실제로 복사됐는지 — 비보안 컨텍스트·권한 거부에서 false"
5102
+ },
5103
+ {
5104
+ name: "labels",
5105
+ type: "CopyButtonLabels",
5106
+ description: "copy / copied / failed 문구"
4798
5107
  }
4799
5108
  ] },
4800
- testDescribe: "PageAside"
5109
+ testDescribe: "CopyButton"
4801
5110
  },
4802
5111
  {
4803
- name: "ContentTabs",
4804
- category: "shell",
4805
- description: " 콘텐츠 패널. Tabs primitive 래퍼.",
4806
- slug: "content-tabs",
4807
- exports: ["ContentTabs"],
4808
- props: { ContentTabs: [{
4809
- name: "tabs",
4810
- type: "TabDef[]",
4811
- required: true,
5112
+ name: "SegmentedControl",
5113
+ category: "core",
5114
+ description: "선택지를 모두 보여주는 단일 선택 컨트롤. radio-group 프리미티브 위에 올려 화살표 키가 동작합니다.",
5115
+ slug: "segmented-control",
5116
+ exports: ["SegmentedControl"],
5117
+ props: { SegmentedControl: [
5118
+ {
5119
+ name: "options",
5120
+ type: "SegmentedControlOption<T>[]",
5121
+ required: true,
5122
+ description: "세그먼트 목록 (value / label / icon / badge / disabled)"
5123
+ },
5124
+ {
5125
+ name: "value",
5126
+ type: "T",
5127
+ required: true,
5128
+ description: "선택된 값"
5129
+ },
5130
+ {
5131
+ name: "onChange",
5132
+ type: "(value: T) => void",
5133
+ required: true,
5134
+ description: "선택 변경"
5135
+ },
5136
+ {
5137
+ name: "size",
5138
+ type: "\"sm\" | \"md\"",
5139
+ default: "\"md\"",
5140
+ description: "컨트롤 크기"
5141
+ },
5142
+ {
5143
+ name: "fullWidth",
5144
+ type: "boolean",
5145
+ default: "false",
5146
+ description: "컨테이너를 균등 분할"
5147
+ },
5148
+ {
5149
+ name: "disabled",
5150
+ type: "boolean",
5151
+ default: "false",
5152
+ description: "전체 비활성"
5153
+ },
5154
+ {
5155
+ name: "aria-label",
5156
+ type: "string",
5157
+ required: true,
5158
+ description: "radiogroup 이름. 필수"
5159
+ }
5160
+ ] },
5161
+ usage: {
5162
+ bestPractices: ["정확히 하나가 참이고 선택지가 적을 때 씁니다 (이벤트 | 속성)."],
5163
+ avoid: ["여러 개를 동시에 켤 수 있으면 ToggleGroup을 씁니다.", "선택지가 많으면 Select를 씁니다."]
5164
+ },
5165
+ testDescribe: "SegmentedControl"
5166
+ }
5167
+ ];
5168
+ //#endregion
5169
+ //#region src/visuals/_meta.ts
5170
+ /** @deprecated Chart metadata moved to `@reopt-ai/opt-charts/meta`. */
5171
+ const visualMetas = [];
5172
+ const shellMetas = [
5173
+ {
5174
+ name: "FaqAccordion",
5175
+ category: "shell",
5176
+ description: "FAQ 질문/답변 아코디언. Composite + Disclosure 조합.",
5177
+ slug: "faq-accordion",
5178
+ exports: ["FaqAccordion"],
5179
+ props: { FaqAccordion: [{
5180
+ name: "items",
5181
+ type: "FaqItem[]",
5182
+ required: true,
5183
+ description: "FAQ 항목 배열. { id, question, answer }"
5184
+ }] },
5185
+ ariaRole: "region",
5186
+ keyboardShortcuts: [{
5187
+ keys: "↑ ↓",
5188
+ action: "질문 사이 포커스 이동"
5189
+ }, {
5190
+ keys: "Enter / Space",
5191
+ action: "답변 펼치기 또는 접기"
5192
+ }],
5193
+ dependencies: [{
5194
+ slug: "disclosure",
5195
+ category: "core"
5196
+ }],
5197
+ testDescribe: "FaqAccordion"
5198
+ },
5199
+ {
5200
+ name: "PageAside",
5201
+ category: "shell",
5202
+ description: "Sticky 페이지 사이드 aside chrome. 고정 너비 + header/body/footer 3-slot.",
5203
+ slug: "page-aside",
5204
+ exports: ["PageAside"],
5205
+ props: { PageAside: [
5206
+ {
5207
+ name: "header",
5208
+ type: "ReactNode",
5209
+ description: "상단 고정 슬롯 (예: package switcher)."
5210
+ },
5211
+ {
5212
+ name: "children",
5213
+ type: "ReactNode",
5214
+ required: true,
5215
+ description: "스크롤 가능한 네비게이션 영역."
5216
+ },
5217
+ {
5218
+ name: "footer",
5219
+ type: "ReactNode",
5220
+ description: "하단 고정 슬롯 (예: version label)."
5221
+ },
5222
+ {
5223
+ name: "widthClassName",
5224
+ type: "string",
5225
+ description: "너비 Tailwind 클래스. 기본값 `w-60`."
5226
+ },
5227
+ {
5228
+ name: "label",
5229
+ type: "string",
5230
+ description: "`<aside>`의 aria-label."
5231
+ },
5232
+ {
5233
+ name: "hideOnMobile",
5234
+ type: "boolean",
5235
+ description: "md 아래에서 숨김. 기본값 true."
5236
+ }
5237
+ ] },
5238
+ testDescribe: "PageAside"
5239
+ },
5240
+ {
5241
+ name: "ContentTabs",
5242
+ category: "shell",
5243
+ description: "탭 콘텐츠 패널. Tabs primitive 래퍼.",
5244
+ slug: "content-tabs",
5245
+ exports: ["ContentTabs"],
5246
+ props: { ContentTabs: [{
5247
+ name: "tabs",
5248
+ type: "TabDef[]",
5249
+ required: true,
4812
5250
  description: "탭 정의 배열. { id, label, content }"
4813
5251
  }, {
4814
5252
  name: "defaultTabId",
@@ -5073,6 +5511,25 @@ const fields = [
5073
5511
  slug: "kbd",
5074
5512
  category: "core"
5075
5513
  }],
5514
+ ariaRole: "menubar",
5515
+ keyboardShortcuts: [
5516
+ {
5517
+ keys: "← →",
5518
+ action: "상위 메뉴 사이 포커스 이동"
5519
+ },
5520
+ {
5521
+ keys: "Enter / Space / ↓",
5522
+ action: "메뉴 열기"
5523
+ },
5524
+ {
5525
+ keys: "↑ ↓",
5526
+ action: "열린 메뉴 항목 이동"
5527
+ },
5528
+ {
5529
+ keys: "Escape",
5530
+ action: "메뉴 닫고 트리거로 복귀"
5531
+ }
5532
+ ],
5076
5533
  testDescribe: "AppMenubar"
5077
5534
  },
5078
5535
  {
@@ -5084,7 +5541,7 @@ const fields = [
5084
5541
  props: { CommandPalette: [
5085
5542
  {
5086
5543
  name: "commands",
5087
- type: "Omit<Command, \"action\">[]",
5544
+ type: "Omit<CommandDef, \"action\">[]",
5088
5545
  required: true,
5089
5546
  description: "커맨드 목록. { id, label, group, shortcut? }"
5090
5547
  },
@@ -5102,7 +5559,7 @@ const fields = [
5102
5559
  },
5103
5560
  {
5104
5561
  name: "onSelect",
5105
- type: "(command: Omit<Command, 'action'>) => void",
5562
+ type: "(command: Omit<CommandDef, \"action\">) => void",
5106
5563
  description: "커맨드 선택 핸들러"
5107
5564
  },
5108
5565
  {
@@ -5157,13 +5614,13 @@ const fields = [
5157
5614
  props: { CommandPaletteTrigger: [
5158
5615
  {
5159
5616
  name: "commands",
5160
- type: "Omit<Command, \"action\">[]",
5617
+ type: "Omit<CommandDef, \"action\">[]",
5161
5618
  required: true,
5162
5619
  description: "커맨드 목록"
5163
5620
  },
5164
5621
  {
5165
5622
  name: "onSelect",
5166
- type: "(command: Omit<Command, 'action'>) => void",
5623
+ type: "(command: Omit<CommandDef, \"action\">) => void",
5167
5624
  description: "커맨드 선택 핸들러"
5168
5625
  },
5169
5626
  {
@@ -5638,7 +6095,7 @@ const data = [
5638
6095
  },
5639
6096
  {
5640
6097
  name: "onFilterChange",
5641
- type: "(filterId: string, value: string | string[]) => void",
6098
+ type: "(filterId: string, value: string | string[] | boolean) => void",
5642
6099
  description: "필터 변경 핸들러"
5643
6100
  },
5644
6101
  {
@@ -5829,7 +6286,7 @@ const filters = [
5829
6286
  },
5830
6287
  {
5831
6288
  name: "onSubmit",
5832
- type: "(values: { email, password, confirmPassword?, rememberMe?, mode }) => void",
6289
+ type: "(values: { email: string; password: string; confirmPassword?: string; rememberMe?: boolean; mode: \"login\" | \"signup\" }) => void",
5833
6290
  description: "제출 핸들러"
5834
6291
  },
5835
6292
  {
@@ -5994,7 +6451,7 @@ const filters = [
5994
6451
  },
5995
6452
  {
5996
6453
  name: "onSubmit",
5997
- type: "(values: { title, description, file }) => void",
6454
+ type: "(values: { title: string; description: string; file: File | null }) => void",
5998
6455
  description: "제출 핸들러"
5999
6456
  },
6000
6457
  {
@@ -6591,7 +7048,7 @@ const filters = [
6591
7048
  },
6592
7049
  {
6593
7050
  name: "onSave",
6594
- type: "(report: { name, description, chartType, dataSourceId }) => void",
7051
+ type: "(report: { name: string; description: string; chartType: ReportBuilderChartType; dataSourceId: string }) => void",
6595
7052
  description: "저장 콜백"
6596
7053
  },
6597
7054
  {
@@ -6789,14 +7246,25 @@ const filters = [
6789
7246
  },
6790
7247
  {
6791
7248
  name: "columns",
6792
- type: "2 | 3 | 4",
7249
+ type: "2 | 3 | 4 | 5 | 6",
6793
7250
  default: "4",
6794
7251
  description: "컬럼 수"
6795
7252
  },
6796
7253
  {
6797
7254
  name: "onStatClick",
6798
7255
  type: "(stat: StatCardType) => void",
6799
- description: "카드 클릭 핸들러"
7256
+ description: "카드 클릭 핸들러. 지정 시 카드 자체가 버튼으로 렌더된다"
7257
+ },
7258
+ {
7259
+ name: "selectedStatId",
7260
+ type: "string",
7261
+ description: "선택 가능한 행에서 현재 뷰를 이끄는 카드 id"
7262
+ },
7263
+ {
7264
+ name: "size",
7265
+ type: "\"sm\" | \"md\"",
7266
+ default: "\"md\"",
7267
+ description: "카드 밀도. 표 위 헤더 스트립에는 sm"
6800
7268
  },
6801
7269
  {
6802
7270
  name: "formatValue",
@@ -6832,124 +7300,375 @@ const filters = [
6832
7300
  testDescribe: "SummaryRow"
6833
7301
  },
6834
7302
  {
6835
- name: "ComparisonSelector",
7303
+ name: "EventTimeline",
6836
7304
  category: "shell",
6837
- description: "비교 선택기. 고유 레이블이 연결된 Select × 2 + Swap 버튼 조합. CohortComparison, FunnelAnalysis 등에서 사용합니다.",
6838
- slug: "comparison-selector",
6839
- exports: ["ComparisonSelector"],
6840
- props: { ComparisonSelector: [
7305
+ functionalCategory: "data-display",
7306
+ maturity: "stable",
7307
+ keywords: [
7308
+ "timeline",
7309
+ "events",
7310
+ "session",
7311
+ "activity",
7312
+ "sequence"
7313
+ ],
7314
+ description: "이벤트를 순서로 읽히게 하는 타임라인. 인접 이벤트 사이의 경과 시간을 라벨링하고, 속성 페이로드를 제자리에서 펼치며, 세션 단위 그룹핑을 지원한다.",
7315
+ slug: "event-timeline",
7316
+ exports: ["EventTimeline"],
7317
+ props: { EventTimeline: [
6841
7318
  {
6842
- name: "segments",
6843
- type: "ComparisonOption[]",
6844
- required: true,
6845
- description: "선택 가능한 세그먼트 목록"
7319
+ name: "items",
7320
+ type: "EventTimelineItem[]",
7321
+ description: "평면 목록. groups가 있으면 무시된다"
6846
7322
  },
6847
7323
  {
6848
- name: "valueA",
6849
- type: "string",
6850
- description: "Base 선택값"
7324
+ name: "groups",
7325
+ type: "EventTimelineGroup[]",
7326
+ description: "그룹(주로 세션) 단위 목록. 접기/펼치기와 그룹 요약을 제공"
6851
7327
  },
6852
7328
  {
6853
- name: "valueB",
6854
- type: "string",
6855
- description: "Compare 선택값"
7329
+ name: "showGaps",
7330
+ type: "boolean",
7331
+ default: "true",
7332
+ description: "인접 이벤트 사이 경과 시간 표시. 체크아웃 직전 4분 정지 같은 것이 발견이 된다"
6856
7333
  },
6857
7334
  {
6858
- name: "onChangeA",
6859
- type: "(value: string) => void",
6860
- description: "Base 변경 핸들러"
7335
+ name: "order",
7336
+ type: "\"desc\" | \"asc\"",
7337
+ default: "\"desc\"",
7338
+ description: "전달된 items의 정렬 방향. 경과 시간 계산 기준"
6861
7339
  },
6862
7340
  {
6863
- name: "onChangeB",
6864
- type: "(value: string) => void",
6865
- description: "Compare 변경 핸들러"
7341
+ name: "minGapMs",
7342
+ type: "number",
7343
+ default: "1000",
7344
+ description: "이 값 미만의 간격은 라벨을 생략"
6866
7345
  },
6867
7346
  {
6868
- name: "labels",
6869
- type: "ComparisonSelectorLabels",
6870
- description: "i18n용 라벨 오버라이드"
6871
- }
6872
- ] },
6873
- dependencies: [{
6874
- slug: "select",
6875
- category: "core"
6876
- }, {
6877
- slug: "button",
6878
- category: "core"
6879
- }],
6880
- testDescribe: "ComparisonSelector"
6881
- },
6882
- {
6883
- name: "ThemeSwitcher",
6884
- category: "shell",
6885
- description: "테마 프리셋 선택기. 프리셋과 light/dark/system 모드 전환.",
6886
- slug: "theme-switcher",
6887
- exports: ["ThemeSwitcher"],
6888
- props: { ThemeSwitcher: [{
6889
- name: "labels",
6890
- type: "ThemeSwitcherLabels",
6891
- description: "i18n용 라벨 오버라이드"
6892
- }] },
6893
- testDescribe: "ThemeSwitcher"
6894
- },
6895
- {
6896
- name: "PromptTemplate",
6897
- category: "shell",
6898
- description: "AI 프롬프트 뷰어/헬퍼. 변수 입력 → 실시간 치환 → 복사 기능.",
6899
- slug: "prompt-template",
6900
- exports: ["PromptTemplate"],
6901
- props: { PromptTemplate: [
7347
+ name: "loading",
7348
+ type: "boolean",
7349
+ default: "false",
7350
+ description: "스켈레톤 표시"
7351
+ },
6902
7352
  {
6903
- name: "title",
6904
- type: "string",
6905
- required: true,
6906
- description: "프롬프트 제목"
7353
+ name: "hasMore",
7354
+ type: "boolean",
7355
+ default: "false",
7356
+ description: " 불러올 항목 존재"
6907
7357
  },
6908
7358
  {
6909
- name: "description",
6910
- type: "string",
6911
- description: "프롬프트 설명 (헤더 아래 표시)"
7359
+ name: "onLoadMore",
7360
+ type: "() => void",
7361
+ description: " 보기 핸들러"
6912
7362
  },
6913
7363
  {
6914
- name: "template",
6915
- type: "string",
6916
- required: true,
6917
- description: "프롬프트 템플릿. {변수명}으로 치환 가능"
7364
+ name: "onItemSelect",
7365
+ type: "(item: EventTimelineItem) => void",
7366
+ description: "이벤트 헤드라인 클릭 핸들러"
6918
7367
  },
6919
7368
  {
6920
- name: "variables",
6921
- type: "PromptVariable[]",
6922
- description: "변수 정의 배열. { name, label, placeholder?, defaultValue? }"
7369
+ name: "locale",
7370
+ type: "string",
7371
+ description: "경과 시간 포맷 로케일"
6923
7372
  },
6924
7373
  {
6925
7374
  name: "labels",
6926
- type: "PromptTemplateLabels",
6927
- description: "i18n용 라벨 오버라이드"
7375
+ type: "EventTimelineLabels",
7376
+ description: "문구 오버라이드"
6928
7377
  }
6929
7378
  ] },
6930
- dependencies: [{
6931
- slug: "input",
6932
- category: "core"
6933
- }],
6934
- testDescribe: "PromptTemplate"
7379
+ usage: {
7380
+ bestPractices: ["사용자 히스토리는 세션으로 그룹핑한다 — 세션 경계가 없으면 히스토리를 읽을 수 없다.", "이벤트 속성은 properties로 넘겨 제자리에서 펼치게 한다."],
7381
+ avoid: ["수천 건을 한 번에 넘기지 않는다. 페이지네이션이나 onLoadMore로 나눈다."]
7382
+ },
7383
+ testDescribe: "EventTimeline"
6935
7384
  },
6936
7385
  {
6937
- name: "Sidebar",
7386
+ name: "ScoreBreakdown",
6938
7387
  category: "shell",
6939
- description: "접기/펼치기를 지원하는 compound 사이드바. Header/Content/Footer 구조 + Group/Item으로 네비게이션 구성. Ctrl+[ 단축키.",
6940
- slug: "sidebar",
6941
- exports: ["Sidebar", "useSidebar"],
6942
- keyboardShortcuts: [
7388
+ functionalCategory: "data-display",
7389
+ maturity: "stable",
7390
+ keywords: [
7391
+ "score",
7392
+ "engagement",
7393
+ "health",
7394
+ "rubric",
7395
+ "breakdown"
7396
+ ],
7397
+ description: "합성 점수를 축별로 분해해 보여준다. 총점과 함께 각 축의 값·가중치·의미를 노출해 '43'을 판정이 아니라 발견으로 만든다.",
7398
+ slug: "score-breakdown",
7399
+ exports: ["ScoreBreakdown"],
7400
+ props: { ScoreBreakdown: [
6943
7401
  {
6944
- keys: "Ctrl+[",
6945
- action: "사이드바 접기/펼치기 토글"
7402
+ name: "score",
7403
+ type: "number",
7404
+ required: true,
7405
+ description: "총점"
6946
7406
  },
6947
7407
  {
6948
- keys: "↑ ↓",
6949
- action: "사이드바 항목 간 이동"
7408
+ name: "max",
7409
+ type: "number",
7410
+ default: "100",
7411
+ description: "총점 상한"
6950
7412
  },
6951
7413
  {
6952
- keys: "Home / End",
7414
+ name: "axes",
7415
+ type: "ScoreAxis[]",
7416
+ required: true,
7417
+ description: "기여 축 배열. { id, label, value, hint?, weight? }"
7418
+ },
7419
+ {
7420
+ name: "bands",
7421
+ type: "ScoreBand[]",
7422
+ description: "점수 구간 이름과 색. { min, label, tone? }"
7423
+ },
7424
+ {
7425
+ name: "formula",
7426
+ type: "ReactNode",
7427
+ description: "총점 산출식 설명 한 줄"
7428
+ },
7429
+ {
7430
+ name: "loading",
7431
+ type: "boolean",
7432
+ default: "false",
7433
+ description: "스켈레톤 표시"
7434
+ },
7435
+ {
7436
+ name: "labels",
7437
+ type: "ScoreBreakdownLabels",
7438
+ description: "문구 오버라이드"
7439
+ }
7440
+ ] },
7441
+ usage: {
7442
+ bestPractices: ["축의 weight 합이 1이 되도록 넘겨 총점이 검산 가능하게 한다.", "각 축에 hint를 붙여 낮은 값이 무엇을 뜻하는지 설명한다."],
7443
+ avoid: ["축 없이 총점만 넘기지 않는다 — 그건 StatCard의 일이다."]
7444
+ },
7445
+ testDescribe: "ScoreBreakdown"
7446
+ },
7447
+ {
7448
+ name: "Flyout",
7449
+ category: "shell",
7450
+ functionalCategory: "overlay",
7451
+ maturity: "stable",
7452
+ keywords: [
7453
+ "panel",
7454
+ "detail",
7455
+ "preview",
7456
+ "side-panel",
7457
+ "inspector"
7458
+ ],
7459
+ description: "목록을 화면에 둔 채 레코드를 옆에서 보여주는 상세 패널. 모달이 아니다 — 포커스 트랩·aria-modal·inert 없이 Escape로 닫고 포커스를 되돌린다.",
7460
+ slug: "flyout",
7461
+ exports: ["Flyout"],
7462
+ props: { Flyout: [
7463
+ {
7464
+ name: "open",
7465
+ type: "boolean",
7466
+ required: true,
7467
+ description: "열림 상태"
7468
+ },
7469
+ {
7470
+ name: "onClose",
7471
+ type: "() => void",
7472
+ required: true,
7473
+ description: "닫기 핸들러"
7474
+ },
7475
+ {
7476
+ name: "title",
7477
+ type: "ReactNode",
7478
+ description: "패널 제목"
7479
+ },
7480
+ {
7481
+ name: "description",
7482
+ type: "ReactNode",
7483
+ description: "제목 아래 한 줄 설명"
7484
+ },
7485
+ {
7486
+ name: "type",
7487
+ type: "\"overlay\" | \"inline\"",
7488
+ default: "\"overlay\"",
7489
+ description: "overlay는 페이지 위에 띄우고, inline은 aside로 렌더해 배치를 부모 레이아웃에 맡긴다"
7490
+ },
7491
+ {
7492
+ name: "side",
7493
+ type: "\"right\" | \"left\"",
7494
+ default: "\"right\"",
7495
+ description: "overlay일 때 붙는 방향"
7496
+ },
7497
+ {
7498
+ name: "size",
7499
+ type: "\"sm\" | \"md\" | \"lg\"",
7500
+ default: "\"md\"",
7501
+ description: "패널 너비"
7502
+ },
7503
+ {
7504
+ name: "actions",
7505
+ type: "ReactNode",
7506
+ description: "닫기 버튼 앞 컨트롤"
7507
+ },
7508
+ {
7509
+ name: "footer",
7510
+ type: "ReactNode",
7511
+ description: "하단 고정 영역"
7512
+ },
7513
+ {
7514
+ name: "scrim",
7515
+ type: "boolean",
7516
+ default: "false",
7517
+ description: "뒤 페이지를 어둡게. 기본 off — 목록이 계속 읽혀야 한다"
7518
+ },
7519
+ {
7520
+ name: "closeOnOutsideClick",
7521
+ type: "boolean",
7522
+ default: "true",
7523
+ description: "바깥 클릭 시 닫기 (overlay 전용)"
7524
+ },
7525
+ {
7526
+ name: "hideCloseButton",
7527
+ type: "boolean",
7528
+ default: "false",
7529
+ description: "닫기 버튼 숨김"
7530
+ },
7531
+ {
7532
+ name: "labels",
7533
+ type: "FlyoutLabels",
7534
+ description: "문구 오버라이드"
7535
+ },
7536
+ {
7537
+ name: "children",
7538
+ type: "ReactNode",
7539
+ required: true,
7540
+ description: "패널 본문"
7541
+ }
7542
+ ] },
7543
+ keyboardShortcuts: [{
7544
+ keys: "Escape",
7545
+ action: "패널 닫고 여는 컨트롤로 포커스 복귀"
7546
+ }],
7547
+ usage: {
7548
+ bestPractices: ["목록에서 행을 미리 볼 때 쓴다. 딥링크가 필요한 상세는 별도 페이지를 함께 유지한다.", "넓은 화면에서는 type=\"inline\"으로 목록 옆에 나란히 둔다."],
7549
+ avoid: ["확인이 필요한 파괴적 작업에는 쓰지 않는다 — 그건 Dialog다."]
7550
+ },
7551
+ testDescribe: "Flyout"
7552
+ },
7553
+ {
7554
+ name: "ComparisonSelector",
7555
+ category: "shell",
7556
+ description: "비교 선택기. 고유 레이블이 연결된 Select × 2 + Swap 버튼 조합. CohortComparison, FunnelAnalysis 등에서 사용합니다.",
7557
+ slug: "comparison-selector",
7558
+ exports: ["ComparisonSelector"],
7559
+ props: { ComparisonSelector: [
7560
+ {
7561
+ name: "segments",
7562
+ type: "ComparisonOption[]",
7563
+ required: true,
7564
+ description: "선택 가능한 세그먼트 목록"
7565
+ },
7566
+ {
7567
+ name: "valueA",
7568
+ type: "string",
7569
+ description: "Base 선택값"
7570
+ },
7571
+ {
7572
+ name: "valueB",
7573
+ type: "string",
7574
+ description: "Compare 선택값"
7575
+ },
7576
+ {
7577
+ name: "onChangeA",
7578
+ type: "(value: string) => void",
7579
+ description: "Base 변경 핸들러"
7580
+ },
7581
+ {
7582
+ name: "onChangeB",
7583
+ type: "(value: string) => void",
7584
+ description: "Compare 변경 핸들러"
7585
+ },
7586
+ {
7587
+ name: "labels",
7588
+ type: "ComparisonSelectorLabels",
7589
+ description: "i18n용 라벨 오버라이드"
7590
+ }
7591
+ ] },
7592
+ dependencies: [{
7593
+ slug: "select",
7594
+ category: "core"
7595
+ }, {
7596
+ slug: "button",
7597
+ category: "core"
7598
+ }],
7599
+ testDescribe: "ComparisonSelector"
7600
+ },
7601
+ {
7602
+ name: "ThemeSwitcher",
7603
+ category: "shell",
7604
+ description: "테마 프리셋 선택기. 프리셋과 light/dark/system 모드 전환.",
7605
+ slug: "theme-switcher",
7606
+ exports: ["ThemeSwitcher"],
7607
+ props: { ThemeSwitcher: [{
7608
+ name: "labels",
7609
+ type: "ThemeSwitcherLabels",
7610
+ description: "i18n용 라벨 오버라이드"
7611
+ }] },
7612
+ testDescribe: "ThemeSwitcher"
7613
+ },
7614
+ {
7615
+ name: "PromptTemplate",
7616
+ category: "shell",
7617
+ description: "AI 프롬프트 뷰어/헬퍼. 변수 입력 → 실시간 치환 → 복사 기능.",
7618
+ slug: "prompt-template",
7619
+ exports: ["PromptTemplate"],
7620
+ props: { PromptTemplate: [
7621
+ {
7622
+ name: "title",
7623
+ type: "string",
7624
+ required: true,
7625
+ description: "프롬프트 제목"
7626
+ },
7627
+ {
7628
+ name: "description",
7629
+ type: "string",
7630
+ description: "프롬프트 설명 (헤더 아래 표시)"
7631
+ },
7632
+ {
7633
+ name: "template",
7634
+ type: "string",
7635
+ required: true,
7636
+ description: "프롬프트 템플릿. {변수명}으로 치환 가능"
7637
+ },
7638
+ {
7639
+ name: "variables",
7640
+ type: "PromptVariable[]",
7641
+ description: "변수 정의 배열. { name, label, placeholder?, defaultValue? }"
7642
+ },
7643
+ {
7644
+ name: "labels",
7645
+ type: "PromptTemplateLabels",
7646
+ description: "i18n용 라벨 오버라이드"
7647
+ }
7648
+ ] },
7649
+ dependencies: [{
7650
+ slug: "input",
7651
+ category: "core"
7652
+ }],
7653
+ testDescribe: "PromptTemplate"
7654
+ },
7655
+ {
7656
+ name: "Sidebar",
7657
+ category: "shell",
7658
+ description: "접기/펼치기를 지원하는 compound 사이드바. Header/Content/Footer 구조 + Group/Item으로 네비게이션 구성. Ctrl+[ 단축키.",
7659
+ slug: "sidebar",
7660
+ exports: ["Sidebar", "useSidebar"],
7661
+ keyboardShortcuts: [
7662
+ {
7663
+ keys: "Ctrl+[",
7664
+ action: "사이드바 접기/펼치기 토글"
7665
+ },
7666
+ {
7667
+ keys: "↑ ↓",
7668
+ action: "사이드바 항목 간 이동"
7669
+ },
7670
+ {
7671
+ keys: "Home / End",
6953
7672
  action: "첫 번째/마지막 사이드바 항목으로 이동"
6954
7673
  }
6955
7674
  ],
@@ -7187,7 +7906,7 @@ const filters = [
7187
7906
  props: { AsyncCombobox: [
7188
7907
  {
7189
7908
  name: "onSearch",
7190
- type: "(q: string) => Promise<string[]>",
7909
+ type: "(query: string) => Promise<string[]>",
7191
7910
  required: true,
7192
7911
  description: "검색 콜백"
7193
7912
  },
@@ -7207,7 +7926,11 @@ const filters = [
7207
7926
  name: "placeholder",
7208
7927
  type: "string",
7209
7928
  default: "\"Search…\"",
7210
- description: "플레이스홀더"
7929
+ description: "플레이스홀더",
7930
+ deprecated: {
7931
+ since: "1.9.0",
7932
+ replacement: "labels.placeholder"
7933
+ }
7211
7934
  },
7212
7935
  {
7213
7936
  name: "onSelect",
@@ -7222,7 +7945,11 @@ const filters = [
7222
7945
  {
7223
7946
  name: "loadingText",
7224
7947
  type: "string",
7225
- description: "로딩 문구. labels.loading 사용 권장"
7948
+ description: "로딩 문구",
7949
+ deprecated: {
7950
+ since: "1.9.0",
7951
+ replacement: "labels.loading"
7952
+ }
7226
7953
  }
7227
7954
  ] },
7228
7955
  dependencies: [{
@@ -7258,6 +7985,14 @@ const filters = [
7258
7985
  description: "커스텀 CSS 클래스"
7259
7986
  }
7260
7987
  ] },
7988
+ ariaRole: "tablist",
7989
+ keyboardShortcuts: [{
7990
+ keys: "↑ ↓",
7991
+ action: "세로 탭 사이 이동 및 선택"
7992
+ }, {
7993
+ keys: "Home / End",
7994
+ action: "첫 번째 또는 마지막 탭으로 이동"
7995
+ }],
7261
7996
  dependencies: [{
7262
7997
  slug: "tabs",
7263
7998
  category: "core"
@@ -7330,12 +8065,30 @@ const filters = [
7330
8065
  default: "24",
7331
8066
  description: "들여쓰기 크기 (px)"
7332
8067
  },
8068
+ {
8069
+ name: "getRowLabel",
8070
+ type: "(node: TreeNode<T>) => string",
8071
+ description: "행별 펼침/접기 컨트롤의 접근 가능한 이름 생성기"
8072
+ },
8073
+ {
8074
+ name: "aria-label",
8075
+ type: "string",
8076
+ description: "표의 접근 가능한 이름"
8077
+ },
7333
8078
  {
7334
8079
  name: "className",
7335
8080
  type: "string",
7336
8081
  description: "커스텀 CSS 클래스"
7337
8082
  }
7338
8083
  ] },
8084
+ ariaRole: "table",
8085
+ keyboardShortcuts: [{
8086
+ keys: "Tab",
8087
+ action: "펼침/접기 컨트롤 사이 이동"
8088
+ }, {
8089
+ keys: "Enter / Space",
8090
+ action: "하위 행 펼치기 또는 접기"
8091
+ }],
7339
8092
  dependencies: [{
7340
8093
  slug: "data-table",
7341
8094
  category: "shells"
@@ -7399,7 +8152,7 @@ const filters = [
7399
8152
  },
7400
8153
  {
7401
8154
  name: "onDragEnd",
7402
- type: "(itemId, fromCol, toCol, toIndex) => void",
8155
+ type: "(itemId: string, fromColumnId: string, toColumnId: string, toIndex: number) => void",
7403
8156
  required: true,
7404
8157
  description: "드래그 완료 콜백"
7405
8158
  },
@@ -7635,7 +8388,7 @@ const filters = [
7635
8388
  },
7636
8389
  {
7637
8390
  name: "onImport",
7638
- type: "(mapping, data) => void",
8391
+ type: "(mapping: Record<string, string>, data: string[][]) => void",
7639
8392
  required: true,
7640
8393
  description: "임포트 확정 콜백"
7641
8394
  },
@@ -7687,12 +8440,32 @@ const filters = [
7687
8440
  default: "\"Menu\"",
7688
8441
  description: "트리거 버튼 텍스트"
7689
8442
  },
8443
+ {
8444
+ name: "aria-label",
8445
+ type: "string",
8446
+ description: "열린 메가 메뉴 탐색 영역의 접근 가능한 이름"
8447
+ },
7690
8448
  {
7691
8449
  name: "className",
7692
8450
  type: "string",
7693
8451
  description: "커스텀 CSS 클래스"
7694
8452
  }
7695
8453
  ] },
8454
+ ariaRole: "navigation",
8455
+ keyboardShortcuts: [
8456
+ {
8457
+ keys: "Enter / Space",
8458
+ action: "메가 메뉴 열기"
8459
+ },
8460
+ {
8461
+ keys: "Tab",
8462
+ action: "메뉴 링크와 액션 사이 이동"
8463
+ },
8464
+ {
8465
+ keys: "Escape",
8466
+ action: "메가 메뉴 닫기"
8467
+ }
8468
+ ],
7696
8469
  dependencies: [{
7697
8470
  slug: "popover",
7698
8471
  category: "core"
@@ -8289,6 +9062,16 @@ const messages = [
8289
9062
  slug: "time-series-panel",
8290
9063
  exports: ["TimeSeriesPanel"],
8291
9064
  props: { TimeSeriesPanel: [
9065
+ {
9066
+ name: "title",
9067
+ type: "ReactNode",
9068
+ description: "패널 제목. aria-label이 없으면 region 이름으로 연결됩니다"
9069
+ },
9070
+ {
9071
+ name: "description",
9072
+ type: "ReactNode",
9073
+ description: "패널 보조 설명"
9074
+ },
8292
9075
  {
8293
9076
  name: "data",
8294
9077
  type: "TimeSeriesPoint[]",
@@ -8335,6 +9118,11 @@ const messages = [
8335
9118
  type: "number",
8336
9119
  default: "280",
8337
9120
  description: "차트 높이"
9121
+ },
9122
+ {
9123
+ name: "aria-label",
9124
+ type: "string",
9125
+ description: "제목 대신 사용할 패널 region의 접근 가능한 이름"
8338
9126
  }
8339
9127
  ] },
8340
9128
  ariaRole: "region",
@@ -8518,9 +9306,355 @@ const messages = [
8518
9306
  ] },
8519
9307
  ariaRole: "complementary",
8520
9308
  testDescribe: "FieldSidebar"
9309
+ },
9310
+ {
9311
+ name: "InspectorLayout",
9312
+ category: "shells",
9313
+ description: "목록과 인스펙터의 2-pane 레이아웃. 넓으면 밀어내고(push) 좁으면 덮으며(overlay), 닫을 때 포커스를 열었던 자리로 되돌립니다.",
9314
+ slug: "inspector-layout",
9315
+ exports: ["InspectorLayout"],
9316
+ props: { InspectorLayout: [
9317
+ {
9318
+ name: "children",
9319
+ type: "ReactNode",
9320
+ required: true,
9321
+ description: "목록. 인스펙터가 열려도 마운트와 스크롤을 유지"
9322
+ },
9323
+ {
9324
+ name: "inspector",
9325
+ type: "ReactNode",
9326
+ description: "인스펙터 내용. 닫혀 있으면 렌더하지 않음"
9327
+ },
9328
+ {
9329
+ name: "open",
9330
+ type: "boolean",
9331
+ required: true,
9332
+ description: "열림"
9333
+ },
9334
+ {
9335
+ name: "onClose",
9336
+ type: "() => void",
9337
+ required: true,
9338
+ description: "닫기"
9339
+ },
9340
+ {
9341
+ name: "title",
9342
+ type: "ReactNode",
9343
+ description: "인스펙터 제목"
9344
+ },
9345
+ {
9346
+ name: "description",
9347
+ type: "ReactNode",
9348
+ description: "제목 아래 한 줄"
9349
+ },
9350
+ {
9351
+ name: "actions",
9352
+ type: "ReactNode",
9353
+ description: "닫기 버튼 옆 컨트롤"
9354
+ },
9355
+ {
9356
+ name: "footer",
9357
+ type: "ReactNode",
9358
+ description: "인스펙터 하단"
9359
+ },
9360
+ {
9361
+ name: "breakpoint",
9362
+ type: "\"lg\" | \"xl\" | \"2xl\"",
9363
+ default: "\"xl\"",
9364
+ description: "이 폭 이상이면 push"
9365
+ },
9366
+ {
9367
+ name: "width",
9368
+ type: "\"sm\" | \"md\" | \"lg\"",
9369
+ default: "\"md\"",
9370
+ description: "push 레이아웃에서의 인스펙터 폭"
9371
+ }
9372
+ ] },
9373
+ usage: {
9374
+ bestPractices: ["목록 → 상세 → 목록 왕복이 잦은 화면에 씁니다. 다이얼로그는 목록을 가져가 버립니다."],
9375
+ avoid: ["확인이 필요한 파괴적 작업에는 ConfirmDialog를 씁니다.", "포커스 복귀를 직접 다루지 않습니다. Flyout이 소유하며, 부모가 겹쳐 잡으면 패널을 열었던 요소로 오인합니다."]
9376
+ },
9377
+ testDescribe: "InspectorLayout"
9378
+ },
9379
+ {
9380
+ name: "QueryFilterBar",
9381
+ category: "shells",
9382
+ description: "QueryBar와 필터 버튼이 하나의 Query를 공유합니다. 버튼은 상태를 갖지 않고 Query를 읽어 활성 여부를 계산합니다.",
9383
+ slug: "query-filter-bar",
9384
+ exports: ["QueryFilterBar", "hasActiveQuery"],
9385
+ props: { QueryFilterBar: [
9386
+ {
9387
+ name: "value",
9388
+ type: "Query",
9389
+ required: true,
9390
+ description: "필터 상태 전부"
9391
+ },
9392
+ {
9393
+ name: "onChange",
9394
+ type: "(query: Query) => void",
9395
+ required: true,
9396
+ description: "Query 변경"
9397
+ },
9398
+ {
9399
+ name: "filters",
9400
+ type: "QueryFilterDef[]",
9401
+ required: true,
9402
+ description: "필터 컨트롤 정의 (is / field_value_selection / field_value_toggle)"
9403
+ },
9404
+ {
9405
+ name: "fields",
9406
+ type: "QueryField[]",
9407
+ description: "텍스트 입력 자동완성"
9408
+ },
9409
+ {
9410
+ name: "actions",
9411
+ type: "ReactNode",
9412
+ description: "필터 행 끝 슬롯"
9413
+ },
9414
+ {
9415
+ name: "showClauses",
9416
+ type: "boolean",
9417
+ default: "true",
9418
+ description: "절을 제거 가능한 칩으로 표시"
9419
+ },
9420
+ {
9421
+ name: "live",
9422
+ type: "boolean",
9423
+ default: "false",
9424
+ description: "매 입력마다 커밋"
9425
+ },
9426
+ {
9427
+ name: "disabled",
9428
+ type: "boolean",
9429
+ default: "false",
9430
+ description: "전체 비활성"
9431
+ },
9432
+ {
9433
+ name: "queryBarLabels",
9434
+ type: "QueryBarLabels",
9435
+ description: "QueryBar로 통과되는 나머지 문구. placeholder/ariaLabel은 labels가 이깁니다"
9436
+ }
9437
+ ] },
9438
+ usage: {
9439
+ bestPractices: ["URL에 Query 한 줄만 실어 필터 상태 전체를 공유 가능하게 만듭니다."],
9440
+ avoid: ["필터 버튼이 자체 상태를 갖게 하지 않습니다. 진실 원천이 둘로 갈라집니다."]
9441
+ },
9442
+ testDescribe: "QueryFilterBar"
9443
+ },
9444
+ {
9445
+ name: "CatalogFrame",
9446
+ category: "shells",
9447
+ description: "카탈로그 목록의 공통 뼈대. 헤더·통계·고지·필터·인스펙터·페이지네이션·일괄 작업과 빈/에러 상태를 담고, 필터나 페이지가 바뀌면 선택을 비웁니다.",
9448
+ slug: "catalog-frame",
9449
+ exports: [
9450
+ "CatalogFrame",
9451
+ "CatalogRowActions",
9452
+ "CatalogCellControls"
9453
+ ],
9454
+ props: {
9455
+ CatalogFrame: [
9456
+ {
9457
+ name: "children",
9458
+ type: "ReactNode",
9459
+ required: true,
9460
+ description: "테이블. 빈/에러 상태에서는 대체됨"
9461
+ },
9462
+ {
9463
+ name: "isEmpty",
9464
+ type: "boolean",
9465
+ required: true,
9466
+ description: "테이블이 0행인지"
9467
+ },
9468
+ {
9469
+ name: "query",
9470
+ type: "Query",
9471
+ required: true,
9472
+ description: "필터 상태"
9473
+ },
9474
+ {
9475
+ name: "onQueryChange",
9476
+ type: "(query: Query) => void",
9477
+ required: true,
9478
+ description: "Query 변경"
9479
+ },
9480
+ {
9481
+ name: "filters",
9482
+ type: "QueryFilterDef[]",
9483
+ description: "필터 정의"
9484
+ },
9485
+ {
9486
+ name: "stats",
9487
+ type: "StatCardType[]",
9488
+ description: "통계 카드"
9489
+ },
9490
+ {
9491
+ name: "onStatClick",
9492
+ type: "(stat: StatCardType) => void",
9493
+ description: "카드 클릭 = 필터"
9494
+ },
9495
+ {
9496
+ name: "notice",
9497
+ type: "ReactNode",
9498
+ description: "목록 위 고지"
9499
+ },
9500
+ {
9501
+ name: "emptyActions",
9502
+ type: "ReactNode",
9503
+ description: "필터 없는 빈 상태의 다음 행동 (필터된 빈 상태는 필터 지우기 유지)"
9504
+ },
9505
+ {
9506
+ name: "header",
9507
+ type: "ReactNode",
9508
+ description: "헤더 슬롯"
9509
+ },
9510
+ {
9511
+ name: "actions",
9512
+ type: "ReactNode",
9513
+ description: "액션 슬롯"
9514
+ },
9515
+ {
9516
+ name: "toolbar",
9517
+ type: "ReactNode",
9518
+ description: "통계와 필터 사이 컨트롤 (스코프 세그먼트 등)"
9519
+ },
9520
+ {
9521
+ name: "inspector",
9522
+ type: "ReactNode",
9523
+ description: "인스펙터 내용"
9524
+ },
9525
+ {
9526
+ name: "inspectorOpen",
9527
+ type: "boolean",
9528
+ required: true,
9529
+ description: "인스펙터 열림"
9530
+ },
9531
+ {
9532
+ name: "onInspectorClose",
9533
+ type: "() => void",
9534
+ required: true,
9535
+ description: "인스펙터 닫기"
9536
+ },
9537
+ {
9538
+ name: "inspectorTitle",
9539
+ type: "ReactNode",
9540
+ description: "인스펙터 제목"
9541
+ },
9542
+ {
9543
+ name: "inspectorDescription",
9544
+ type: "ReactNode",
9545
+ description: "인스펙터 설명"
9546
+ },
9547
+ {
9548
+ name: "inspectorActions",
9549
+ type: "ReactNode",
9550
+ description: "인스펙터 헤더 컨트롤"
9551
+ },
9552
+ {
9553
+ name: "inspectorFooter",
9554
+ type: "ReactNode",
9555
+ description: "인스펙터 하단"
9556
+ },
9557
+ {
9558
+ name: "inspectorWidth",
9559
+ type: "\"sm\" | \"md\" | \"lg\"",
9560
+ description: "push 레이아웃 폭"
9561
+ },
9562
+ {
9563
+ name: "page",
9564
+ type: "number",
9565
+ default: "1",
9566
+ description: "현재 페이지"
9567
+ },
9568
+ {
9569
+ name: "pageSize",
9570
+ type: "number",
9571
+ default: "50",
9572
+ description: "페이지 크기"
9573
+ },
9574
+ {
9575
+ name: "totalCount",
9576
+ type: "number",
9577
+ description: "필터 반영 총 개수"
9578
+ },
9579
+ {
9580
+ name: "onPageChange",
9581
+ type: "(page: number) => void",
9582
+ description: "페이지 변경"
9583
+ },
9584
+ {
9585
+ name: "selectedKeys",
9586
+ type: "string[]",
9587
+ description: "선택된 행"
9588
+ },
9589
+ {
9590
+ name: "onSelectionChange",
9591
+ type: "(keys: string[]) => void",
9592
+ description: "선택 변경. 필터·페이지가 바뀌면 프레임이 비웁니다"
9593
+ },
9594
+ {
9595
+ name: "bulkActions",
9596
+ type: "FloatingAction[]",
9597
+ description: "일괄 작업"
9598
+ },
9599
+ {
9600
+ name: "onBulkAction",
9601
+ type: "(actionId: string) => void",
9602
+ description: "일괄 작업 실행"
9603
+ },
9604
+ {
9605
+ name: "error",
9606
+ type: "ReactNode",
9607
+ description: "읽기 실패. 빈 목록과 다른 답"
9608
+ },
9609
+ {
9610
+ name: "onRetry",
9611
+ type: "() => void",
9612
+ description: "재시도"
9613
+ },
9614
+ {
9615
+ name: "labels",
9616
+ type: "CatalogFrameLabels",
9617
+ description: "빈/필터된 빈/에러 문구"
9618
+ }
9619
+ ],
9620
+ CatalogRowActions: [
9621
+ {
9622
+ name: "item",
9623
+ type: "T",
9624
+ required: true,
9625
+ description: "이 행의 항목"
9626
+ },
9627
+ {
9628
+ name: "actions",
9629
+ type: "CatalogRowAction<T>[]",
9630
+ required: true,
9631
+ description: "행 액션"
9632
+ },
9633
+ {
9634
+ name: "visibleCount",
9635
+ type: "number",
9636
+ default: "2",
9637
+ description: "오버플로 전에 노출할 개수. available 필터링 후에 적용됨"
9638
+ },
9639
+ {
9640
+ name: "labels",
9641
+ type: "CatalogRowActionsLabels",
9642
+ description: "그룹·오버플로 라벨"
9643
+ }
9644
+ ],
9645
+ CatalogCellControls: [{
9646
+ name: "children",
9647
+ type: "ReactNode",
9648
+ required: true,
9649
+ description: "행 클릭으로 새면 안 되는 셀 컨트롤"
9650
+ }]
9651
+ },
9652
+ usage: {
9653
+ bestPractices: ["카탈로그 성격의 목록 화면은 이 프레임 위에 컬럼 정의와 labels만 얹습니다."],
9654
+ avoid: ["선택 초기화를 호출자에게 맡기지 않습니다 — 보이지 않는 행에 일괄 작업이 적용됩니다."]
9655
+ },
9656
+ testDescribe: "CatalogFrame"
8521
9657
  }
8522
9658
  ];
8523
9659
  //#endregion
8524
9660
  export { coreMetas, shellMetas, visualMetas };
8525
-
8526
- //# sourceMappingURL=meta.js.map