@reopt-ai/opt-ui 1.14.0 → 1.15.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 (38) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/COMPONENT_CATALOG.md +222 -69
  3. package/README.md +26 -0
  4. package/dist/core/index.cjs +4 -4
  5. package/dist/core/index.d.cts +2 -2
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +2 -2
  8. package/dist/docs/02-components/01-core.md +37 -33
  9. package/dist/docs/02-components/02-visuals.md +1 -1
  10. package/dist/docs/02-components/03-shells.md +191 -36
  11. package/dist/docs/02-components/04-surfaces.md +1 -1
  12. package/dist/docs/02-components/index.md +4 -4
  13. package/dist/id-registry.cjs +24 -0
  14. package/dist/id-registry.js +24 -0
  15. package/dist/id-registry.json +51 -0
  16. package/dist/index.cjs +445 -381
  17. package/dist/index.d.cts +16 -13
  18. package/dist/index.d.ts +16 -13
  19. package/dist/index.js +303 -245
  20. package/dist/{key-pad-menu-CqFI6la9.js → key-pad-menu-BCOeYvsH.js} +50 -61
  21. package/dist/{key-pad-menu-D6pYzaj-.d.cts → key-pad-menu-CaIsTHB2.d.cts} +12 -8
  22. package/dist/{key-pad-menu-BgtSL3xe.cjs → key-pad-menu-Jaqxhx9P.cjs} +48 -71
  23. package/dist/{key-pad-menu-Yh7Fpb8m.d.ts → key-pad-menu-odAEycIX.d.ts} +12 -8
  24. package/dist/meta.cjs +447 -7
  25. package/dist/meta.js +447 -7
  26. package/dist/{field-sidebar-mSVCMAZp.js → replay-player-layout-BVUNsUS1.js} +891 -191
  27. package/dist/{field-sidebar-ePqFX6MN.cjs → replay-player-layout-Cmzptq89.cjs} +931 -189
  28. package/dist/{field-sidebar-CJ4HxP8D.d.cts → replay-player-layout-DpqdfdQ6.d.ts} +246 -3
  29. package/dist/{field-sidebar-DnhpcT2A.d.ts → replay-player-layout-DuZW-gFF.d.cts} +246 -3
  30. package/dist/shells/index.cjs +58 -52
  31. package/dist/shells/index.d.cts +2 -2
  32. package/dist/shells/index.d.ts +2 -2
  33. package/dist/shells/index.js +2 -2
  34. package/dist/{text-truncate-Bd-0WkSP.js → text-truncate-B-8nJgnu.js} +200 -101
  35. package/dist/{text-truncate-Bbul-jMD.d.cts → text-truncate-DOxUijpB.d.cts} +2 -0
  36. package/dist/{text-truncate-Bbul-jMD.d.ts → text-truncate-DOxUijpB.d.ts} +2 -0
  37. package/dist/{text-truncate-BeYW6XZu.cjs → text-truncate-gQhpMy-Z.cjs} +214 -97
  38. package/package.json +1 -1
package/dist/meta.js CHANGED
@@ -531,7 +531,7 @@ const coreMetas = [
531
531
  name: "onSave",
532
532
  type: "(next: string) => boolean | void | Promise<boolean | void>",
533
533
  required: true,
534
- description: "저장. false 반환하면 거절하고 편집을 유지"
534
+ description: "저장. false 반환 또는 Promise 실패 시 입력을 유지하고 오류를 알림"
535
535
  },
536
536
  {
537
537
  name: "as",
@@ -575,7 +575,7 @@ const coreMetas = [
575
575
  {
576
576
  name: "labels",
577
577
  type: "InlineEditLabels",
578
- description: "지역화 문자열"
578
+ description: "입력 이름(input), 편집·저장·취소·오류 안내 문자열"
579
579
  },
580
580
  {
581
581
  name: "className",
@@ -2211,6 +2211,21 @@ export function Example() {
2211
2211
  }
2212
2212
  ],
2213
2213
  FormSelect: [
2214
+ {
2215
+ name: "id",
2216
+ type: "string",
2217
+ description: "트리거 ID. 외부 FormLabel의 htmlFor와 연결"
2218
+ },
2219
+ {
2220
+ name: "aria-label",
2221
+ type: "string",
2222
+ description: "입력의 접근 가능한 이름"
2223
+ },
2224
+ {
2225
+ name: "aria-describedby",
2226
+ type: "string",
2227
+ description: "설명·오류 요소의 ID 목록"
2228
+ },
2214
2229
  {
2215
2230
  name: "name",
2216
2231
  type: "string",
@@ -4302,6 +4317,11 @@ export function Example() { return <PopoverRoot><PopoverTrigger>공유 안내</P
4302
4317
  type: "(value: number) => void",
4303
4318
  description: "값 변경 핸들러"
4304
4319
  },
4320
+ {
4321
+ name: "onValueCommit",
4322
+ type: "(value: number) => void",
4323
+ description: "포인터·키보드 편집 완료 시 한 번 호출. 취소와 외부 값 갱신은 제외"
4324
+ },
4305
4325
  {
4306
4326
  name: "min",
4307
4327
  type: "number",
@@ -5664,7 +5684,7 @@ const shellMetas = [
5664
5684
  description: "폼 필드 정의 배열"
5665
5685
  }, {
5666
5686
  name: "onSubmit",
5667
- type: "(values: Record<string, unknown>) => void",
5687
+ type: "(values: Record<string, unknown>) => void | Promise<void>",
5668
5688
  description: "제출 핸들러"
5669
5689
  }] },
5670
5690
  dependencies: [
@@ -6535,7 +6555,7 @@ const filters = [
6535
6555
  },
6536
6556
  {
6537
6557
  name: "onSubmit",
6538
- type: "(values: { email: string; password: string; confirmPassword?: string; rememberMe?: boolean; mode: \"login\" | \"signup\" }) => void",
6558
+ type: "(values: { email: string; password: string; confirmPassword?: string; rememberMe?: boolean; mode: \"login\" | \"signup\" }) => void | Promise<void>",
6539
6559
  description: "제출 핸들러"
6540
6560
  },
6541
6561
  {
@@ -6578,7 +6598,7 @@ const filters = [
6578
6598
  },
6579
6599
  {
6580
6600
  name: "onSubmit",
6581
- type: "(values: Record<string, unknown>) => void",
6601
+ type: "(values: Record<string, unknown>) => void | Promise<void>",
6582
6602
  description: "제출 핸들러"
6583
6603
  },
6584
6604
  {
@@ -6646,7 +6666,7 @@ const filters = [
6646
6666
  },
6647
6667
  {
6648
6668
  name: "onSubmit",
6649
- type: "(values: string[]) => void",
6669
+ type: "(values: string[]) => void | Promise<void>",
6650
6670
  description: "제출 핸들러"
6651
6671
  },
6652
6672
  {
@@ -6700,7 +6720,7 @@ const filters = [
6700
6720
  },
6701
6721
  {
6702
6722
  name: "onSubmit",
6703
- type: "(values: { title: string; description: string; file: File | null }) => void",
6723
+ type: "(values: { title: string; description: string; file: File | null }) => void | Promise<void>",
6704
6724
  description: "제출 핸들러"
6705
6725
  },
6706
6726
  {
@@ -11180,6 +11200,426 @@ const messages = [
11180
11200
  category: "core"
11181
11201
  }],
11182
11202
  testDescribe: "QueryWorkspace"
11203
+ },
11204
+ {
11205
+ name: "ReplayScrubber",
11206
+ category: "shell",
11207
+ functionalCategory: "data-display",
11208
+ maturity: "beta",
11209
+ keywords: [
11210
+ "replay",
11211
+ "scrubber",
11212
+ "seekbar",
11213
+ "timeline",
11214
+ "session recording",
11215
+ "playhead"
11216
+ ],
11217
+ description: "세션 리플레이의 탐색 트랙. 활동/비활성/미로딩 구간, 활동 밀도, 이벤트 마커를 한 플레이헤드 아래에 겹쳐 그리고, 드래그·키보드 탐색이 끝났을 때만 seek을 커밋한다.",
11218
+ slug: "replay-scrubber",
11219
+ exports: ["ReplayScrubber", "groupReplayMarkers"],
11220
+ props: { ReplayScrubber: [
11221
+ {
11222
+ name: "start",
11223
+ type: "number",
11224
+ required: true,
11225
+ description: "재생 가능한 첫 시각(절대값, 보통 epoch ms)"
11226
+ },
11227
+ {
11228
+ name: "end",
11229
+ type: "number",
11230
+ required: true,
11231
+ description: "재생 가능한 마지막 시각"
11232
+ },
11233
+ {
11234
+ name: "position",
11235
+ type: "number",
11236
+ required: true,
11237
+ description: "현재 플레이헤드. [start, end]로 클램프"
11238
+ },
11239
+ {
11240
+ name: "segments",
11241
+ type: "ReplayScrubberSegment[]",
11242
+ description: "활동 분류 구간. active/inactive/gap/buffer"
11243
+ },
11244
+ {
11245
+ name: "buffered",
11246
+ type: "Array<{ start: number; end: number }>",
11247
+ description: "이미 로드된 범위"
11248
+ },
11249
+ {
11250
+ name: "activity",
11251
+ type: "number[]",
11252
+ description: "0..1로 정규화한 활동 밀도 버킷. 로그 스케일 영역으로 그린다"
11253
+ },
11254
+ {
11255
+ name: "markers",
11256
+ type: "ReplayScrubberMarker[]",
11257
+ description: "트랙 위 관심 지점. 가까운 마커는 하나로 합쳐진다"
11258
+ },
11259
+ {
11260
+ name: "onSeek",
11261
+ type: "(time: number) => void",
11262
+ required: true,
11263
+ description: "포인터·키보드 조작이 끝났을 때 한 번 호출"
11264
+ },
11265
+ {
11266
+ name: "onPreview",
11267
+ type: "(time: number | null) => void",
11268
+ description: "드래그·호버 중 미리보기 시각. 비싼 seek에 쓰지 않는다"
11269
+ },
11270
+ {
11271
+ name: "onMarkerSelect",
11272
+ type: "(marker: ReplayScrubberMarker) => void",
11273
+ description: "마커 선택. 생략하면 해당 시각으로 seek"
11274
+ },
11275
+ {
11276
+ name: "formatTime",
11277
+ type: "(time: number) => string",
11278
+ description: "배지와 aria-valuetext 포맷. 기본 start 기준 m:ss"
11279
+ },
11280
+ {
11281
+ name: "stepMs",
11282
+ type: "number",
11283
+ default: "5000",
11284
+ description: "화살표 키 이동량"
11285
+ },
11286
+ {
11287
+ name: "largeStepMs",
11288
+ type: "number",
11289
+ default: "30000",
11290
+ description: "PageUp/PageDown·Shift+화살표 이동량"
11291
+ },
11292
+ {
11293
+ name: "disabled",
11294
+ type: "boolean",
11295
+ default: "false",
11296
+ description: "모든 조작을 비활성화"
11297
+ },
11298
+ {
11299
+ name: "labels",
11300
+ type: "ReplayScrubberLabels",
11301
+ description: "접근성 문구 오버라이드"
11302
+ },
11303
+ {
11304
+ name: "className",
11305
+ type: "string",
11306
+ description: "추가 클래스"
11307
+ }
11308
+ ] },
11309
+ usage: {
11310
+ bestPractices: ["onSeek에만 실제 seek을 연결한다. onPreview는 시각 표시용이다.", "segments의 buffer는 '아직 안 옴'이고 gap은 '없음'이다. 플레이어가 기다릴지 건너뛸지가 갈린다."],
11311
+ avoid: ["마커를 수천 개 넘기지 않는다. 우선순위로 100개 안팎으로 추려서 넘긴다."]
11312
+ },
11313
+ testDescribe: "ReplayScrubber",
11314
+ examples: [{
11315
+ title: "구간·마커가 있는 탐색 트랙",
11316
+ code: `import { ReplayScrubber } from "@reopt-ai/opt-ui";
11317
+
11318
+ <ReplayScrubber
11319
+ start={recording.start}
11320
+ end={recording.end}
11321
+ position={playhead}
11322
+ segments={[
11323
+ { start: recording.start, end: recording.start + 60_000, kind: "active" },
11324
+ { start: recording.start + 60_000, end: recording.end, kind: "inactive" },
11325
+ ]}
11326
+ markers={[{ id: "err", time: errorAt, label: "TypeError", tone: "danger" }]}
11327
+ onSeek={(time) => player.seek(time)}
11328
+ labels={{ track: "녹화 탐색" }}
11329
+ />`
11330
+ }]
11331
+ },
11332
+ {
11333
+ name: "ReplayTransport",
11334
+ category: "shell",
11335
+ functionalCategory: "actions",
11336
+ maturity: "beta",
11337
+ keywords: [
11338
+ "replay",
11339
+ "transport",
11340
+ "play",
11341
+ "pause",
11342
+ "speed",
11343
+ "skip inactivity",
11344
+ "session recording"
11345
+ ],
11346
+ description: "세션 리플레이 재생 컨트롤. 재생/일시정지, 앞뒤 점프, 배속 메뉴, 비활성 건너뛰기 토글, 전체화면, 경과/시계 타임코드 전환을 제공하며 재생 상태는 호스트가 소유한다.",
11347
+ slug: "replay-transport",
11348
+ exports: ["ReplayTransport", "formatReplayElapsed"],
11349
+ props: { ReplayTransport: [
11350
+ {
11351
+ name: "playing",
11352
+ type: "boolean",
11353
+ required: true,
11354
+ description: "재생 중 여부"
11355
+ },
11356
+ {
11357
+ name: "ended",
11358
+ type: "boolean",
11359
+ default: "false",
11360
+ description: "끝에 도달. 재생 버튼이 다시 시작을 제안"
11361
+ },
11362
+ {
11363
+ name: "position",
11364
+ type: "number",
11365
+ required: true,
11366
+ description: "절대 플레이헤드 시각"
11367
+ },
11368
+ {
11369
+ name: "start",
11370
+ type: "number",
11371
+ required: true,
11372
+ description: "녹화 시작 시각(절대값)"
11373
+ },
11374
+ {
11375
+ name: "end",
11376
+ type: "number",
11377
+ required: true,
11378
+ description: "녹화 종료 시각(절대값)"
11379
+ },
11380
+ {
11381
+ name: "onTogglePlay",
11382
+ type: "() => void",
11383
+ required: true,
11384
+ description: "재생/일시정지 토글. ended면 처음부터 다시 시작"
11385
+ },
11386
+ {
11387
+ name: "onSkip",
11388
+ type: "(deltaMs: number) => void",
11389
+ required: true,
11390
+ description: "부호 있는 시간만큼 이동"
11391
+ },
11392
+ {
11393
+ name: "onSkipToStart",
11394
+ type: "() => void",
11395
+ description: "처음으로. 생략하면 버튼 숨김"
11396
+ },
11397
+ {
11398
+ name: "jumpMs",
11399
+ type: "number",
11400
+ default: "10000",
11401
+ description: "앞뒤 점프 버튼 이동량(ms)"
11402
+ },
11403
+ {
11404
+ name: "speed",
11405
+ type: "number",
11406
+ required: true,
11407
+ description: "현재 배속"
11408
+ },
11409
+ {
11410
+ name: "speeds",
11411
+ type: "readonly number[]",
11412
+ default: "[0.5, 1, 1.5, 2, 3, 4, 8, 16]",
11413
+ description: "선택 가능한 배속 목록"
11414
+ },
11415
+ {
11416
+ name: "onSpeedChange",
11417
+ type: "(speed: number) => void",
11418
+ required: true,
11419
+ description: "배속 변경 콜백"
11420
+ },
11421
+ {
11422
+ name: "skipInactive",
11423
+ type: "boolean",
11424
+ required: true,
11425
+ description: "비활성 구간 건너뛰기 여부"
11426
+ },
11427
+ {
11428
+ name: "onSkipInactiveChange",
11429
+ type: "(skip: boolean) => void",
11430
+ required: true,
11431
+ description: "건너뛰기 토글 콜백"
11432
+ },
11433
+ {
11434
+ name: "fullscreen",
11435
+ type: "boolean",
11436
+ description: "전체화면 상태(호스트 소유)"
11437
+ },
11438
+ {
11439
+ name: "onFullscreenChange",
11440
+ type: "(fullscreen: boolean) => void",
11441
+ description: "있으면 전체화면 버튼 표시"
11442
+ },
11443
+ {
11444
+ name: "timecode",
11445
+ type: "\"elapsed\" | \"clock\"",
11446
+ default: "\"elapsed\"",
11447
+ description: "경과 시간 또는 실제 시각 표시 모드"
11448
+ },
11449
+ {
11450
+ name: "onTimecodeChange",
11451
+ type: "(mode: \"elapsed\" | \"clock\") => void",
11452
+ description: "있으면 타임코드를 클릭해 전환"
11453
+ },
11454
+ {
11455
+ name: "formatClock",
11456
+ type: "(time: number) => string",
11457
+ description: "clock 모드 포맷"
11458
+ },
11459
+ {
11460
+ name: "disabled",
11461
+ type: "boolean",
11462
+ default: "false",
11463
+ description: "모든 조작을 비활성화"
11464
+ },
11465
+ {
11466
+ name: "children",
11467
+ type: "ReactNode",
11468
+ description: "오른쪽에 추가 컨트롤"
11469
+ },
11470
+ {
11471
+ name: "labels",
11472
+ type: "ReplayTransportLabels",
11473
+ description: "문구 오버라이드"
11474
+ },
11475
+ {
11476
+ name: "shortcuts",
11477
+ type: "ReplayTransportShortcuts",
11478
+ description: "툴팁에 표시할 단축키"
11479
+ },
11480
+ {
11481
+ name: "className",
11482
+ type: "string",
11483
+ description: "추가 클래스"
11484
+ }
11485
+ ] },
11486
+ usage: {
11487
+ bestPractices: ["단축키는 ReplayPlayerLayout의 shortcuts로 묶고, 여기엔 표시용 문자열만 넘긴다.", "clock 타임코드는 로그와 대조할 때 필요하다. 세션의 실제 시각을 넘긴다."],
11488
+ avoid: ["재생 상태를 컴포넌트 안에 두지 않는다. 플레이어 엔진이 단일 소스다."]
11489
+ },
11490
+ testDescribe: "ReplayTransport",
11491
+ examples: [{
11492
+ title: "호스트가 재생 상태를 소유하는 트랜스포트",
11493
+ code: `import { ReplayTransport } from "@reopt-ai/opt-ui";
11494
+
11495
+ <ReplayTransport
11496
+ playing={playing}
11497
+ position={playhead}
11498
+ start={recording.start}
11499
+ end={recording.end}
11500
+ onTogglePlay={() => (playing ? player.pause() : player.play())}
11501
+ onSkip={(delta) => player.seek(playhead + delta)}
11502
+ speed={speed}
11503
+ onSpeedChange={setSpeed}
11504
+ skipInactive={skipInactive}
11505
+ onSkipInactiveChange={setSkipInactive}
11506
+ labels={{ play: "재생", pause: "일시정지" }}
11507
+ />`
11508
+ }]
11509
+ },
11510
+ {
11511
+ name: "ReplayPlayerLayout",
11512
+ category: "shell",
11513
+ functionalCategory: "layout",
11514
+ maturity: "beta",
11515
+ keywords: [
11516
+ "replay",
11517
+ "player",
11518
+ "layout",
11519
+ "inspector",
11520
+ "fullscreen",
11521
+ "shortcuts",
11522
+ "session recording"
11523
+ ],
11524
+ description: "세션 리플레이 플레이어의 프레임. 스테이지·메타 바·컨트롤·리사이즈 가능한 인스펙터를 배치하고, 플레이어 전체의 전체화면과 입력 필드를 피하는 키보드 단축키 범위를 소유한다.",
11525
+ slug: "replay-player-layout",
11526
+ exports: ["ReplayPlayerLayout", "replayShortcutKey"],
11527
+ props: { ReplayPlayerLayout: [
11528
+ {
11529
+ name: "stage",
11530
+ type: "ReactNode",
11531
+ required: true,
11532
+ description: "녹화가 렌더되는 영역(iframe 호스트)"
11533
+ },
11534
+ {
11535
+ name: "meta",
11536
+ type: "ReactNode",
11537
+ description: "스테이지 위 얇은 바: URL, 창, 기기"
11538
+ },
11539
+ {
11540
+ name: "controls",
11541
+ type: "ReactNode",
11542
+ description: "스테이지 아래 트랜스포트와 스크러버"
11543
+ },
11544
+ {
11545
+ name: "inspector",
11546
+ type: "ReactNode",
11547
+ description: "측면 패널. 생략하면 패널 없음"
11548
+ },
11549
+ {
11550
+ name: "inspectorOpen",
11551
+ type: "boolean",
11552
+ default: "true",
11553
+ description: "인스펙터 표시 여부(제어됨)"
11554
+ },
11555
+ {
11556
+ name: "onInspectorOpenChange",
11557
+ type: "(open: boolean) => void",
11558
+ description: "있으면 토글 버튼 표시"
11559
+ },
11560
+ {
11561
+ name: "inspectorSize",
11562
+ type: "number",
11563
+ default: "34",
11564
+ description: "인스펙터 초기 폭(%)"
11565
+ },
11566
+ {
11567
+ name: "layoutId",
11568
+ type: "string",
11569
+ description: "리사이즈 레이아웃 영속 id"
11570
+ },
11571
+ {
11572
+ name: "fullscreen",
11573
+ type: "boolean",
11574
+ default: "false",
11575
+ description: "전체화면 상태(호스트 소유)"
11576
+ },
11577
+ {
11578
+ name: "onFullscreenChange",
11579
+ type: "(fullscreen: boolean) => void",
11580
+ description: "전체화면 변경 콜백. 브라우저가 스스로 나가도 호출된다"
11581
+ },
11582
+ {
11583
+ name: "shortcuts",
11584
+ type: "ReplayShortcutMap",
11585
+ description: "포커스가 안에 있고 입력 필드가 아닐 때만 동작. false를 반환하면 전파"
11586
+ },
11587
+ {
11588
+ name: "overlay",
11589
+ type: "ReactNode",
11590
+ description: "스테이지 위 오버레이: 로딩, 오류, 건너뛰는 중"
11591
+ },
11592
+ {
11593
+ name: "labels",
11594
+ type: "ReplayPlayerLayoutLabels",
11595
+ description: "문구 오버라이드"
11596
+ },
11597
+ {
11598
+ name: "className",
11599
+ type: "string",
11600
+ description: "추가 클래스"
11601
+ }
11602
+ ] },
11603
+ usage: {
11604
+ bestPractices: ["단축키 키 이름은 replayShortcutKey가 만든다: space, arrowleft, shift+arrowright, mod+arrowleft, f.", "전체화면 상태는 호스트가 들고, 브라우저가 스스로 나가면 onFullscreenChange(false)로 통지된다."],
11605
+ avoid: ["스테이지 iframe에 직접 requestFullscreen하지 않는다. 컨트롤이 함께 보여야 한다."]
11606
+ },
11607
+ testDescribe: "ReplayPlayerLayout",
11608
+ examples: [{
11609
+ title: "스테이지·컨트롤·인스펙터 배치와 단축키",
11610
+ code: `import { ReplayPlayerLayout } from "@reopt-ai/opt-ui";
11611
+
11612
+ <ReplayPlayerLayout
11613
+ layoutId="replay-player"
11614
+ meta={<span>{currentPath}</span>}
11615
+ stage={<div ref={stageRef} />}
11616
+ controls={<>{scrubber}{transport}</>}
11617
+ inspector={<EventList onSelect={(e) => player.seek(e.time)} />}
11618
+ inspectorOpen={inspectorOpen}
11619
+ onInspectorOpenChange={setInspectorOpen}
11620
+ shortcuts={{ space: togglePlay, arrowleft: () => skip(-10_000), arrowright: () => skip(10_000) }}
11621
+ />`
11622
+ }]
11183
11623
  }
11184
11624
  ];
11185
11625
  //#endregion