@things-factory/board-import 10.0.0-beta.108

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 (137) hide show
  1. package/config/config.development.js +44 -0
  2. package/config/config.production.js +18 -0
  3. package/dist-server/index.d.ts +7 -0
  4. package/dist-server/index.js +11 -0
  5. package/dist-server/index.js.map +1 -0
  6. package/dist-server/service/adapters/dxf-adapter.d.ts +8 -0
  7. package/dist-server/service/adapters/dxf-adapter.js +220 -0
  8. package/dist-server/service/adapters/dxf-adapter.js.map +1 -0
  9. package/dist-server/service/adapters/image-adapter.d.ts +32 -0
  10. package/dist-server/service/adapters/image-adapter.js +420 -0
  11. package/dist-server/service/adapters/image-adapter.js.map +1 -0
  12. package/dist-server/service/ai/types.d.ts +185 -0
  13. package/dist-server/service/ai/types.js +378 -0
  14. package/dist-server/service/ai/types.js.map +1 -0
  15. package/dist-server/service/converters/generic-to.d.ts +31 -0
  16. package/dist-server/service/converters/generic-to.js +84 -0
  17. package/dist-server/service/converters/generic-to.js.map +1 -0
  18. package/dist-server/service/extraction/contracts.d.ts +112 -0
  19. package/dist-server/service/extraction/contracts.js +3 -0
  20. package/dist-server/service/extraction/contracts.js.map +1 -0
  21. package/dist-server/service/extraction/index.d.ts +17 -0
  22. package/dist-server/service/extraction/index.js +62 -0
  23. package/dist-server/service/extraction/index.js.map +1 -0
  24. package/dist-server/service/extraction/label-scale-calibrator.d.ts +5 -0
  25. package/dist-server/service/extraction/label-scale-calibrator.js +111 -0
  26. package/dist-server/service/extraction/label-scale-calibrator.js.map +1 -0
  27. package/dist-server/service/extraction/layout-refiner.d.ts +16 -0
  28. package/dist-server/service/extraction/layout-refiner.js +88 -0
  29. package/dist-server/service/extraction/layout-refiner.js.map +1 -0
  30. package/dist-server/service/extraction/pipeline.d.ts +11 -0
  31. package/dist-server/service/extraction/pipeline.js +124 -0
  32. package/dist-server/service/extraction/pipeline.js.map +1 -0
  33. package/dist-server/service/extraction/source-router.d.ts +17 -0
  34. package/dist-server/service/extraction/source-router.js +40 -0
  35. package/dist-server/service/extraction/source-router.js.map +1 -0
  36. package/dist-server/service/extraction/stubs.d.ts +18 -0
  37. package/dist-server/service/extraction/stubs.js +38 -0
  38. package/dist-server/service/extraction/stubs.js.map +1 -0
  39. package/dist-server/service/extraction/vector-geometry.d.ts +7 -0
  40. package/dist-server/service/extraction/vector-geometry.js +21 -0
  41. package/dist-server/service/extraction/vector-geometry.js.map +1 -0
  42. package/dist-server/service/extraction/vlm-recognizer.d.ts +18 -0
  43. package/dist-server/service/extraction/vlm-recognizer.js +78 -0
  44. package/dist-server/service/extraction/vlm-recognizer.js.map +1 -0
  45. package/dist-server/service/import-session/import-actions.d.ts +79 -0
  46. package/dist-server/service/import-session/import-actions.js +98 -0
  47. package/dist-server/service/import-session/import-actions.js.map +1 -0
  48. package/dist-server/service/import-session/import-session-resolver.d.ts +39 -0
  49. package/dist-server/service/import-session/import-session-resolver.js +263 -0
  50. package/dist-server/service/import-session/import-session-resolver.js.map +1 -0
  51. package/dist-server/service/import-session/import-session.d.ts +28 -0
  52. package/dist-server/service/import-session/import-session.js +152 -0
  53. package/dist-server/service/import-session/import-session.js.map +1 -0
  54. package/dist-server/service/import-session/import-worker.d.ts +1 -0
  55. package/dist-server/service/import-session/import-worker.js +121 -0
  56. package/dist-server/service/import-session/import-worker.js.map +1 -0
  57. package/dist-server/service/import-session/index.d.ts +8 -0
  58. package/dist-server/service/import-session/index.js +19 -0
  59. package/dist-server/service/import-session/index.js.map +1 -0
  60. package/dist-server/service/import-session/materialize-from-session.d.ts +66 -0
  61. package/dist-server/service/import-session/materialize-from-session.js +104 -0
  62. package/dist-server/service/import-session/materialize-from-session.js.map +1 -0
  63. package/dist-server/service/import-session/suggest-board-name.d.ts +80 -0
  64. package/dist-server/service/import-session/suggest-board-name.js +195 -0
  65. package/dist-server/service/import-session/suggest-board-name.js.map +1 -0
  66. package/dist-server/service/import-tools.d.ts +10 -0
  67. package/dist-server/service/import-tools.js +227 -0
  68. package/dist-server/service/import-tools.js.map +1 -0
  69. package/dist-server/service/index.d.ts +26 -0
  70. package/dist-server/service/index.js +60 -0
  71. package/dist-server/service/index.js.map +1 -0
  72. package/dist-server/service/pipeline/index.d.ts +66 -0
  73. package/dist-server/service/pipeline/index.js +127 -0
  74. package/dist-server/service/pipeline/index.js.map +1 -0
  75. package/dist-server/service/pipeline/stage2-mapping.d.ts +29 -0
  76. package/dist-server/service/pipeline/stage2-mapping.js +231 -0
  77. package/dist-server/service/pipeline/stage2-mapping.js.map +1 -0
  78. package/dist-server/service/pipeline/stage3-board.d.ts +52 -0
  79. package/dist-server/service/pipeline/stage3-board.js +108 -0
  80. package/dist-server/service/pipeline/stage3-board.js.map +1 -0
  81. package/dist-server/service/pipeline/stage4-binding.d.ts +9 -0
  82. package/dist-server/service/pipeline/stage4-binding.js +28 -0
  83. package/dist-server/service/pipeline/stage4-binding.js.map +1 -0
  84. package/dist-server/service/registry/index.d.ts +27 -0
  85. package/dist-server/service/registry/index.js +108 -0
  86. package/dist-server/service/registry/index.js.map +1 -0
  87. package/dist-server/service/types/index.d.ts +152 -0
  88. package/dist-server/service/types/index.js +3 -0
  89. package/dist-server/service/types/index.js.map +1 -0
  90. package/dist-server/service/types/registry.d.ts +175 -0
  91. package/dist-server/service/types/registry.js +3 -0
  92. package/dist-server/service/types/registry.js.map +1 -0
  93. package/dist-server/tsconfig.tsbuildinfo +1 -0
  94. package/package.json +35 -0
  95. package/probe/gcp-vision-probe.mjs +114 -0
  96. package/probe/opencv-geometry-probe.py +98 -0
  97. package/server/index.ts +7 -0
  98. package/server/service/adapters/dxf-adapter.ts +230 -0
  99. package/server/service/adapters/image-adapter.test.ts +545 -0
  100. package/server/service/adapters/image-adapter.ts +464 -0
  101. package/server/service/ai/types.ts +619 -0
  102. package/server/service/converters/generic-to.test.ts +105 -0
  103. package/server/service/converters/generic-to.ts +121 -0
  104. package/server/service/extraction/contracts.ts +142 -0
  105. package/server/service/extraction/extraction.test.ts +374 -0
  106. package/server/service/extraction/index.ts +51 -0
  107. package/server/service/extraction/label-scale-calibrator.ts +124 -0
  108. package/server/service/extraction/layout-refiner.ts +104 -0
  109. package/server/service/extraction/pipeline.ts +163 -0
  110. package/server/service/extraction/source-router.ts +45 -0
  111. package/server/service/extraction/stubs.ts +46 -0
  112. package/server/service/extraction/vector-geometry.ts +26 -0
  113. package/server/service/extraction/vlm-recognizer.ts +87 -0
  114. package/server/service/import-session/import-actions.test.ts +185 -0
  115. package/server/service/import-session/import-actions.ts +164 -0
  116. package/server/service/import-session/import-session-resolver.ts +245 -0
  117. package/server/service/import-session/import-session.ts +141 -0
  118. package/server/service/import-session/import-worker.ts +132 -0
  119. package/server/service/import-session/index.ts +24 -0
  120. package/server/service/import-session/materialize-from-session.test.ts +274 -0
  121. package/server/service/import-session/materialize-from-session.ts +158 -0
  122. package/server/service/import-session/suggest-board-name.test.ts +271 -0
  123. package/server/service/import-session/suggest-board-name.ts +279 -0
  124. package/server/service/import-tools.test.ts +137 -0
  125. package/server/service/import-tools.ts +255 -0
  126. package/server/service/index.ts +117 -0
  127. package/server/service/pipeline/index.ts +184 -0
  128. package/server/service/pipeline/stage2-mapping.test.ts +204 -0
  129. package/server/service/pipeline/stage2-mapping.ts +270 -0
  130. package/server/service/pipeline/stage3-board.test.ts +133 -0
  131. package/server/service/pipeline/stage3-board.ts +179 -0
  132. package/server/service/pipeline/stage4-binding.ts +31 -0
  133. package/server/service/registry/index.ts +127 -0
  134. package/server/service/types/index.ts +169 -0
  135. package/server/service/types/registry.ts +186 -0
  136. package/things-factory.config.js +1 -0
  137. package/tsconfig.json +9 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * convertPlaceholderTo / attachGlbToPlaceholder — 단위 테스트.
3
+ *
4
+ * 코어 placeholder type (placeholder) + 도메인 추상 (GenericTransport 등 호출자 명시)
5
+ * 모두 변환 가능한지 검증. 좌표/크기/source 보존, GLB attach morph 정책 회귀 방지.
6
+ */
7
+ import {
8
+ convertPlaceholderTo,
9
+ isPlaceholderType,
10
+ CORE_PLACEHOLDER_TYPES
11
+ } from './generic-to'
12
+ import { GENERIC_OBJECT_TYPE } from '../pipeline/stage2-mapping'
13
+
14
+ // 호출자가 도메인 추상까지 인정하는 경우의 fixtures.
15
+ const EXTRA = ['GenericTransport', 'GenericFacility'] as const
16
+
17
+ // ── 계약 어설션 ─────────────────────────────────────────────────────
18
+ // things-scene 의 CORE_PLACEHOLDER_TYPES 와 mirror. client/server 경계로
19
+ // 직접 import 불가 — 이 테스트가 양쪽 값 일치를 보증.
20
+ describe('CORE_PLACEHOLDER_TYPES contract', () => {
21
+ test('GENERIC_OBJECT_TYPE 은 반드시 placeholder 고정', () => {
22
+ expect(GENERIC_OBJECT_TYPE).toBe('placeholder')
23
+ })
24
+
25
+ test('CORE_PLACEHOLDER_TYPES 는 [placeholder] 고정 — things-scene 과 동기화', () => {
26
+ expect(Array.from(CORE_PLACEHOLDER_TYPES)).toEqual(['placeholder'])
27
+ })
28
+ })
29
+
30
+ describe('isPlaceholderType', () => {
31
+ test('코어 type 들은 인정', () => {
32
+ for (const t of CORE_PLACEHOLDER_TYPES) {
33
+ expect(isPlaceholderType(t)).toBe(true)
34
+ }
35
+ })
36
+
37
+ test('extra list 의 도메인 추상도 인정', () => {
38
+ expect(isPlaceholderType('GenericTransport', EXTRA)).toBe(true)
39
+ expect(isPlaceholderType('GenericFacility', EXTRA)).toBe(true)
40
+ })
41
+
42
+ test('미등록 type 은 거부', () => {
43
+ expect(isPlaceholderType('Stocker')).toBe(false)
44
+ expect(isPlaceholderType(undefined)).toBe(false)
45
+ })
46
+
47
+ test('extra 미지정 시 도메인 추상 거부', () => {
48
+ expect(isPlaceholderType('GenericTransport')).toBe(false)
49
+ })
50
+ })
51
+
52
+ describe('convertPlaceholderTo', () => {
53
+ const base = {
54
+ type: 'placeholder',
55
+ id: 'src-1',
56
+ left: 100,
57
+ top: 200,
58
+ width: 80,
59
+ height: 60,
60
+ rotation: 0.5,
61
+ fillStyle: 'rgba(180,180,180,0.18)',
62
+ placeholderLabel: 'maybe stocker',
63
+ placeholderHint: 'STK_001'
64
+ }
65
+
66
+ test('placeholder 가 아닌 type 은 throw', () => {
67
+ expect(() =>
68
+ convertPlaceholderTo({ ...base, type: 'rect' } as any, 'Stocker')
69
+ ).toThrow(/not a generic placeholder/)
70
+ })
71
+
72
+ test('좌표/크기/회전/id 보존', () => {
73
+ const r = convertPlaceholderTo(base as any, 'Stocker') as any
74
+ expect(r.left).toBe(100)
75
+ expect(r.top).toBe(200)
76
+ expect(r.width).toBe(80)
77
+ expect(r.height).toBe(60)
78
+ expect(r.rotation).toBe(0.5)
79
+ expect(r.id).toBe('src-1')
80
+ })
81
+
82
+ test('새 type 으로 변환 + category=unknown (rule 미등록)', () => {
83
+ const r = convertPlaceholderTo(base as any, 'Stocker') as any
84
+ expect(r.type).toBe('Stocker')
85
+ expect(r.category).toBe('unknown')
86
+ })
87
+
88
+ test('placeholder 시각 메타 (fillStyle / placeholderLabel) 는 제거', () => {
89
+ const r = convertPlaceholderTo(base as any, 'Stocker') as any
90
+ expect(r.fillStyle).toBeUndefined()
91
+ expect(r.placeholderLabel).toBeUndefined()
92
+ expect(r.placeholderHint).toBeUndefined()
93
+ expect(r.convertedFrom).toBeUndefined()
94
+ })
95
+
96
+ test('도메인 추상 (extraPlaceholderTypes 명시) 도 변환 가능', () => {
97
+ const transport = { ...base, type: 'GenericTransport', id: 'tx-1' }
98
+ const r = convertPlaceholderTo(transport as any, 'Forklift', {
99
+ extraPlaceholderTypes: ['GenericTransport']
100
+ }) as any
101
+ expect(r.type).toBe('Forklift')
102
+ expect(r.convertedFrom).toBeUndefined()
103
+ })
104
+ })
105
+
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Generic placeholder → 도메인 컴포넌트 변환.
3
+ *
4
+ * Import 시 매칭되지 않은 entity 는 `placeholder` 타입으로 보드에 배치된다.
5
+ * 사용자가 사후에 모델러 UI 에서 "Convert to ..." 액션으로 도메인 컴포넌트로 변환.
6
+ *
7
+ * 아키텍처 원칙:
8
+ * - `placeholder` — 도메인 비종속 임시 추상. 다른 type 으로 변환하는 기능만.
9
+ * GLB 지원 없음 (GltfComponent mixin 제거됨).
10
+ * - 도메인 generic 추상 (GenericTransport, GenericFacility 등) — 영구 도메인 추상.
11
+ * GLB 바디를 직접 채우는 방식으로 사용. 변환 대상 아님.
12
+ *
13
+ * 변환 시:
14
+ * - 좌표/크기/회전 보존
15
+ * - source (원본 entity attribs) 보존
16
+ * - 등록된 ImportRule.build 의 defaultState / idStrategy 적용
17
+ */
18
+ import { find3DPreset, getImportRules } from '../registry/index'
19
+ import type { BoardComponent } from '../pipeline/stage3-board'
20
+ import type { ComponentCategory } from '../types/registry'
21
+ import { GENERIC_OBJECT_TYPE } from '../pipeline/stage2-mapping'
22
+
23
+ export interface ConvertOptions {
24
+ scopes?: string[]
25
+ /** 새 컴포넌트의 ID. 미지정 시 ImportRule 의 idStrategy 적용 또는 자동 생성. */
26
+ id?: string
27
+ /**
28
+ * 코어 placeholder type ('placeholder') 외에 인정할 도메인 추상 type list.
29
+ * 예: ['GenericTransport', 'GenericFacility'].
30
+ * 호출자가 자기 도메인 패키지의 추상까지 변환 대상으로 포함하도록 명시.
31
+ */
32
+ extraPlaceholderTypes?: ReadonlyArray<string>
33
+ }
34
+
35
+ /**
36
+ * things-scene 코어가 등록한 도메인 비종속 placeholder type.
37
+ *
38
+ * @sync things-scene/src/components/generic-placeholders.ts CORE_PLACEHOLDER_TYPES
39
+ * client/server 경계로 직접 import 불가 — 값을 바꾸면 양쪽 동시 수정 필수.
40
+ * 계약은 generic-to.test.ts 의 'CORE_PLACEHOLDER_TYPES contract' 어설션으로 보증.
41
+ */
42
+ export const CORE_PLACEHOLDER_TYPES = [GENERIC_OBJECT_TYPE] as const
43
+
44
+ export function isPlaceholderType(
45
+ type: string | undefined,
46
+ extraTypes: ReadonlyArray<string> = []
47
+ ): boolean {
48
+ if (!type) return false
49
+ if ((CORE_PLACEHOLDER_TYPES as readonly string[]).includes(type)) return true
50
+ return extraTypes.includes(type)
51
+ }
52
+
53
+ // ── 일반화된 변환 helper ─────────────────────────────────────────────
54
+
55
+ /**
56
+ * 추상 placeholder 를 지정된 도메인 type 으로 변환. 호환되는 모든 generic-* 수용 (코어 + 도메인).
57
+ *
58
+ * 기존 좌표/크기/회전/source 보존. placeholder 시각 메타 (placeholderLabel/Hint, fillStyle 등) 는 제거.
59
+ */
60
+ export function convertPlaceholderTo(
61
+ placeholder: BoardComponent,
62
+ toType: string,
63
+ options: ConvertOptions = {}
64
+ ): BoardComponent {
65
+ const extra = options.extraPlaceholderTypes ?? []
66
+ if (!isPlaceholderType(placeholder.type, extra)) {
67
+ throw new Error(
68
+ `Source component is not a generic placeholder (type=${placeholder.type}). ` +
69
+ `Expected one of: ${[...CORE_PLACEHOLDER_TYPES, ...extra].join(', ')}`
70
+ )
71
+ }
72
+
73
+ const rules = getImportRules(options.scopes)
74
+ const rule = rules.find(r => r.componentType === toType)
75
+
76
+ const category: ComponentCategory = rule?.category ?? 'unknown'
77
+ const newId = options.id ?? placeholder.id
78
+
79
+ const preset = find3DPreset(toType, category, options.scopes)
80
+ const threeD = {
81
+ geometry: preset.geometry,
82
+ ...(preset.material && { material: preset.material }),
83
+ origin: preset.origin ?? 'bottom'
84
+ }
85
+
86
+ // placeholder 시각/스타일 메타는 새 type 으로 옮길 때 의미 없음 — 제거.
87
+ const {
88
+ strokeStyle: _strokeStyle,
89
+ strokeDashArray: _strokeDashArray,
90
+ fillStyle: _fillStyle,
91
+ label: _label,
92
+ placeholderLabel: _placeholderLabel,
93
+ placeholderHint: _placeholderHint,
94
+ ...preservedState
95
+ } = placeholder as any
96
+
97
+ return {
98
+ ...preservedState,
99
+ category,
100
+ type: toType,
101
+ id: newId,
102
+ threeD,
103
+ ...(rule?.build.defaultState || {})
104
+ }
105
+ }
106
+
107
+ // ── Backward-compat alias — 기존 호출처 호환 ──────────────────────
108
+
109
+ /**
110
+ * @deprecated Use `convertPlaceholderTo`. Placeholder 한정 — 새 코드는 일반화된 helper.
111
+ */
112
+ export function convertGenericTo(
113
+ generic: BoardComponent,
114
+ toType: string,
115
+ options: ConvertOptions = {}
116
+ ): BoardComponent {
117
+ if (generic.type !== GENERIC_OBJECT_TYPE) {
118
+ throw new Error(`Source component is not Placeholder (type=${generic.type})`)
119
+ }
120
+ return convertPlaceholderTo(generic, toType, options)
121
+ }
@@ -0,0 +1,142 @@
1
+ /*
2
+ * 하이브리드 추출 contracts (Phase Board-AI §3.8) — 이미지/도면 → 레이아웃 추출을
3
+ * *역할분담* 으로 분해하기 위한 provider-중립 인터페이스.
4
+ *
5
+ * 위치/기하 ← GeometryExtractor (벡터 파싱 > CV > VLM폴백)
6
+ * 식별/라벨 ← LabelExtractor (OCR: GCP/Textract/Tesseract)
7
+ * 의미/타입 ← Recognizer (VLM — region 인식 전담, 좌표 아님)
8
+ * 스케일 ← ScaleCalibrator (치수/스케일바/알려진 크기)
9
+ * 정리 ← LayoutRefiner (정렬/스냅/등간격/겹침해소)
10
+ *
11
+ * 구현체는 스캐폴딩 단계에선 stub. 본격 테스트 단계에서 소스 적합 impl 을 채운다.
12
+ */
13
+ import type { UniversalEntity, Vec2, BoundingBox, AdapterInput, ParseResult } from '../types/index'
14
+
15
+ /** 입력 소스 분류 — 추출 경로 선택 기준. */
16
+ export type SourceKind =
17
+ | 'vector' // DXF/IFC/SVG/PDF-vector — 정밀 기하 직접 파싱
18
+ | 'raster-topdown' // 평면도 비트맵 — CV + OCR + VLM 하이브리드
19
+ | 'photo' // 사진/3D 렌더 — VLM 러프 폴백
20
+ | 'unknown'
21
+
22
+ /** 검출된 저수준 기하 프리미티브 (픽셀/소스 좌표계). */
23
+ export interface GeometricPrimitive {
24
+ kind: 'line' | 'rect' | 'region' | 'point'
25
+ bbox: BoundingBox
26
+ /** line 일 때 양 끝점. */
27
+ segment?: { a: Vec2; b: Vec2 }
28
+ /** 검출 신뢰도 0..1 (있으면). */
29
+ score?: number
30
+ /**
31
+ * 이미 완전한 엔티티를 아는 추출기(벡터: DXF/IFC/SVG)가 원 엔티티를 그대로 실어 보낸다.
32
+ * 있으면 fuse 가 무손실로 사용(attribs/rotation/vertices 보존). CV 추출기는 비움 → fuse 가
33
+ * bbox 로 bare 엔티티 생성 후 Recognizer 가 의미 부여.
34
+ */
35
+ entity?: UniversalEntity
36
+ /** 추출기별 원시 메타. */
37
+ meta?: Record<string, any>
38
+ }
39
+
40
+ /** OCR 라벨 — 텍스트 + 위치 (식별/앵커/스케일 단서). */
41
+ export interface Label {
42
+ text: string
43
+ bbox: BoundingBox
44
+ score?: number
45
+ }
46
+
47
+ /** 한 region(프리미티브/crop)에 대한 의미 인식 결과. */
48
+ export interface RecognitionResult {
49
+ /** 실제 보드 컴포넌트 type (카탈로그 그라운딩). 불명 시 undefined. */
50
+ componentType?: string
51
+ confidence: number
52
+ attribs?: Record<string, any>
53
+ }
54
+
55
+ /** 스케일 캘리브레이션 결과 — 픽셀 → board 단위 변환. */
56
+ export interface ScaleResult {
57
+ unitsPerPixel: number
58
+ origin: Vec2
59
+ units?: ParseResult['units']
60
+ }
61
+
62
+ /** 추출 컨텍스트 — 이미지 dims, 사용자 힌트, 카탈로그 등 (확장 여지). */
63
+ export interface ExtractionContext {
64
+ imageWidth?: number
65
+ imageHeight?: number
66
+ userPrompt?: string
67
+ /** 카탈로그 그라운딩용 컴포넌트 type 목록 (Recognizer 가 제약). */
68
+ catalog?: string[]
69
+ /**
70
+ * 명시 스케일 기준 — 알려진 픽셀 span = 알려진 실제 길이 (예: UI 2점 캘리브레이션,
71
+ * 스케일바). ScaleCalibrator 가 최우선 사용. unitsPerPixel = units / pixels.
72
+ */
73
+ scaleReference?: { pixels: number; units: number; unit?: string }
74
+ [k: string]: any
75
+ }
76
+
77
+ // ── contracts ────────────────────────────────────────────────────────────────
78
+
79
+ export interface GeometryExtractor {
80
+ readonly id: string
81
+ extract(input: AdapterInput, ctx?: ExtractionContext): Promise<GeometricPrimitive[]>
82
+ }
83
+
84
+ export interface LabelExtractor {
85
+ readonly id: string
86
+ extract(input: AdapterInput, ctx?: ExtractionContext): Promise<Label[]>
87
+ }
88
+
89
+ export interface Recognizer {
90
+ readonly id: string
91
+ /** region(프리미티브 + ctx)이 어떤 컴포넌트인지 — VLM 인식 전담(좌표 아님). */
92
+ recognize(region: GeometricPrimitive, ctx?: ExtractionContext): Promise<RecognitionResult>
93
+ }
94
+
95
+ export interface ScaleCalibrator {
96
+ readonly id: string
97
+ calibrate(labels: Label[], primitives: GeometricPrimitive[], ctx?: ExtractionContext): Promise<ScaleResult>
98
+ }
99
+
100
+ export interface LayoutRefiner {
101
+ readonly id: string
102
+ refine(entities: UniversalEntity[], ctx?: ExtractionContext): UniversalEntity[]
103
+ }
104
+
105
+ /** 한 시안(해석) — 공유 기하 위에 의미/그룹핑/전략만 달리한 결과. */
106
+ export interface VariantDraft {
107
+ /** URL 친화 short id (예: 'equipment-focus'). */
108
+ id: string
109
+ /** 사용자 표시 라벨 (예: '장비 중심'). */
110
+ label: string
111
+ description?: string
112
+ entities: UniversalEntity[]
113
+ subjectName?: string
114
+ subjectDescription?: string
115
+ importStrategy?: string
116
+ }
117
+
118
+ /**
119
+ * 시안 전략 — *정확한 공유 기하+라벨* 을 받아 N개 해석 시안을 만든다.
120
+ * (UX: 사용자가 3안 중 선택하는 흐름 보존. 기하는 공유라 셋 다 레이아웃 정상, 해석만 다름.)
121
+ * 미지정 시 파이프라인이 단일 default 시안 1개를 만든다.
122
+ */
123
+ export interface VariantStrategy {
124
+ readonly id: string
125
+ interpret(
126
+ primitives: GeometricPrimitive[],
127
+ labels: Label[],
128
+ set: ExtractorSet,
129
+ ctx?: ExtractionContext
130
+ ): Promise<VariantDraft[]>
131
+ }
132
+
133
+ /** 한 소스경로의 추출기 묶음 (geometry 필수, 나머지는 경로별 선택). */
134
+ export interface ExtractorSet {
135
+ geometry: GeometryExtractor
136
+ labels?: LabelExtractor
137
+ recognizer?: Recognizer
138
+ scale?: ScaleCalibrator
139
+ refiner?: LayoutRefiner
140
+ /** 다중 시안 생성 전략 (3안 선택 UX). 미지정 시 단일 default 시안. */
141
+ variants?: VariantStrategy
142
+ }