@g1cloud/ui-modeler-next 5.0.0-alpha.1

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 (68) hide show
  1. package/README.md +140 -0
  2. package/dist/adapter/attrCodec.d.ts +12 -0
  3. package/dist/adapter/interpretFlat.d.ts +20 -0
  4. package/dist/adapter/legacyGridSort.d.ts +11 -0
  5. package/dist/adapter/legacyText.d.ts +31 -0
  6. package/dist/adapter/legacyValidator.d.ts +2 -0
  7. package/dist/adapter/migrate.d.ts +47 -0
  8. package/dist/adapter/persisted.d.ts +75 -0
  9. package/dist/adapter/uiModelAdapter.d.ts +80 -0
  10. package/dist/agent/buildAgentBatch.d.ts +22 -0
  11. package/dist/agent/describeScreen.d.ts +21 -0
  12. package/dist/agent/resolver.d.ts +72 -0
  13. package/dist/agent/schema.d.ts +15 -0
  14. package/dist/agent/strictSubset.d.ts +30 -0
  15. package/dist/agent/symbolicOp.d.ts +97 -0
  16. package/dist/catalog/builtin/basic.d.ts +3 -0
  17. package/dist/catalog/builtin/chart.d.ts +3 -0
  18. package/dist/catalog/builtin/composite.d.ts +3 -0
  19. package/dist/catalog/builtin/etc.d.ts +3 -0
  20. package/dist/catalog/builtin/filter.d.ts +3 -0
  21. package/dist/catalog/builtin/grid.d.ts +3 -0
  22. package/dist/catalog/builtin/index.d.ts +3 -0
  23. package/dist/catalog/builtin/input.d.ts +3 -0
  24. package/dist/catalog/builtin/layout.d.ts +3 -0
  25. package/dist/catalog/builtin/listField.d.ts +3 -0
  26. package/dist/catalog/builtin/multilang.d.ts +3 -0
  27. package/dist/catalog/builtin/shared.d.ts +69 -0
  28. package/dist/catalog/builtin/tree.d.ts +3 -0
  29. package/dist/catalog/catalog.d.ts +13 -0
  30. package/dist/catalog/containment.d.ts +26 -0
  31. package/dist/catalog/index.d.ts +6 -0
  32. package/dist/catalog/propertyEditor.d.ts +24 -0
  33. package/dist/catalog/styleTokens.d.ts +54 -0
  34. package/dist/catalog/toAgentCatalog.d.ts +57 -0
  35. package/dist/catalog/types.d.ts +117 -0
  36. package/dist/command/commands.d.ts +25 -0
  37. package/dist/command/op.d.ts +83 -0
  38. package/dist/command/opSync.d.ts +84 -0
  39. package/dist/command/stack.d.ts +47 -0
  40. package/dist/command/types.d.ts +23 -0
  41. package/dist/core/attrValue.d.ts +50 -0
  42. package/dist/core/caseMessage.d.ts +13 -0
  43. package/dist/core/gridDefaultSort.d.ts +15 -0
  44. package/dist/core/id.d.ts +2 -0
  45. package/dist/core/lang.d.ts +25 -0
  46. package/dist/core/partType.d.ts +30 -0
  47. package/dist/core/traverse.d.ts +24 -0
  48. package/dist/core/types.d.ts +40 -0
  49. package/dist/core/validation.d.ts +21 -0
  50. package/dist/core/validatorConfig.d.ts +48 -0
  51. package/dist/editor/controller.d.ts +125 -0
  52. package/dist/index.d.ts +42 -0
  53. package/dist/ui-modeler-next.css +1 -0
  54. package/dist/ui-modeler.js +3965 -0
  55. package/dist/ui-modeler.umd.cjs +1 -0
  56. package/dist/view/InlineIssueBadge.vue.d.ts +7 -0
  57. package/dist/view/MultiLangTextField.vue.d.ts +19 -0
  58. package/dist/view/PartCanvas.vue.d.ts +12 -0
  59. package/dist/view/PartPalette.vue.d.ts +2 -0
  60. package/dist/view/PartTree.vue.d.ts +7 -0
  61. package/dist/view/PropertyPanel.vue.d.ts +2 -0
  62. package/dist/view/RendererRegistry.d.ts +28 -0
  63. package/dist/view/SchematicPart.vue.d.ts +29 -0
  64. package/dist/view/dnd.d.ts +19 -0
  65. package/dist/view/preview/IframePreview.vue.d.ts +22 -0
  66. package/dist/view/preview/protocol.d.ts +40 -0
  67. package/dist/view/validationDisplay.d.ts +8 -0
  68. package/package.json +56 -0
@@ -0,0 +1,84 @@
1
+ import { OpConflict, OpOrigin, OpResult, PartId, PersistedOpBatch } from './op';
2
+ import { CommandStack } from './stack';
3
+ import { EditorState } from './types';
4
+ /**
5
+ * 컨테이너 파트 rev의 라이브 캐시 — 모든 forward op의 base rev 단일 출처. `partId` 키(단일 'part'
6
+ * 축, 불변식 #2 — 시블링의 layoutVersion/logicalVersion 없음). v2-read(M10)가 평면 문서를 파싱해
7
+ * `seed`로 주입하고, 호스트 응답이 drain된 시점에 확정.
8
+ */
9
+ export declare class RevCache {
10
+ private revs;
11
+ /** v2 문서 적재/재적재 시 캐시 초기화(M10 fromPersisted v2 분기가 호출, M7 테스트는 손수 주입). */
12
+ seed(snapshot: {
13
+ revs?: Record<PartId, number>;
14
+ }): void;
15
+ rev(id: PartId): number;
16
+ setRev(id: PartId, v: number): void;
17
+ deleteRev(id: PartId): void;
18
+ /** 진단·테스트용 읽기 스냅샷(내부 Map 노출 안 함). */
19
+ snapshot(): {
20
+ revs: Record<PartId, number>;
21
+ };
22
+ }
23
+ /** 호스트로 배치를 전송하는 함수(M10 applyUiOpBatch 라우트에 연결, 테스트는 mock). */
24
+ export type OpSyncTransport = (batch: PersistedOpBatch) => Promise<OpResult>;
25
+ export interface OpSyncOptions {
26
+ modelId: string;
27
+ /** emitOps(state) 호출 대상 — controller의 reactive EditorState(트리 하나). */
28
+ state: EditorState;
29
+ transport: OpSyncTransport;
30
+ /** op 출처. GUI 편집 전용 어댑터라 기본 'gui'(agent는 별도 stateless 빌더 — M8). */
31
+ origin?: OpOrigin;
32
+ /** coalesce 그룹 idle 디바운스(ms). 기본 300. */
33
+ debounceMs?: number;
34
+ /** 멱등 키 생성기(주입 가능 — 테스트 결정성). 기본 newPartId. */
35
+ newClientOpId?: () => string;
36
+ /** 충돌(또는 전송 실패)로 하드 프리즈 진입 시 — controller가 editable=false + reload UX 연동(M9). */
37
+ onFreeze?: (conflict: OpConflict) => void;
38
+ /** 전송 거부(네트워크 등) 통지 — 진단용(프리즈는 별도 onFreeze로). */
39
+ onError?: (err: unknown) => void;
40
+ }
41
+ export declare class OpSyncAdapter {
42
+ private readonly opts;
43
+ private readonly cache;
44
+ private readonly newClientOpId;
45
+ private readonly debounceMs;
46
+ private readonly emissions;
47
+ private open;
48
+ private debounceHandle;
49
+ private readonly queue;
50
+ private sending;
51
+ private _frozen;
52
+ private stack;
53
+ private settledWaiters;
54
+ constructor(opts: OpSyncOptions);
55
+ /** RevCache 시드(M10 v2-read가 평면 파싱 후 호출, M7 테스트는 직접). */
56
+ seed(snapshot: Parameters<RevCache['seed']>[0]): void;
57
+ /** stack에 구독 — onCommand 알림을 받고, flush 경계에서 sealCoalesce를 호출하기 위해 참조 보관. 해제 함수 반환. */
58
+ attach(stack: CommandStack): () => void;
59
+ get frozen(): boolean;
60
+ /** 미전송 pending 그룹 존재 여부(controller dirty 보조 신호). */
61
+ get hasPending(): boolean;
62
+ /** 진단·테스트용 캐시 스냅샷. */
63
+ cacheSnapshot(): ReturnType<RevCache['snapshot']>;
64
+ /** 강제 flush(save·blur·beforeunload) — 열린 그룹을 즉시 영속에 내보내고 coalesce 경계 봉인. */
65
+ flush(): void;
66
+ /** 큐가 비고 전송 중이 아니며 열린 그룹도 없을 때 resolve — 테스트 동기화용. */
67
+ whenSettled(): Promise<void>;
68
+ private handle;
69
+ private flushOpen;
70
+ private startDebounce;
71
+ private resetDebounce;
72
+ private cancelDebounce;
73
+ private buildBatch;
74
+ private enqueue;
75
+ private pump;
76
+ /**
77
+ * ok 응답 확정 — 낙관 체인 유지가 핵심. 후속 pending 배치가 이 예측 위에 base를 쌓았으므로
78
+ * 큐가 완전히 빈 시점에만 서버 권위값으로 덮어쓴다(중간에 덮으면 후속 배치 base가 stale → 거짓 충돌).
79
+ * ok ⟹ CAS 통과 ⟹ 서버 신 rev = 낙관 예측이므로, 미배수 시 캐시는 이미 정확(덮을 필요 없음).
80
+ */
81
+ private reconcile;
82
+ private freeze;
83
+ private notifySettled;
84
+ }
@@ -0,0 +1,47 @@
1
+ import { Command, EditorState } from './types';
2
+ export type StackListener = () => void;
3
+ /** 명령 적용 방향 — op emit 어댑터가 forward(do/redo) vs invert(undo) op를 고르는 축. */
4
+ export type CommandDirection = 'do' | 'undo' | 'redo';
5
+ /**
6
+ * 명령 적용 알림 구독자(트랙 B). `onChange`(상태 변경 무인자 알림)와 직교 — 이쪽은 *어떤 명령이
7
+ * 어느 방향으로* 적용됐는지(+coalesce 병합 여부)를 전달해 op emit 어댑터가 emitOps로 op 배치를 파생하게 한다.
8
+ */
9
+ export type CommandListener = (cmd: Command, dir: CommandDirection, coalesced: boolean) => void;
10
+ /**
11
+ * 명령 스택 — 레거시 ActionManager 대응. execute/undo/redo와 savePoint 기반 dirty 추적을 제공한다.
12
+ * 도메인 무관(시블링 entity-modeler-next와 동일 계약).
13
+ */
14
+ export declare class CommandStack {
15
+ private readonly state;
16
+ private undoStack;
17
+ private redoStack;
18
+ private savePoint;
19
+ private listeners;
20
+ private commandListeners;
21
+ private coalesceSealed;
22
+ constructor(state: EditorState);
23
+ execute(cmd: Command): void;
24
+ undo(): void;
25
+ redo(): void;
26
+ /**
27
+ * 다음 execute의 coalesce 병합을 1회 차단한다(op 어댑터의 idle flush·강제 flush 시 호출).
28
+ * 어댑터가 coalesce 그룹을 영속에 내보낸 경계 이후의 동일 키 명령이 같은 undo 단위로 빨려
29
+ * 들어가면 undo 시 로컬·서버 상태가 어긋나므로, 봉인으로 새 단위를 강제한다.
30
+ */
31
+ sealCoalesce(): void;
32
+ get canUndo(): boolean;
33
+ get canRedo(): boolean;
34
+ /** 저장 시점 표시 — 이후 dirty 판정 기준. */
35
+ markSavePoint(): void;
36
+ /** 마지막 저장 이후 변경 여부 (undo로 저장 지점에 정확히 돌아오면 clean). */
37
+ get isDirty(): boolean;
38
+ /** 변경 알림 구독 — 반환 함수로 해제. */
39
+ onChange(listener: StackListener): () => void;
40
+ /**
41
+ * 명령 적용 알림 구독(트랙 B op emit). apply/invert 적용 *직후* 발화하므로 구독자가 emitOps()를
42
+ * 호출하면 memento가 채워진 상태를 본다(계약 1). 반환 함수로 해제.
43
+ */
44
+ onCommand(listener: CommandListener): () => void;
45
+ private emit;
46
+ private emitCommand;
47
+ }
@@ -0,0 +1,23 @@
1
+ import { EditorState } from '../core/types';
2
+ import { OpEmission } from './op';
3
+ export type { EditorState };
4
+ /**
5
+ * 명령(Command) — 레거시 EMAction 대응. apply로 상태를 변경하고 invert로 정확히 되돌린다(do/undo 대칭).
6
+ * 계약은 시블링 템플릿과 동일하고 EditorState만 트리형이다.
7
+ */
8
+ export interface Command {
9
+ readonly label: string;
10
+ /**
11
+ * 연속 동종 변경(드래그 이동 등)을 하나의 undo 단위로 병합하기 위한 키. 직전 명령의 키와 같으면
12
+ * 스택에서 합쳐진다(시작 상태 복원을 승계). undefined면 병합하지 않음.
13
+ */
14
+ readonly coalesceKey?: string;
15
+ apply(state: EditorState): void;
16
+ invert(state: EditorState): void;
17
+ /**
18
+ * 자신을 forward/invert op로 파생한다. **apply() 이후 호출 계약**(계약 1) — invert op이 apply가
19
+ * 캡처한 memento에 의존하므로(move=이전 부모/인덱스, remove=삭제된 서브트리). 옵셔널 — 미구현
20
+ * command는 op를 산출하지 않는다(호스트 영속 경로가 부재를 런타임 가드로 감지).
21
+ */
22
+ emitOps?(state: EditorState): OpEmission;
23
+ }
@@ -0,0 +1,50 @@
1
+ import { MultiLangText } from './lang';
2
+ import { ValidatorConfig } from './validatorConfig';
3
+ /**
4
+ * 파트 속성 값의 **타입드 clean 표현**. AI-native 인메모리 모델의 값 어휘다(G6).
5
+ *
6
+ * 레거시 {@code AttrValue{type, val:String}}(모든 값을 string으로 저장 + `AttrType` enum 태깅)의
7
+ * 적대성은 여기 남지 않는다 — 저장 태그(S/I/B/M/…)와 `#{key}`·문자열 코덱은 **v1-read 디코더**
8
+ * ({@link ../adapter/attrCodec})로 강등되고, 이 모델은 태그 없이 타입드 값만 표현한다.
9
+ * write-back(무손실 왕복)은 폐기되었으므로 저장 태그로의 역매핑은 존재하지 않는다.
10
+ *
11
+ * 값 종류(kind)는 catalog {@code PropertyItem.valueType}의 타이핑 SoT와 동일 어휘({@link AttrKind}).
12
+ * 바인딩 성격 값(`dataSource`/`dataMapping`)은 M12 design-intent seam으로 예약(G5) — 값은 유지하되
13
+ * agent엔 비노출(G2). 레거시 `*_REF`(원문 보존용 왕복 kind)와 `ETC`(미지 태그 캡처)는 제거되었다.
14
+ */
15
+ /** 런타임 값 종류. catalog {@code PropertyItem.valueType}와 공유하는 타이핑 어휘. */
16
+ export type AttrKind = 'string' | 'int' | 'bool' | 'text' | 'dataSource' | 'dataMapping' | 'validator';
17
+ export type AttrVal = {
18
+ kind: 'string';
19
+ value: string;
20
+ } | {
21
+ kind: 'int';
22
+ value: number;
23
+ } | {
24
+ kind: 'bool';
25
+ value: boolean;
26
+ } | {
27
+ kind: 'text';
28
+ value: MultiLangText;
29
+ } | {
30
+ kind: 'dataSource';
31
+ value: string;
32
+ } | {
33
+ kind: 'dataMapping';
34
+ value: string;
35
+ } | {
36
+ kind: 'validator';
37
+ value: ValidatorConfig;
38
+ };
39
+ export declare const str: (value: string) => AttrVal;
40
+ export declare const int: (value: number) => AttrVal;
41
+ export declare const bool: (value: boolean) => AttrVal;
42
+ export declare const text: (value: MultiLangText) => AttrVal;
43
+ export declare const dataSource: (value: string) => AttrVal;
44
+ export declare const dataMapping: (value: string) => AttrVal;
45
+ export declare const validator: (value: ValidatorConfig) => AttrVal;
46
+ export declare function asString(v: AttrVal | undefined, defaultValue?: string): string;
47
+ export declare function asInt(v: AttrVal | undefined, defaultValue?: number): number;
48
+ export declare function asBool(v: AttrVal | undefined, defaultValue?: boolean): boolean;
49
+ export declare function asText(v: AttrVal | undefined): MultiLangText | undefined;
50
+ export declare function asValidator(v: AttrVal | undefined): ValidatorConfig | undefined;
@@ -0,0 +1,13 @@
1
+ import { MultiLangText } from './lang';
2
+ /**
3
+ * 조건부 사용자 메시지(성공/실패/확인). 레거시 {@code CaseMessage.java} 포팅.
4
+ */
5
+ export type CaseMsgType = 'NOTI_S' | 'NOTI_F' | 'CONFIRM';
6
+ /** 레거시 enum caption. */
7
+ export declare const CASE_MSG_CAPTION: Record<CaseMsgType, string>;
8
+ export interface CaseMessage {
9
+ caseText?: MultiLangText;
10
+ msgType?: CaseMsgType;
11
+ /** 표시 메시지. 레거시 v1은 `#{key}`를 담으므로 {@link MultiLangText}로 통일(G6). */
12
+ msgText?: MultiLangText;
13
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 그리드 기본 정렬 스펙 — **구조 객체 1급**(G6). 레거시는 `propId:ORDER:dataMapping:name`
3
+ * (콜론 구분, 리스트는 세미콜론 join) 문자열 코덱으로 인코딩했으나, clean 모델은 이 구조를
4
+ * 그대로 표현한다. 레거시 문자열 → 이 구조로의 파싱은 v1-read 디코더
5
+ * ({@link ../adapter/legacyGridSort})에만 있고, 역방향 직렬화(write-back)는 폐기되었다.
6
+ *
7
+ * 파생 원본: bluework-uimodeler-model/.../ui/GridDefaultSort.java
8
+ */
9
+ export interface GridDefaultSort {
10
+ sortPropertyId: string;
11
+ /** 'ASC' | 'DESC' — 레거시는 임의 문자열 허용, 공백이면 ASC로 정규화. */
12
+ order: string;
13
+ sortDataMapping: string;
14
+ sortName: string;
15
+ }
@@ -0,0 +1,2 @@
1
+ /** 파트 식별자 생성. 브라우저/Node 공통 crypto.randomUUID 사용. */
2
+ export declare function newPartId(): string;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 다국어 텍스트. bluesea `MultiLangText` union을 미러링한 **디자인시스템 중립** i18n 개념
3
+ * (bluesea `@g1cloud/bluesea`의 shape와 정렬하되 import 결합은 아님 — 불변식 #1).
4
+ *
5
+ * 세 형태(bluesea 렌더 규칙과 동일):
6
+ * 1. `string` — 평문 리터럴. 그대로 표시.
7
+ * 2. {@link MultiLangString} — locale→text 맵. 현재 locale(폴백 체인)로 선택. (레거시 `msgMap`)
8
+ * 3. {@link MultiLangMessage} — 메시지 카탈로그 키 참조. 런타임에 `key`를 조회·`args` 치환.
9
+ * (레거시 `UiKeyText.key` / v1 저장 형식 `#{key}`가 이 형태로 무손실 디코드된다.)
10
+ *
11
+ * key 참조가 union의 **1급 멤버**이므로 v1 `#{key}`(운영 caption의 사실상 전부)가 관찰적 폐기 없이
12
+ * 보존된다. `key` 조회(MessageResolver)는 호스트/뷰 관심사 → 여기엔 형태만 둔다.
13
+ */
14
+ export type LocaleName = string;
15
+ /** locale→text 맵. 레거시 `Map<String,String>`(`msgMap`)에 대응. */
16
+ export type MultiLangString = Record<LocaleName, string>;
17
+ /** 메시지 카탈로그 키 참조. `key` 존재로 판별({@link isMultiLangMessage}). */
18
+ export interface MultiLangMessage {
19
+ key: string;
20
+ args?: unknown[];
21
+ locale?: LocaleName;
22
+ }
23
+ export type MultiLangText = string | MultiLangString | MultiLangMessage;
24
+ /** `key` 프로퍼티 유무로 메시지 키 참조를 판별. bluesea `isMultiLangMessage`와 동치. */
25
+ export declare function isMultiLangMessage(t: MultiLangText | undefined): t is MultiLangMessage;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * UI 파트(컴포넌트) 종류의 닫힌 집합. 레거시 {@code PartType.java} enum(~90 상수)을 그대로 포팅.
3
+ * 순서는 팔레트/카탈로그 안정성을 위해 레거시 enum 선언 순서를 보존한다.
4
+ *
5
+ * 파생 원본: bluework-uimodeler-model/.../ui/shared/PartType.java
6
+ */
7
+ export type PartCategory = 'Layout' | 'Basic' | 'Input' | 'Composite' | 'MultiLang' | 'Grid' | 'Tree' | 'Filter' | 'ListField' | 'Chart' | 'Etc';
8
+ /** 카테고리별 파트 타입. 레거시 enum의 주석 구획을 그대로 따른다. */
9
+ export declare const PART_TYPES_BY_CATEGORY: {
10
+ readonly Layout: readonly ["VerticalLayout", "HorizontalLayout", "CardLayout", "ActionLayout", "ActionLayoutArea", "FormLayout", "TableLayout", "TableLayoutCell", "FlowLayout", "ExpansionLayout", "InternalLayoutArea"];
11
+ readonly Basic: readonly ["Button", "PopupButton", "Label", "Image", "Link", "LinkTab", "ProgressBar", "Console", "NativeUpload", "AuditInfo", "AuditInfoButton"];
12
+ readonly Input: readonly ["TextField", "TextArea", "StyledTextField", "RichTextArea", "TelField", "NumberField", "CurrencyField", "DateField", "TimeField", "DateRange", "CheckBox", "RadioButton", "RadioGroup", "CheckBoxGroup", "YesNoGroup", "Select", "MultiSelect", "CategorySelect", "PopupSelect", "YesNoSelect", "ColorField"];
13
+ readonly Composite: readonly ["MultiFileUpload", "SingleFileUpload", "SingleImageUpload", "NameField", "AddressField", "PageBuilder", "ContentEditor"];
14
+ readonly MultiLang: readonly ["MultiLangTextField", "MultiLangTextArea", "MultiLangStyledTextField", "MultiLangRichTextArea", "MultiLangPageBuilder", "MultiLangContentEditor", "MultiLangFileUpload"];
15
+ readonly Grid: readonly ["Grid", "GridControl", "GridLookup", "ColumnDef", "GridCellPanel"];
16
+ readonly Tree: readonly ["Tree", "TreeControl", "TabSheet", "TabSheetPanel"];
17
+ readonly Filter: readonly ["TextFilter", "NumberFilter", "SelectFilter", "MultiSelectFilter", "PopupSelectFilter", "CategorySelectFilter", "YesNoSelectFilter"];
18
+ readonly ListField: readonly ["ListField", "ListControl"];
19
+ readonly Chart: readonly ["FlagCard", "Chart"];
20
+ readonly Etc: readonly ["PageRefreshButton", "MultiLangExpandButton", "Fragment", "CommentList", "SourceCodeEditor", "MarkdownViewer", "History"];
21
+ };
22
+ type PartTypesByCategory = typeof PART_TYPES_BY_CATEGORY;
23
+ export type PartType = PartTypesByCategory[keyof PartTypesByCategory][number];
24
+ /** 전 파트 타입 배열(레거시 enum 선언 순서 보존). */
25
+ export declare const PART_TYPES: readonly PartType[];
26
+ /** 파트 타입 → 카테고리 역인덱스. */
27
+ export declare const PART_CATEGORY: Record<PartType, PartCategory>;
28
+ /** 문자열이 알려진 builtin PartType인지 판별(호스트 확장 파트는 false). */
29
+ export declare function isPartType(v: string): v is PartType;
30
+ export {};
@@ -0,0 +1,24 @@
1
+ import { PartModel } from './types';
2
+ /**
3
+ * PartModel 트리 순회 유틸. 레거시 {@code PartModelVisitorSupport.java} 포팅 + 신규 헬퍼.
4
+ */
5
+ export type AcceptResult = 'CONTINUE' | 'STOP' | 'SKIP_CHILDREN';
6
+ export type PartModelVisitor = (model: PartModel) => AcceptResult;
7
+ /**
8
+ * pre-order 방문. 레거시 {@code PartModelAcceptor.accept}와 **동일 시맨틱**:
9
+ * - CONTINUE: 자식으로 하강.
10
+ * - SKIP_CHILDREN: 이 노드 자식은 건너뛰되 부모엔 CONTINUE로 보고(형제 계속).
11
+ * - STOP: 자식 방문 중 STOP이 나오면 그 **형제 그룹 스캔만** 중단하고 현재 노드 결과를 반환한다
12
+ * (전체 순회 중단 아님 — 조부모는 자기 다른 자식으로 계속). 레거시 특이 동작을 그대로 재현.
13
+ */
14
+ export declare function accept(model: PartModel, visitor: PartModelVisitor): AcceptResult;
15
+ /** partId로 트리 전체에서 파트를 찾는다. 레거시 {@code findPartModelRecursive}. */
16
+ export declare function findPartById(root: PartModel | null | undefined, partId: string): PartModel | undefined;
17
+ /** 전 노드 pre-order 방문(제어 없음). */
18
+ export declare function walk(root: PartModel, fn: (model: PartModel) => void): void;
19
+ /** 술어를 만족하는 모든 노드 수집(pre-order). */
20
+ export declare function collect(root: PartModel, pred: (model: PartModel) => boolean): PartModel[];
21
+ /** partId의 부모 파트. root이거나 없으면 undefined. */
22
+ export declare function parentOf(root: PartModel, partId: string): PartModel | undefined;
23
+ /** root→대상 노드까지의 경로(대상 포함). 없으면 undefined. path 핸들 해석 기반. */
24
+ export declare function pathTo(root: PartModel, partId: string): PartModel[] | undefined;
@@ -0,0 +1,40 @@
1
+ import { PartType } from './partType';
2
+ import { AttrVal } from './attrValue';
3
+ import { CaseMessage } from './caseMessage';
4
+ import { MultiLangText } from './lang';
5
+ /**
6
+ * core 모델 허브. 레거시 {@code PartModel.java} + {@code PartInfo.java}를 디자인시스템 중립
7
+ * 재귀 트리로 포팅한 **AI-native clean 모델**(G6)이며 인메모리 SoT다. adapter는 레거시 Jackson
8
+ * JSON(v1)을 이 모델로 **읽어 들이기만** 한다(단방향 lossy-observable 디코더 · write-back 폐기).
9
+ */
10
+ /** 파트별 에디터 능력 플래그(설계 데이터가 아닌 편집기 상태 — 카탈로그에서 파생 가능). */
11
+ export interface PartInfo {
12
+ draggable: boolean;
13
+ droppable: boolean;
14
+ selectable: boolean;
15
+ deletable: boolean;
16
+ }
17
+ /**
18
+ * UI 디자인의 트리 노드. 한 화면 = 하나의 PartModel 트리(root).
19
+ * - `attrs`: 컴포넌트 속성(타입드 {@link AttrVal}). 레거시 attrMap.
20
+ * - `layoutAttrs`: 부모 레이아웃이 부과한 속성(정렬/expandRatio 등). 레거시 layoutAttrMap.
21
+ * - `extraAttrs`: 자유형 부가 메타(root 페이지 정보 등, 이미 평문 문자열). 레거시 extraAttrMap.
22
+ *
23
+ * v1의 미표현 최상위 필드는 원문 보존하지 않는다(G6 — `legacyRaw` 폐기, 관찰적 폐기).
24
+ * v1-read 시 표면화되지 않은 필드는 커버리지 리포트로 보고된다.
25
+ */
26
+ export interface PartModel {
27
+ partId: string;
28
+ partType: PartType;
29
+ info?: PartInfo;
30
+ attrs: Record<string, AttrVal>;
31
+ layoutAttrs?: Record<string, AttrVal>;
32
+ extraAttrs?: Record<string, string>;
33
+ comment?: MultiLangText;
34
+ caseMessages?: CaseMessage[];
35
+ children?: PartModel[];
36
+ }
37
+ /** 편집 세션의 단일 상태 — UI는 레이아웃이 트리에 내재하므로 트리 하나. */
38
+ export interface EditorState {
39
+ root: PartModel;
40
+ }
@@ -0,0 +1,21 @@
1
+ import { PartModel } from './types';
2
+ import { PartCatalog } from '../catalog/types';
3
+ export type IssueSeverity = 'error' | 'warning' | 'info';
4
+ /** 검증 대상 종류. UI는 파트 트리라 대상은 항상 파트다(확장 여지 위해 union 유지). */
5
+ export type IssueTargetKind = 'part';
6
+ export interface ValidationIssue {
7
+ /** 규칙 식별자(예: 'CHK-CONTAIN-1'). 뷰/테스트가 코드로 참조하는 안정 키. */
8
+ ruleId: string;
9
+ severity: IssueSeverity;
10
+ targetKind: IssueTargetKind;
11
+ /** 문제 파트의 partId. 뷰가 클릭→포커스에 사용. */
12
+ targetRef: string;
13
+ /** 보조 ref(예: containment 이슈의 부모 partId) — 뷰 그룹핑/내비용. */
14
+ contextRef?: string;
15
+ message: string;
16
+ }
17
+ /**
18
+ * 구조 검증 — PartModel 트리(root)와 카탈로그를 받아 이슈 목록을 돌려준다. 순수 함수.
19
+ * 카탈로그는 containment·속성 정의의 출처라 필수 인자다(시블링의 옵션 ctx와 달리 검증의 본체 의존).
20
+ */
21
+ export declare function validatePartModel(root: PartModel, catalog: PartCatalog): ValidationIssue[];
@@ -0,0 +1,48 @@
1
+ import { MultiLangText } from './lang';
2
+ /**
3
+ * 필드 검증 설정 — **구조 객체 1급**(G6). 레거시는 세미콜론 구분 문자열 코덱
4
+ * (`required::#{msg}; length:1,10:#{msg}; `)으로 파트 `validate` attr에 인코딩했으나,
5
+ * clean 모델은 이 구조 객체를 그대로 값으로 갖는다({@code AttrVal kind:'validator'}).
6
+ *
7
+ * 레거시 문자열 → 이 구조로의 파싱은 v1-read 디코더({@link ../adapter/legacyValidator})에만 있고,
8
+ * 역방향 직렬화(write-back)는 폐기되었다. 메시지 필드는 {@link MultiLangText}(레거시 `#{key}`는
9
+ * `{key}` 형태로 디코드). Long → number, BigDecimal → string(부동소수 손실 방지 · 중립).
10
+ *
11
+ * 파생 원본: bluework-uimodeler-model/.../ui/ValidatorConfig.java
12
+ */
13
+ export interface ValidatorConfig {
14
+ useRequired?: boolean;
15
+ requiredMsg?: MultiLangText;
16
+ useDupCheck?: boolean;
17
+ dupCheckMsg1?: MultiLangText;
18
+ dupCheckMsg2?: MultiLangText;
19
+ useLength?: boolean;
20
+ lengthMin?: number;
21
+ lengthMax?: number;
22
+ lengthMsg?: MultiLangText;
23
+ useCurrencyRange?: boolean;
24
+ currencyRangeMin?: string;
25
+ currencyRangeMax?: string;
26
+ currencyRangeMinExclusive?: boolean;
27
+ currencyRangeMaxExclusive?: boolean;
28
+ currencyRangeMsg?: MultiLangText;
29
+ useLongRange?: boolean;
30
+ longRangeMin?: number;
31
+ longRangeMax?: number;
32
+ longRangeMinExclusive?: boolean;
33
+ longRangeMaxExclusive?: boolean;
34
+ longRangeMsg?: MultiLangText;
35
+ useDoubleRange?: boolean;
36
+ doubleRangeMin?: string;
37
+ doubleRangeMax?: string;
38
+ doubleRangeMinExclusive?: boolean;
39
+ doubleRangeMaxExclusive?: boolean;
40
+ doubleRangeMsg?: MultiLangText;
41
+ useRegexp?: boolean;
42
+ regexpPattern?: string;
43
+ regexpMsg?: MultiLangText;
44
+ usePlainTextLength?: boolean;
45
+ plainTextLengthMin?: number;
46
+ plainTextLengthMax?: number;
47
+ plainTextLengthMsg?: MultiLangText;
48
+ }
@@ -0,0 +1,125 @@
1
+ import { ComputedRef, InjectionKey, Ref } from 'vue';
2
+ import { PartModel } from '../core/types';
3
+ import { EditorState } from '../command/types';
4
+ import { PartPatch } from '../command/commands';
5
+ import { OpSyncTransport } from '../command/opSync';
6
+ import { OpConflict, PartId } from '../command/op';
7
+ import { ValidationIssue } from '../core/validation';
8
+ import { PartCatalog } from '../catalog/types';
9
+ /**
10
+ * 적재 메타(트랙 B 마커 + revSeed). 시블링의 `DiagramMeta`에 대응하나 UI adapter의 `fromPersisted`는
11
+ * clean 트리(PartModel)만 반환하므로(v1-read 단방향, G6) 마커는 호스트가 v2 문서에서 별도로 읽어 주입한다
12
+ * (M10 v2-read가 `PUiModelV2.schemaVersion/opWriteEnabled`와 partList rev에서 산출). op-mode·read-only·
13
+ * RevCache seed 판정의 단일 출처.
14
+ */
15
+ export interface UiModelMeta {
16
+ /** 데이터 형태 세대 — 1=v1 legacy read, 2=v2 평면(op-write 대상). 부재 시 1. */
17
+ schemaVersion?: number;
18
+ /** op 쓰기 롤아웃 게이트(true ⟹ schemaVersion>=2). v2 && false면 read-only 프리즈. 부재 시 false. */
19
+ opWriteEnabled?: boolean;
20
+ /** RevCache 시드 — partId→rev. M10 v2-read가 partList에서 산출해 주입(부재 시 전부 rev0). */
21
+ revSeed?: {
22
+ revs?: Record<PartId, number>;
23
+ };
24
+ }
25
+ export interface CreateUiEditorControllerOptions {
26
+ /** 파트 정의 카탈로그(팔레트/속성패널/드롭 가드/검증의 SoT). 미주입 시 builtin 84파트. */
27
+ catalog?: PartCatalog;
28
+ /** op 배치의 modelId(호스트 문서 식별자). 미주입 시 'ui-model'. */
29
+ modelId?: string;
30
+ /** 트랙 B 마커 + revSeed(호스트가 v2 문서에서 주입). op-mode·read-only 판정. */
31
+ meta?: UiModelMeta;
32
+ /**
33
+ * 호스트 op 서비스로 배치를 보내는 transport(`applyUiOpBatch` fetch 래퍼). op-mode + 주입 시에만
34
+ * 어댑터가 활성(미주입 = dev/test → 어댑터 비활성, v2 write 저장 경로는 별도 markSaved 흐름).
35
+ */
36
+ opTransport?: OpSyncTransport;
37
+ /** op 전송 실패(transport throw) 통지 — 진단용. 하드 프리즈는 별도 opConflict.kind='missing'. */
38
+ onOpError?: (err: unknown) => void;
39
+ /** op 멱등 키 생성기(테스트 결정성 주입). 기본 newPartId. */
40
+ newClientOpId?: () => string;
41
+ }
42
+ export interface UiEditorController {
43
+ state: EditorState;
44
+ /** 파트 카탈로그(뷰가 팔레트·속성패널·렌더러 매핑에 공유). */
45
+ catalog: PartCatalog;
46
+ /**
47
+ * 구조 검증 이슈(reactive 파생) — `validatePartModel(state.root, catalog)`. 순수 함수 + reactive
48
+ * state라 모델 변경 시 자동 재계산. 검증 패널과 인라인 배지가 공유하는 단일 출처(단일 호출).
49
+ */
50
+ issues: ComputedRef<ValidationIssue[]>;
51
+ /**
52
+ * targetRef(partId) → 그 파트의 이슈 목록. 인라인 배지가 파트 단위로 룩업해 최고 심각도를 표시한다.
53
+ */
54
+ issuesByRef: ComputedRef<Map<string, ValidationIssue[]>>;
55
+ canUndo: Ref<boolean>;
56
+ canRedo: Ref<boolean>;
57
+ dirty: Ref<boolean>;
58
+ /** 모델 변경 신호 — 명령 적용마다 단조 증가. 외부 관찰자가 재조회 트리거로 watch. */
59
+ revision: Ref<number>;
60
+ /**
61
+ * 편집 가능 여부 — false(조회 모드)면 모든 mutation(add/update/move/remove/undo/redo/save)이
62
+ * 차단된다. 선택·hover 같은 읽기성 동작은 유지. `run()`/undo/redo 한 곳에서 차단하는 데이터
63
+ * 안전의 최종 방어선. read-only(v2 && !opWriteEnabled)면 false로 시작한다.
64
+ */
65
+ editable: Ref<boolean>;
66
+ /** 조회/편집 전환. 편집 활성(true)은 op-mode에서만 유효(비-op doc은 no-op). false는 항상 허용. */
67
+ setEditable(v: boolean): void;
68
+ schemaVersion: Ref<number>;
69
+ opWriteEnabled: Ref<boolean>;
70
+ /** op-mode 활성 여부 — `schemaVersion>=2 && opWriteEnabled`. op emit 어댑터 attach 게이트. */
71
+ opMode: ComputedRef<boolean>;
72
+ /**
73
+ * 마지막 op 충돌(또는 전송 실패) — 어댑터 `onFreeze`가 채운다. null이면 정상. 충돌 시 `editable`이
74
+ * 함께 false로 내려가고(하드 프리즈), 호스트가 이 값으로 "다른 사용자가 변경함" UX를 띄운 뒤
75
+ * `loadDiagram`으로 재적재하면 해소된다. op-mode가 아니면 항상 null.
76
+ */
77
+ opConflict: Ref<OpConflict | null>;
78
+ /** 현재 선택 파트 id(단일 선택). 트리·캔버스·속성패널이 공유. */
79
+ selection: Ref<string | null>;
80
+ /** 선택된 파트 모델(없으면 null). */
81
+ selectedPart: ComputedRef<PartModel | null>;
82
+ select(partId: string | null): void;
83
+ /**
84
+ * 캔버스/트리 포커스 이동 요청 신호 — 외부(트리 클릭 등)에서 특정 파트로 스크롤/센터링 요청.
85
+ * nonce는 동일 id를 연속 클릭해도 watch가 매번 발동하게 하는 단조 증가 카운터.
86
+ */
87
+ focusRequest: Ref<{
88
+ id: string;
89
+ nonce: number;
90
+ } | null>;
91
+ requestFocus(id: string): void;
92
+ /**
93
+ * 파트 추가 — `parentId` 아래 `index` 위치에 `part` 서브트리 삽입. **canContain 드롭 가드**로
94
+ * deny면 차단(false 반환). warn/allow는 통과(warn=부모 규칙 미정 → validation이 사후 표면화).
95
+ * editable=false·부모 부재 시에도 false. 성공 시 새 파트를 선택하고 true 반환.
96
+ */
97
+ addPart(parentId: string, part: PartModel, index?: number): boolean;
98
+ /** 파트 비구조 필드 부분 수정(attrs/layoutAttrs/comment/…). editable=false면 no-op(false). */
99
+ updatePart(partId: string, patch: PartPatch): boolean;
100
+ /**
101
+ * 파트 이동 — `partId`를 `toParentId`의 `index` 위치로. **canContain 드롭 가드** + **사이클 가드**
102
+ * (자기 자신·후손으로의 이동 차단) 적용. editable=false·대상 부모 부재·파트 부재 시 false. 성공 시 true.
103
+ * `index`는 command 계약상 **대상 파트를 제거한 뒤 배열 기준**이다(같은 부모 재정렬 시 뷰가 `view/dnd`로 보정).
104
+ */
105
+ movePart(partId: string, toParentId: string, index?: number): boolean;
106
+ /** 파트 삭제(서브트리 통째). editable=false·root·파트 부재 시 false. 선택 중이면 선택 해제. */
107
+ removePart(partId: string): boolean;
108
+ undo(): void;
109
+ redo(): void;
110
+ markSaved(): void;
111
+ /**
112
+ * 프리즈 후(또는 외부 변경 감지 시) 재적재 — 호스트가 파싱한 root/meta를 주입(생성자와 대칭).
113
+ * state를 in-place 교체하고 스택을 리셋하며, 마커(schemaVersion/opWriteEnabled/editable)를 재평가하고
114
+ * op 어댑터를 재구성·재seed·재attach한다. undo 이력·선택·충돌 상태는 리셋된다.
115
+ */
116
+ loadModel(root: PartModel, meta?: UiModelMeta): void;
117
+ /** 열린 coalesce 그룹(드래그 등)을 즉시 호스트로 내보내고 경계를 봉인. save·blur에서 호출. op-mode 아니면 no-op. */
118
+ flush(): void;
119
+ /** op 큐가 비고 전송 중이 아닐 때 resolve(save 동기화·테스트용). op-mode가 아니면 즉시 resolve. */
120
+ whenOpsSettled(): Promise<void>;
121
+ }
122
+ export declare const EDITOR: InjectionKey<UiEditorController>;
123
+ /** 빈 root 파트(기본 컨테이너) — dev/테스트 시드. 미지 partType은 adapter와 동일하게 캐스팅(호스트 확장 허용). */
124
+ export declare function emptyRoot(partType?: string): PartModel;
125
+ export declare function createUiEditorController(initialRoot?: PartModel, options?: CreateUiEditorControllerOptions): UiEditorController;
@@ -0,0 +1,42 @@
1
+ export * from './core/partType';
2
+ export * from './core/attrValue';
3
+ export * from './core/caseMessage';
4
+ export * from './core/lang';
5
+ export * from './core/types';
6
+ export * from './core/id';
7
+ export * from './core/validatorConfig';
8
+ export * from './core/gridDefaultSort';
9
+ export * from './core/traverse';
10
+ export * from './core/validation';
11
+ export * from './adapter/persisted';
12
+ export * from './adapter/legacyText';
13
+ export * from './adapter/legacyValidator';
14
+ export * from './adapter/legacyGridSort';
15
+ export * from './adapter/attrCodec';
16
+ export * from './adapter/uiModelAdapter';
17
+ export * from './adapter/migrate';
18
+ export * from './adapter/interpretFlat';
19
+ export * from './catalog';
20
+ export * from './agent/symbolicOp';
21
+ export * from './agent/schema';
22
+ export * from './agent/strictSubset';
23
+ export * from './agent/resolver';
24
+ export * from './command/types';
25
+ export * from './command/op';
26
+ export * from './command/stack';
27
+ export * from './command/commands';
28
+ export * from './command/opSync';
29
+ export * from './agent/describeScreen';
30
+ export * from './agent/buildAgentBatch';
31
+ export * from './editor/controller';
32
+ export * from './view/RendererRegistry';
33
+ export * from './view/validationDisplay';
34
+ export { default as PartCanvas } from './view/PartCanvas.vue';
35
+ export { default as PartPalette } from './view/PartPalette.vue';
36
+ export { default as PropertyPanel } from './view/PropertyPanel.vue';
37
+ export { default as PartTree } from './view/PartTree.vue';
38
+ export { default as SchematicPart } from './view/SchematicPart.vue';
39
+ export { default as MultiLangTextField } from './view/MultiLangTextField.vue';
40
+ export { default as InlineIssueBadge } from './view/InlineIssueBadge.vue';
41
+ export * from './view/preview/protocol';
42
+ export { default as IframePreview } from './view/preview/IframePreview.vue';
@@ -0,0 +1 @@
1
+ .ui-schematic-part[data-v-46916ee5]{border:1px solid var(--ui-schematic-border, #cbd5e1);border-radius:4px;background:var(--ui-schematic-bg, #fff);font-size:12px;min-width:48px}.ui-schematic-part.is-selected[data-v-46916ee5]{outline:2px solid var(--ui-schematic-select, #3b82f6);outline-offset:1px}.ui-schematic-head[data-v-46916ee5]{display:flex;align-items:baseline;gap:6px;padding:3px 6px}.ui-schematic-type[data-v-46916ee5]{font-weight:600;color:var(--ui-schematic-type, #475569)}.ui-schematic-caption[data-v-46916ee5]{color:var(--ui-schematic-caption, #0f172a);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-schematic-body[data-v-46916ee5]{display:flex;flex-direction:column;gap:4px;padding:8px;min-height:32px;border-top:1px dashed var(--ui-schematic-border, #cbd5e1)}.ui-schematic-drop-hint[data-v-46916ee5]{align-self:stretch;text-align:center;padding:6px;font-size:11px;color:var(--ui-schematic-hint, #94a3b8);border:1px dashed var(--ui-schematic-border, #cbd5e1);border-radius:4px}.ui-canvas-node[data-v-d9963ad2]{display:block;position:relative}.ui-canvas-node[draggable=true][data-v-d9963ad2]{cursor:grab}.ui-canvas-node.is-drop-into[data-v-d9963ad2]{outline:2px dashed var(--ui-canvas-drop, #3b82f6);outline-offset:2px;border-radius:4px;background:var(--ui-canvas-drop-bg, rgba(59, 130, 246, .06))}.ui-canvas-node.is-drop-before[data-v-d9963ad2]:before,.ui-canvas-node.is-drop-after[data-v-d9963ad2]:after{content:"";position:absolute;left:0;right:0;height:2px;background:var(--ui-canvas-drop, #3b82f6);border-radius:2px}.ui-canvas-node.is-drop-before[data-v-d9963ad2]:before{top:-1px}.ui-canvas-node.is-drop-after[data-v-d9963ad2]:after{bottom:-1px}.ui-palette[data-v-ba70ed30]{font-size:12px}.ui-palette__group[data-v-ba70ed30]{padding:6px 8px;border-bottom:1px solid #f1f5f9}.ui-palette__title[data-v-ba70ed30]{margin:0 0 4px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:#64748b}.ui-palette__list[data-v-ba70ed30]{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:4px}.ui-palette__item[data-v-ba70ed30]{padding:2px 8px;border:1px solid #cbd5e1;border-radius:4px;background:#fff;color:#334155;cursor:grab}.ui-palette__item[data-v-ba70ed30]:hover{border-color:#3b82f6;color:#1e3a8a}.ui-mlang[data-v-fa16ddef]{display:flex;flex-direction:column;gap:4px}.ui-mlang__tabs[data-v-fa16ddef]{display:flex;gap:2px}.ui-mlang__tab[data-v-fa16ddef]{display:inline-flex;align-items:center;gap:1px;padding:1px 7px;border:1px solid #cbd5e1;border-radius:4px;background:transparent;color:#64748b;font-size:11px;cursor:pointer}.ui-mlang__tab.is-active[data-v-fa16ddef]{color:#0f172a;border-color:#3b82f6;background:#eff6ff}.ui-mlang__dot[data-v-fa16ddef]{color:#3b82f6;font-size:13px;line-height:1}.ui-mlang__input[data-v-fa16ddef]{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid #cbd5e1;border-radius:4px;font:inherit;font-size:12px}.ui-mlang__key[data-v-fa16ddef]{font-size:12px;color:#7c3aed;background:#f5f3ff;padding:2px 6px;border-radius:4px}.ui-inline-issue[data-v-a0454177]{display:flex;align-items:flex-start;gap:4px;margin:3px 0 0;font-size:12px;line-height:1.4}.ui-inline-issue__msgs[data-v-a0454177]{display:flex;flex-direction:column;min-width:0}.ui-inline-issue.is-error[data-v-a0454177]{color:#dc2626}.ui-inline-issue.is-warning[data-v-a0454177]{color:#d97706}.ui-inline-issue.is-info[data-v-a0454177]{color:#2563eb}.ui-props[data-v-74030a51]{font-size:12px;color:#0f172a}.ui-props__empty[data-v-74030a51]{color:#94a3b8;padding:8px}.ui-props__head[data-v-74030a51]{display:flex;align-items:baseline;gap:8px;padding:6px 8px;border-bottom:1px solid #e2e8f0}.ui-props__type[data-v-74030a51]{font-weight:600}.ui-props__id[data-v-74030a51]{color:#94a3b8;font-size:11px}.ui-props__form[data-v-74030a51]{border:0;margin:0;padding:0;min-width:0}.ui-props__form[disabled][data-v-74030a51]{opacity:.7}.ui-props__group[data-v-74030a51]{padding:6px 8px;border-bottom:1px solid #f1f5f9}.ui-props__group-title[data-v-74030a51]{margin:0 0 4px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:#64748b}.ui-props__row[data-v-74030a51]{display:grid;grid-template-columns:88px 1fr;gap:6px;align-items:start;margin-bottom:4px}.ui-props__label[data-v-74030a51]{padding-top:3px;color:#475569;overflow:hidden;text-overflow:ellipsis}.ui-props__field[data-v-74030a51]{min-width:0}.ui-props__input[data-v-74030a51]{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid #cbd5e1;border-radius:4px;font:inherit;font-size:12px}.ui-props__readonly[data-v-74030a51]{color:#94a3b8}.ui-tree-row[data-v-cfddebcf]{display:flex;align-items:baseline;gap:6px;width:100%;padding:3px 6px;border:0;background:transparent;text-align:left;font:inherit;font-size:12px;color:#334155;cursor:pointer}.ui-tree-row[data-v-cfddebcf]:hover{background:#f1f5f9}.ui-tree-row.is-selected[data-v-cfddebcf]{background:#dbeafe;color:#1e3a8a}.ui-tree-type[data-v-cfddebcf]{font-weight:600}.ui-tree-caption[data-v-cfddebcf]{color:#64748b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-iframe-preview[data-v-72e13e1b]{width:100%;min-height:120px;border:1px solid var(--ui-preview-border, #cbd5e1);border-radius:4px;background:var(--ui-preview-bg, #fff)}