API 참조 — 공개 표면
이 페이지는 제품 어셈블리에 있는 모든 공개 타입을 나열한다. 여기 나열되지 않은 것은 설계상 internal이다 — 공개 표면은 의도적으로 좁다.
- Core(
SheetForge.Core+SheetForge.Core.Tooling): 공개 타입 137개(Core: 131개, Core.Tooling: 6개). Core.Tooling은 리포팅과 push 계획 같은 임포트 시점 서비스를 담은 에디터 전용 절반이다 — 플레이어 빌드에는 전혀 포함되지 않는다. - Editor: 최상위 공개 타입 53개와 그 공개 중첩 타입들.
- Runtime: 타입 7개, 그리고 생성된 출력물.
이는 정확히 (InternalsVisibleTo가 없는) 소비자 시뮬레이션 테스트가 컴파일 대상으로 삼는 표면이다.
감지 계약(타입이 아님): 별도의 에셋은 Editor 어셈블리가 자체 등록하는
SHEETFORGE스크립팅 define 심볼을 통해 컴파일 타임에도 SheetForge가 설치되어 있음을 감지할 수 있다. 이는 define이지 공개 타입이 아니므로 아래 표에는 나열되지 않는다 — 플러그인 작성 ▸ 다른 에셋에서 SheetForge 감지하기 참고. (SHEETFORGE_ADDRESSABLES와는 별개다 — 후자는 Addressables 패키지가 있는지만 표시하는 내부 버전 정의다.)
관례: 시그니처는 축약되어 있다(… = 소스 XML 문서 참고). "순수(pure)"는 UnityEngine 없음 / IO 없음을 의미한다.
Core 어셈블리 (SheetForge.Core) — 순수 C#
UnityEngine 없음, IO 없음, 네트워크 없음, 도메인 지식 없음. 컴파일러로 강제됨: Core는 아무것도 참조하지 않는다.
플러그인 등록 계약 (SheetForge.Core.Plugins)
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
ISheetForgePlugin | 인터페이스 | 기본 도메인 플러그인 계약. string Name · RegisterEnums(EnumRegistry) · RegisterCellParsers(CellParserRegistry) |
ISheetForgeValidatorPlugin | 인터페이스 | 검증 규칙을 위한 선택적 애드온. RegisterValidators(DomainValidatorRegistry) |
ISheetForgeEdgePlugin | 인터페이스 | 엣지 선언을 위한 선택적 애드온. RegisterEdgeContributors(EdgeContributorRegistry) |
ISheetForgeMarkerPlugin | 인터페이스 | 커스텀 구조 마커를 위한 선택적 애드온. RegisterStructuralMarkers(MarkerRegistry) |
ISheetForgeTemplatePlugin | 인터페이스 | "시트 생성" 템플릿을 위한 선택적 애드온. RegisterTemplates(TemplateRegistry) |
ISheetForgeGraphPlugin | 인터페이스 | 탭별 데이터 스튜디오 캔버스 오버라이드를 등록하는 선택적 애드온. RegisterGraphShapes(GraphShapeRegistry) |
ISheetForgeCodeRegistryPlugin | 인터페이스 | 코드가 소유하는 참조 대상(잠긴 가상 탭)을 위한 선택적 애드온. RegisterCodeRegistries(CodeRegistryCatalog) |
ISheetForgeThemePlugin | 인터페이스 | 창 컬러 프리셋을 위한 선택적 애드온. RegisterThemes(ThemeRegistry) |
ISheetForgeStudioPlugin | 인터페이스 | 선언적 작성 표면(액션, 패널, 컬럼 배지, 셀 에디터 힌트)을 위한 선택적 애드온. RegisterStudioUi(StudioUiRegistry). Editor가 아니라 Core에 있으므로 등록 하나가 UIToolkit 에디터와 브라우저 양쪽에서 렌더링된다 |
ISheetForgeStringsPlugin | 인터페이스 | 언어별로 팩 자신의 UI 문자열을 등록하는 선택적 애드온. RegisterStrings(StringOverlayRegistry). 에디터에만 닿을 수 있던 폐지된 Editor 측 ISheetForgeLocPlugin / PluginLocRegistry 쌍을 대체한다 |
ISheetForgePipelinePlugin | 인터페이스 | 파이프라인 관찰자를 등록하는 선택적 애드온. RegisterPipelineObservers(PipelineObserverRegistry) |
구성과 호환성 (SheetForge.Core.Plugins)
발견은 호스트별이다 — 에디터에서는 Unity의 TypeCache, 웹에서는 브라우저의 업로드된 어셈블리 스캔. 그 이후의 모든 것(인스턴스화, 순서, 격리, 호환성 게이트)은 하나의 공유 Core 함수이며, 이것이 두 호스트가 슬롯 단위로 어긋나지 않게 지켜준다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
PluginComposition | static 클래스 | 유일한 어셈블리 경로. 타입당 하나의 인스턴스가 자신이 구현하는 모든 계약으로 캐스팅된다. 두 멤버와 진단 분리는 표 아래에 있다 |
PluginSet | sealed 클래스 | 조립된 결과 — 열두 슬롯: Enums · Parsers · Validators · EdgeContributors · Markers · Templates · GraphShapes · CodeRegistries · Themes · Strings · StudioUi · PipelineObservers. 새 슬롯은 여기에 추가되는 것만으로 두 호스트 모두에 닿는다 |
SheetForgePluginCompatAttribute | sealed 애트리뷰트(어셈블리) | [assembly: SheetForgePluginCompat(SheetForgePluginFormat.Current, MinHostVersion = "…", PluginVersion = "…")]. int FormatVersion · string MinHostVersion(숫자 점 표기 비교; null/빈 값 = 요구 사항 없음) · string PluginVersion(표시 전용, 결코 비교되지 않음). 아무것도 인스턴스화하지 않고 읽히며, 어셈블리 단위로 판정된다 — 거부된 어셈블리는 절반만 로드되는 대신 등록 전체를 잃는다. 없으면 = 세대 Minimum, 호스트 요구 사항 없음 |
SheetForgePluginFormat | static 클래스 | 세대 상수: const int Current · const int Minimum. 플러그인 형식 자체가 교체될 때만 움직인다 — 순수하게 추가되는 성장은 그 번호를 그대로 둔다 |
PluginComposition — 두 멤버:
IReadOnlyList<Type> ContractTypes— 발견 필터. 그 순서는 고정되어 있다. 진단이 나타나는 순서를 결정하기 때문이다.PluginSet Compose(IReadOnlyList<Type> candidateTypes, string hostVersion, ErrorCollector errors, ICollection<string> failures, Func<string,bool> isProductKey = null)— 조립 호출 자체.
두 종류의 문제는 서로 분리되어 유지된다. 등록 충돌과 호환성 거부는 errors의 구조화된 진단이 되고, 구현 버그 — 생성 실패, throw하는 콜백 — 는 failures의 영어 문장이 되며, 그곳에 null을 넘기면 이들은 버려진다.
후행하는 isProductKey 조건은 문자열 오버레이의 "플러그인은 제품 키를 덮어쓸 수 없다" 규칙이 Core가 언어 테이블을 전혀 보지 않고도 강제되는 방법이다: 규칙은 여기에 살고, 호스트는 재료만 공급한다. 이 조건을 생략하면 그 한 가지 규칙만 건너뛰어진다.
레지스트리 (SheetForge.Core.Model / .Validation / .Edges)
| 타입 | 역할 및 주요 멤버 |
|---|---|
EnumRegistry | Enum 이름 → CLR 타입(코드젠 소재). Register<TEnum>() · Register(name, memberNames) · TryGetMembers · TryGetClrTypeName · TryGetClrAssemblyName · RegisteredEnumNames. 추가 멤버 세 개는 표 아래에서 상세히 설명한다 |
CellParserRegistry | 타입 이름 → 셀 파서(개방-폐쇄). 중복 등록 시 예외 발생. Register(ICellValueParser) · TryGet · TryGetCustomRenderer · RegisteredTypeNames · RegisterWrapper(ICellWrapperType) · TryGetWrapper · RegisteredWrapperNames(래퍼 타입) |
DomainValidatorRegistry | 추가 전용 검증기 목록, 순서 보존. Register(IDomainValidator) · Validators |
EdgeContributorRegistry | 추가 전용 기여자 목록, 순서 보존. Register(IEdgeContributor) · Contributors |
MarkerRegistry | 마커 이름(@ 없음) → 커스텀 구조 마커. 내장 마커(SheetSyntax.ReservedMarkers — @name/@type/@desc/@overlap/@style/@enum/@loc)와의 충돌 / 중복 / 잘못된 식별자는 예외 발생. Register(IStructuralMarkerDefinition) · TryGet · IsEmpty · RegisteredMarkerNames · AppendMarkerTokens |
TemplateRegistry | "시트 생성" 템플릿 키 → 템플릿. 빈/중복 키, 빈 표시 이름, 탭이 하나도 없는 경우, 빈 탭 TSV는 예외 발생. Register(DataTemplate) · TryGet · Templates · IsEmpty |
EnumRegistry — 세 멤버 상세:
EnumRegistry(EnumRegistry parent)— 부모를 관통해 읽고 자신에게만 등록하는 자식이다. 부모는 도메인 리로드 전체에 걸친 플러그인 등록 CLR enum을, 자식은 이번 임포트의 시트 정의 enum을 담으므로, 임포트가 공유 캐시를 변경하는 일은 없다. 부모가 이미 소유한 이름을 등록하면 가리는 대신 예외를 던진다.Contains(name)— 자신 다음 부모, Ordinal.SetClrTypeName(name, fullTypeName)— 문자열만으로 등록된 이후 CLR 이름을 채운다. 타입이 아직 존재하지 않으므로 어셈블리 이름은 비어 있는 채로 남는다.
"시트 생성" 템플릿 (SheetForge.Core.Model)
| 타입 | 역할 및 주요 멤버 |
|---|---|
DataTemplate | 플러그인이 등록한 템플릿: string Key(레지스트리 정체성) · string DisplayName(플러그인이 소유한 텍스트) · IReadOnlyList<DataTemplateTab> Tabs(하나 이상) |
DataTemplateTab | 템플릿의 탭 하나: string TabName · string Tsv(완전한 정규화된 TSV — 마커 행 플러스 예제 데이터) |
커스텀 셀 타입 (SheetForge.Core.Model)
| 타입 | 역할 및 주요 멤버 |
|---|---|
ICellValueParser | 스칼라 셀 하나를 파싱한다. 실패 시 = context.Errors에 수집 + false 반환(결코 throw하지 않음). string TypeName · bool TryParse(CellParseContext, string, out object) |
ICustomCellType | 선택적인 코드젠/라운드트립 헬퍼. Type ValueType · bool TryRender(object, out string text, out string reason) |
IReferencingCellType | 등록된 ICellValueParser가 함께 구현할 수 있는 선택적 capability로, 자신의 표기법 안에 묻힌 키가 완전한 RecordId@Tab 대우를 받게 한다 — 무결성 + 제안, 페이로드가 보존되는 키 이름 변경 전파, 그래프 엣지와 포트, ▾ 피커, 고아 탐지, export되는 드롭다운 규칙. 등록된 파서를 캐스팅해서 발견된다(별도의 등록 없음). bool TryGetTokenKey(elementText, out key) · string MakeToken(key) · bool TryRetargetToken(elementText, newKey, out newText) · bool TryRemoveToken(elementText, key, out newText)(빈 결과 = 요소가 사라짐) · bool TryRewriteKeys(elementText, IReadOnlyDictionary<string,string> renames, out newText). 한 번의 호출 = 요소 하나(셀 전체, 또는 ;로 구분된 요소 하나)이므로, 페이로드는 ;를 포함할 수 없다. @target은 실제 시트 탭을 가리켜야 한다(그렇지 않으면 UnknownTargetTab). 결코 throw하지 않는다 — false/null은 "해석할 수 없음"을 뜻하며, 재작성은 나머지를 보존한다 |
IRefBearingValue | 위의 값 쪽 절반으로, 파싱된 값이 구현한다: IEnumerable<string> ReferencedKeys(선언 순서 = 진단과 제안 예산의 순서; null/빈 항목은 건너뛴다). 스캐너는 이를 읽고, 위의 텍스트 훅은 셀을 재작성한다. 둘 다 필요하다 — 파싱된 값은 작성자의 표기법을 복원할 수 없고, 텍스트는 읽히지 않고서는 검증될 수 없다 |
ICellWrapperType | 제네릭 래퍼 값 형태 MyWrapper<T>(예: Pair<int> = 1~2) — 래퍼가 외부 문법을 소유하고 Core가 내부 타입을 재귀적으로 파싱한다. string Name · bool TrySplit(string, out IReadOnlyList<string> pieces, out string reason) · string JoinCanonical(IReadOnlyList<string>) · Type OpenClrType · object Assemble(IReadOnlyList<object>, Type closed) · bool TryDisassemble(object, out IReadOnlyList<object>, out string reason) |
WrapperValue | 래퍼 셀의 파싱된 IR — 래퍼 전략을 담고 내부의 CellValue들을 노출한다(그래서 내부의 참조가 검증, 키/탭 이름 변경, Export를 그대로 통과한다). ICellWrapperType Wrapper · IReadOnlyList<CellValue> Inner |
IStructuralMarkerDefinition | 커스텀 @marker 행(컬럼별 값, 컬럼별로 검증 — @overlap을 일반화한 것). string MarkerName(@ 없음) · string Description · void ValidateCell(MarkerCellContext) |
MarkerCellContext | 마커 셀 검증 호출 하나. string MarkerName · string RawText · string FieldName · CellCoordinate Coordinate · void Reject(string reason, string suggestion = null)(→ MarkerCellInvalid) |
CellParseContext | 파싱 호출 하나의 컨텍스트. TypeToken Type · CellCoordinate Coordinate · ErrorCollector Errors · EnumRegistry Enums |
시트 문법 상수 (SheetForge.Core.Model)
셀 텍스트를 읽거나 쓰는 팩은 임포터와 동일한 문법으로 동작한다 — 리스트 셀을 나누고, @type 문자열을 조립하고, 이름이 이미 사용 중인지 확인하는 일들이다.
이 상수들이 그 문법의 단일 진실이므로, 팩은 결코 자신만의 구분자를 다시 적지 않는다: 문자를 복사해 두면 문법이 바뀌는 날 어긋난다. 목록들은 읽기 전용으로 제공되므로, 팩이 무엇을 하든 문법 자체를 바꿀 수는 없다. 이들이 나타내는 표기법은 시트 문법 페이지에 전부 문서화되어 있다 — 이것은 그 문법에 대한 프로그래밍적 접근 수단이다.
| 타입 | 종류 | 역할 |
|---|---|---|
SheetSyntax | 정적 클래스 | 시트 문법을 상수로 담은 것 — 아래에 묶어 정리 |
마커
CommentPrefix(#) ·MarkerPrefix(@).- 내장 마커 행마다 상수 하나씩:
NameMarker·TypeMarker·DescMarker·OverlapMarker·StyleMarker·EnumMarker·LocMarker. RequiredMarkers— 모든 시트가 반드시 지녀야 하는 세 개.ReservedMarkers— 모든 내장 이름. 커스텀 마커에 이름을 붙이기 전에 이것을 확인한다: 충돌은 등록 시점에 거부된다.
구분자
ListSeparator(;) — 리스트 원소 사이.EntrySeparator(,),FieldSeparator(:),SectionSeparator(|),KeyTimeSeparator(@) — 값 하나 안쪽의 층위들이며, 그래서;는 값 자신의 텍스트 안에는 결코 나타나지 않는다.StyleKeyValueSeparator(=) —@style셀 내부.
@type 표기법
OptionalSuffix(?) ·DefaultSeparator(=) ·TargetSeparator(@,RecordId@Tab에서처럼).ListTypeName·ListOpen(List<) ·ListClose(>).
타입 이름
- 내장 이름마다 상수 하나씩:
IntTypeName·FloatTypeName·BoolTypeName·StringTypeName·RecordIdTypeName·IntIdTypeName·AssetRefTypeName·LocRefTypeName·ColorTypeName·AnimationCurveTypeName·GradientTypeName·EnumTypeName. BuiltinScalarTypes와IsBuiltinScalarTypeName(name)— "이 이름이 이미 내장 타입인가?"를 파서가 그 이름으로 등록되기 전에 답해 준다.StyleKeyNames(title,color) ·LocReservedColumns(smart,comment).
값
TrueCanonical/FalseCanonical— 정본bool텍스트.NumberCellStyles— 모든 숫자 셀을 읽을 때 쓰는NumberStyles. 천 단위 구분자는 제외되고 컬처는 항상 고정(invariant)이므로, 로케일의 소수점 쉼표는 숫자를 조용히 바꾸는 대신 요란하게 실패한다.
도메인 검증 (SheetForge.Core.Validation)
| 타입 | 역할 및 주요 멤버 |
|---|---|
IDomainValidator | 컬럼 간/탭 간 규칙. 위반 사항은 4가지 요소를 모두 갖춘 DomainRuleViolation으로 ctx.Errors에 전달된다. string Name · Validate(DomainValidationContext) |
DomainValidationContext | Tables(탭 → SheetTable) · KeyIndices · AssetKeys(null = 생략됨) · Errors |
엣지 이음매 (SheetForge.Core.Validation / .Edges)
| 타입 | 역할 및 주요 멤버 |
|---|---|
ReferenceScanner(정적) | 참조 발생 열거를 위한 단일 진실 공급원. Scan(tables) · ScanTable · ScanField · IsReferenceField(TypeToken), 그리고 표 아래에서 상세히 설명하는 두 개의 참조 판정자 |
RefKeyKind(enum) | 참조가 문자열(RecordId) 키 공간과 정수(IntId) 키 공간 중 어느 것과 일치하는지. ReferenceScanner.GetReferenceKind가 반환하며, 소비자는 이를 기준으로 분기한다. 추가 전용 |
ReferenceOccurrence(구조체) | 발생 하나 — Kind · FromTab · RowNumber · ColumnNumber · FieldName · TargetTab · TargetId · ToCoordinate() |
ReferenceOccurrenceKind(enum) | Scalar · ListElement · ExplicitDefault · WrapperElement · CustomElement(IRefBearingValue가 자신의 표기법 밖에서 선언한 참조 — 내부 레이아웃이 그 타입의 것이므로 셀 수준 좌표를 갖는다). 추가 전용이므로 기존 값은 의미를 유지한다 |
IEdgeContributor | 스캐너가 볼 수 없는 엣지를 선언한다. 진단 정보 없음. string Name · ContributeEdges(EdgeContributionContext, ICollection<EdgeSpec>) |
EdgeSpec | 엣지 하나 — FromTab/FromRecordId/ToTab/ToRecordId(+ 선택적 FieldName, 레코드 엣지를 위한 PayloadTab/PayloadRecordId, Label) |
EdgeContributionContext | 읽기 전용 Tables + KeyIndices(에러 수집기 없음 — 엣지는 검증이 아니다) |
IAuthorableEdgeContributor | IEdgeContributor가 함께 구현할 수 있는 선택적 capability로, 그 엣지가 그래프 캔버스에서 편집 가능해지게 한다. bool TryPlanConnect(EdgeAuthoringContext, fromTab, fromRecordId, toTab, toRecordId, out EdgeCellWrite) · bool TryPlanDisconnect(EdgeAuthoringContext, RecordEdge, out EdgeCellWrite) — false = 아무것도 스테이징되지 않으며 어포던스는 사유와 함께 비활성화된다. 둘 다 try/catch 안에서 실행된다 |
IEdgeTokenEditor | IEdgeContributor가 함께 구현할 수 있는 선택적 capability로, 그 토큰의 나머지 부분(키가 아닌 모든 것)을 와이어 인스펙터에서 편집할 수 있게 한다. bool TryDescribeToken(EdgeAuthoringContext, RecordEdge, out EdgeTokenDescription) · bool TryPlanSetModifier(EdgeAuthoringContext, RecordEdge, string newModifier, out EdgeCellWrite) — 둘 다 같은 셀을 읽는다(엣지는 자신이 무엇을 가리키는지는 알지만 오늘 어떻게 쓰여 있는지는 모른다). false = 행이 숨겨지거나 정직하게 비활성화된다. 둘 다 try/catch 안에서 실행된다 |
EdgeTokenDescription | 토큰이 무엇이며 그 나머지를 어떻게 편집하는지 — TokenText(강조할 조각) · ModifierText · HasModifier · ModifierLabel · IsChoice · Options / OptionLabels. new EdgeTokenDescription(tokenText) = 나머지 없음, 그래서 행이 그려지지 않는다. choice 생성자는 옵션 목록이 비어 있으면 자유 텍스트로 대체된다 |
IBatchAuthorableEdgeContributor | IAuthorableEdgeContributor의 형제인 선택적 capability(상속이 아니다): 하나의 제스처가 병렬로 짝지어진 여러 셀을 함께 바꿔야 하는 데이터를 위해, 연결/연결 해제 계획을 셀 쓰기의 목록으로 만든다. bool TryPlanConnectMany(EdgeAuthoringContext, fromTab, fromRecordId, toTab, toRecordId, out IReadOnlyList<EdgeCellWrite>) · bool TryPlanDisconnectMany(EdgeAuthoringContext, RecordEdge, out IReadOnlyList<EdgeCellWrite>) — 전체 목록이 하나의 undo 단계로 스테이징되거나 전혀 스테이징되지 않는다. 단수 기여자는 계속 동작하며(폴백), 한 클래스가 둘 다 구현하면 배치 형태가 우선한다 |
IVirtualNodeFactory | 선택적 capability: 새 시트 행이 아닌 캔버스 "생성" 제스처. IReadOnlyList<VirtualNodeKind> KindsFor(EdgeAuthoringContext, tab, recordId)(메뉴 빌드마다 호출 — 가볍게 유지) · bool TryPlanCreate(EdgeAuthoringContext, tab, recordId, VirtualNodeKind, out IReadOnlyList<EdgeCellWrite>) — false = 세션이 건드려지지 않는다. 계획은 같은 제스처에서 생성된 레코드를 대상으로 할 수 없다 |
VirtualNodeKind(구조체) | 생성 가능한 종류 하나 — Id(선택 시 그대로 반환됨) · Label(이미 번역된 메뉴 텍스트; /는 서브메뉴를 만든다) · IsUsable. null 안전, default 안전 |
IEdgeSlotDeclarer | 선택적 capability: (가상) 노드가 살아 있는 엣지 없이도 여는 포트. IReadOnlyList<DeclaredSlot> DeclareSlots(EdgeAuthoringContext, nodeTab, nodeRecordId) — 선언된 슬롯은 연결 메뉴, 포트 피커, 카드의 포트 행에 합류한다. 렌더링마다 호출되므로 구현은 가볍고 부작용이 없어야 한다 |
DeclaredSlot(구조체) | 선언된 슬롯 하나 — FieldName(노드 안에서 고유해야 하며, 와이어가 앵커링되려면 기여자 엣지의 FieldName과 일치해야 한다) · TargetTab · IsList · IsUsable. null 안전, default 안전 |
EdgeAuthoringContext | 계획 입력값 — Tables + string CellText(tab, recordId, field), 이는 셀을 지금 읽히는 그대로(baseline 더하기 스테이징) 반환하므로, 연달아 만든 두 링크는 서로를 본다 |
EdgeCellWrite(구조체) | 계획: TabName · RecordId · FieldName · NewRawText(비어 있으면 = 지움) · IsAddressable. 행 번호가 아니라 키로 주소화된다 |
ReferenceScanner — 두 개의 참조 판정자:
GetReferencedTab(TypeToken)— 모든 소비자가 묻는 단일 판정자, "이것은 참조인가, 그렇다면 어디를 향하는가".RecordId@Tab에 대해,IntId@Tab(정수 키 공간)에 대해, 래퍼 내부에 대해, 그리고IsCustomReference로 표시된 커스텀 타입에 대해 답한다. 옵트인 하나 — 그리고IntId@Tab의 경우 이 판정자의 확장 하나 — 가 이들을 한 번에 모두 켜는 이유다.GetReferenceKind(TypeToken)→RefKeyKind— 그 참조가 문자열 키 공간과 정수 키 공간 중 어느 쪽과 비교되는지를 알려주므로, 이름 변경 전파·드롭다운·피커가 올바르게 분기한다.GetReferenceKind(TypeToken, tables)— 테이블을 인지하는 오버로드.
코어 참조는 자신의 공간(RecordId / IntId)을 스스로 명시한다. 참조형 커스텀 타입에는 이를 말할 표기법이 없다 — MyType@Tab이 유일한 표기이므로 — 그래서 그 공간은 대상 탭의 아이덴티티로부터 유도된다: RecordId 자기 키는 문자열 공간을, IntId만 있으면 정수 공간을 뜻하며, 알 수 없는 탭이나 null 테이블은 토큰만 있는 오버로드와 동일한 답인 문자열 공간으로 대체된다. 그 유도 방식 덕분에 기존 IReferencingCellType 구현이 코드 한 줄도 바꾸지 않고 IntId 키 탭을 겨냥할 수 있다.
참조 그래프 인덱스 (SheetForge.Core.Edges)
코어가 스캔한 참조와 기여자 엣지를 하나의 모델로 병합해 양방향으로 색인하는 불변 스냅샷이다. 표시 소재다 — 결코 진단 정보를 생성하지 않는다(문제에 대한 유일한 진실 공급원은 계속 프로젝션의 Diagnostics다).
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
RecordEdge(구조체) | value | 엣지 하나. RecordEdgeOrigin Origin · FromTab · FromRecordId(필드 수준 엣지는 비어 있음) · FieldName · RowNumber / ColumnNumber(1부터 시작; 0 = 필드/탭 수준) · ToTab · ToRecordId(해석되지 않았을 때도 의도된 id) · bool IsDangling(빌드 시점에 고정됨) · Label · PayloadTab / PayloadRecordId(레코드 엣지) |
RecordEdgeOrigin(enum) | — | CoreReference(RecordId@Tab 셀에서 읽음 — 좌표를 가짐) · Contributor(IEdgeContributor가 선언함 — 레코드 수준) |
ReferenceIndex | sealed 클래스 | 스냅샷. 정적 Build(tables, keyIndices, contributorEdges, codeRegistries, extraKeys = null)(마지막 세 개는 null일 수 있다. extraKeys = 존재하지만 아직 파싱되지 않은 탭 → 키, 예를 들어 작성 표면이 방금 스테이징한 행 — 그래서 그것들로의 링크는 깨진 것으로 그려지지 않는다) · AllEdges(결정적 순서: from-tab Ordinal → 행 → 컬럼 → 발생) · OutEdges(tab, recordId) / InEdges(tab, recordId)(결코 null 아님) · int InCount(tab, recordId) · bool TryGetRowKey(tab, rowNumber, out recordId) · DanglingEdges |
데이터 스튜디오 레코드 캔버스 (SheetForge.Core.Graphing)
캔버스는 무엇을 그릴지 스스로 결정한다: 열어본 레코드(종착점)로부터 참조 인덱스를 바깥으로 따라가며 결과를 결정적으로 배치한다. 플러그인은 그 그림을 대체하지 않는다 — 그저 더할 뿐이다. 전체가 순수한 데이터다: 컬럼은 픽셀이 아니라 그리드 셀이며, 색상은 창이 팔레트로 매핑하는 자유 형식 Category 문자열이다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
IRecordCanvasAugmenter | 인터페이스 | 하나의 탭에 대한 캔버스 오버라이드로, 클로저가 조립된 이후에 호출된다. Augment(GraphBuildContext, CanvasAugmentBuilder, string terminusTab, string terminusRecordId). 아무것도 추가하지 않으면 코어의 그림이 그대로 남는다. throw는 창이 붙잡아 영어 콘솔 경고가 된다. 아이덴티티는 데이터에 속한다(가상 노드는 같은 키의 실제 레코드에게 진다) — 표현(표시 힌트)은 그렇지 않다 |
CanvasAugmentBuilder | sealed 클래스 | 쓰기 표면으로, 오직 네 가지만 있다 — 멤버와 규칙은 표 아래에 있다 |
GraphShapeRegistry | sealed 클래스 | 탭 이름 → 캔버스 오버라이드. Register(tabName, IRecordCanvasAugmenter)(중복 탭 / 빈 이름 / null은 예외) · TryGet · IsEmpty |
GraphBuildContext | sealed 클래스 | 오버라이드의 읽기 전용 입력값. Tables(탭 → SheetTable) · ReferenceIndex References · IReadOnlyList<CodeRegistrySource> CodeRegistries(비어 있음, 결코 null 아님). 에러 수집기 없음 — 캔버스는 표시일 뿐 검증이 아니다 |
GraphSpecBuilder | sealed 클래스 | 그래프 조립 헬퍼. 생성자 (GraphBuildContext) · 정적 NodeKey(tab, recordId)(와이어가 가리키는 단일 진실) · AddNode(GraphNodeSpec)(첫 (Key, Column)이 우선) · AddWire(GraphWireSpec) · AddWire(fromKey, toKey, label, fromTab, fromRecordId, fieldName, isCyclic = false, cyclicNote = null)(링크가 쓰이는 셀도 함께 명명하는 오버로드로, 이것이 와이어를 편집 가능하게 만드는 것이다) |
GraphSpec | sealed 클래스 | 캔버스가 그리는 조립된 결과 — Nodes · Wires(조립은 빌더를 거치며, 생성자는 internal이다) |
GraphNodeSpec | sealed 클래스 | 노드 하나. Key · Tab · RecordId · Title · Subtitle · Category · CellCoordinate Address · Column / Row(캔버스가 이미 풀어낸 그리드 셀 — 여기서는 선택되는 것이 아니라 운반된다) · IsFocus(종착점) · IsMissing · InCount · IsCyclic |
GraphWireSpec | sealed 클래스 | 와이어 하나. FromKey · ToKey · Label · IsCyclic · CyclicNote, 그리고 선택적으로 소유 셀: FromTab · FromRecordId · FieldName · RecordEdge? SourceEdge(null = 표시 전용 와이어 — 그러면 캔버스는 편집할 수 없다고 말한다). 다섯 개의 표시 인자는 변하지 않았으므로, 기존 호출은 그대로 컴파일되고 동일하게 렌더링된다 |
IAuthorableGraphShape | 인터페이스 | IRecordCanvasAugmenter가 함께 구현할 수 있는 선택적 capability. IReadOnlyList<string> CreatableTabs(GraphBuildContext, string tabName) — 캔버스가 레코드를 생성할 수 있는 곳(비어 있음 = 아무 데도 없음). 이것이 없을 때의 두 가지 기본값은 표 아래에 있다 |
CanvasAugmentBuilder — 쓰기 표면. 오직 네 가지뿐이다:
AddNode(tab, recordId, title = null, category = null)/AddNode(tab, recordId, title, category, CellCoordinate address)— 시트 레코드가 아닌 아이덴티티(이벤트 키, 코드 원자)를 위한 가상 노드다. 탭은 비어 있을 수 있다.AddEdge(fromTab, fromRecordId, toTab, toRecordId, label = null, fieldName = null, fieldOnTarget = false, isCyclic = false, cyclicNote = null)— 코어 스캐너가 볼 수 없는 추가 엣지다.fieldName을 명명하면 링크가 어느 셀에 쓰이는지를 말하고,fieldOnTarget은 그 셀이 출발이 아니라 도착에 있다고 말하며, 순환 한 쌍은 도메인만 아는 note와 함께 루프를 표시용으로 표시한다.SetLayer(tab, recordId, layer)— 절대적 레이어 힌트(0 = 가장 왼쪽, 음수 = 더 왼쪽, 나머지는 이를 보정하기 위해 오른쪽으로 이동한다).SetLayerRelative(tab, recordId, offset)— 동일하지만 종착점으로부터 센다(−1 = 그 왼쪽 한 칸), 어떤 힌트가 이동시키기 전의 종착점 컬럼을 기준으로 해석된다.SetSubtitle(tab, recordId, subtitle)— 표시 힌트로, 이미 존재하는 레코드와 화면에 없는 레코드 모두에 적용되는 유일한 것이다(연결 피커가 그것들을 읽는다).
키가 빈 항목은 무시된다. 무엇이 수집되었는지는 internal이다 — 병합 규칙이 한 곳에 살기 때문이다. 모든 확장은 후행이므로, 더 이른 표면을 대상으로 작성된 오버라이드도 여전히 컴파일된다.
IAuthorableGraphShape — 이것이 없을 때의 두 기본값:
- 이 capability가 대체하는 생성 가능 탭 목록 — 캔버스가 애초에 열리는지, 대기 행 스윕이 얼마나 도달하는지도 결정하는 축 — 은 스키마를 전이적으로 따라가며 포커스 탭에서 도달 가능한 모든 탭을 포함한다.
- 사용자가 실제로 보게 되는 연결 계단식 목록은 현재 그려진 포트들이 가리키는 탭에서 시작한다.
둘 다 코드 레지스트리 탭과 키 컬럼이 없는 탭을 제외한다. 이것이 반환하는, 그려진 어떤 포트도 받아들이지 않는 탭은 사유가 붙은 채로 연결 계단식 목록에 남으며, 창 자신의 게이트가 여전히 그 위에 적용된다.
컬러 프리셋 (SheetForge.Core.Theming)
| 타입 | 역할 및 주요 멤버 |
|---|---|
ThemeRegistry | 프리셋 id → 테마. 빈 id, 중복, 그리고 예약된 두 내장 id는 예외를 던진다. Register(SheetForgeTheme) · TryGet · Themes · IsEmpty · IsBuiltInId(id) · BuiltInDefaultId · BuiltInHighContrastId |
SheetForgeTheme | 컬러 프리셋 하나. Id · DisplayName · DarkColors / LightColors(IReadOnlyDictionary<ThemeColorSlot, uint>, 생성 시 복사됨) · TryGetColor(dark, slot, out rgb) · IsEmpty |
ThemeColorSlot | enum — 프리셋이 오버라이드할 수 있는 33개의 색상 역할(표면, 선, 텍스트, 시맨틱 색상, 스테이징 표시, 실패 표면, 스크림, 그래프). 색상은 0xRRGGBB다: Core는 엔진 타입을 전혀 참조하지 않으며, 반투명 채우기는 슬롯 색상에 고정된 알파를 더해 파생된다. 추가 전용. |
프리셋은 자신이 이름 붙인 슬롯만 오버라이드한다. 그 외의 모든 슬롯은 제품 기본값을 유지하므로, 슬롯이 추가되어도 프리셋은 계속 유효하다. 등록이 곧 프리셋 적용은 아니다 — 사용자가 Preferences ▸ SheetForge ▸ Theme에서 하나를 선택한다.
선언적 작성 표면 (SheetForge.Core.Studio)
플러그인은 무엇을 보여줄지를 서술한다 — 껍데기는 데이터로, 조건과 효과는 델리게이트로 — 그러면 각 호스트는 자신의 위젯으로 이를 그린다: 에디터에서는 UIToolkit, 브라우저에서는 React. 레이아웃 수치는 어디에도 나타나지 않는다. 무엇을 말할지는 플러그인의 것이고, 어떻게 배치할지는 렌더러의 것이다.
여기 있는 모든 enum은 추가 전용이므로, 어휘가 자라나도 등록은 그 의미를 유지한다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
StudioUiRegistry | sealed 클래스 | RegisterStudioUi가 채우는 것. AddAction(StudioActionDescriptor) · AddPanel(StudioPanelDescriptor) · AddColumnBadge(StudioColumnBadgeDescriptor) · AddCellEditorHint(StudioCellEditorHint) · Actions / Panels / ColumnBadges / CellEditorHints · IsEmpty |
StudioUiNode | sealed 클래스 | 서술된 조각 하나, 불변, 정적 팩토리로 만들어진다 — 팩토리, 읽기용 속성, URL 규칙은 표 아래에 있다 |
StudioUiNodeKind | enum | 위의 13가지 종류(Row … Link) |
StudioActionDescriptor | sealed 클래스 | 동사(verb) 하나. Id(고유) · LabelKey(Loc 키; 등록되지 않으면 그대로 표시) · StudioActionPlacement Placement · Func<StudioSurfaceContext,bool> AppliesTo · Action<StudioSurfaceContext> Execute · ConfirmKey(선택 — 호스트가 이 문장을 먼저 묻는다). 호스트는 호출 시점에 AppliesTo를 다시 확인하므로, 오래된 메뉴 항목은 정직한 무동작과 다시 그리기로 답한다 |
StudioActionPlacement | enum | Inspector · RowContextMenu · TopbarMenu · ColumnHeaderMenu · CanvasNodeMenu. 각 자리는 서로 다른 컨텍스트 필드를 채운다 — 행 자리는 레코드를, 컬럼 자리는 컬럼 이름을, 캔버스 자리는 그 노드의 레코드를 담는다 |
StudioPanelDescriptor | sealed 클래스 | 스튜디오 오른쪽 패널의 패널 하나. Id · TitleKey · Func<StudioSurfaceContext,StudioUiNode> Build — 재계산 틱마다 다시 만들어지므로 상태를 갖지 않는다. 아무 패널도 등록되지 않으면 그 패널은 아예 그려지지 않는다 |
StudioColumnBadgeDescriptor | sealed 클래스 | 컬럼 헤더 옆의 배지 하나. Func<StudioSurfaceContext,string,string,StudioUiNode> Provide(컨텍스트, 탭, 필드) — null은 그 컬럼에 아무것도 없음을 뜻한다 |
StudioCellEditorHint | sealed 클래스 | "이 타입에는 이 내장 위젯을 써라" — 직접 제공하는 대신 종류를 고르는 것이다. TypeName(정확한 CellParserRegistry 타입 이름; 리스트 셀은 요소 이름으로 매칭되고, 래퍼 셀은 정본 텍스트를 유지하며 결코 매칭되지 않는다) · StudioCellEditorArchetype Archetype · GetOptions(드롭다운 전용 — Func<context, tab, field, IReadOnlyList<StudioCellOption>>) · SliderMin / SliderMax · ToggleTrueValue / ToggleFalseValue. 재료 형태마다 하나씩, 생성자 네 개. 등록된 IStudioCellEditorProvider가 거부한 이후, 내장 분기 이전에 참조된다; 팩의 힌트는 아래의 내장 힌트보다 먼저 참조되므로, Color, AnimationCurve, Gradient 아래에 하나를 등록하면 그 타입의 기본 에디터를 오버라이드한다. 요소 힌트가 ColorPicker, CurveEditor, GradientEditor인 List<>는 두 호스트 모두에서 칩 에디터가 된다 |
StudioCellEditorArchetype | enum | Dropdown · MultilineText · Slider · Toggle · ColorPicker(셀 텍스트 #RRGGBB / #RRGGBBAA) · CurveEditor(셀 텍스트 = 정규 CurveValue 표기) · GradientEditor(셀 텍스트 = 정규 GradientValue 표기). 추가 전용이다 — 가장 최근 두 개는 5와 6이다 |
BuiltinCellEditorHints | 정적 클래스 | Core 자신이 선언하는 세 가지 힌트 — Color → ColorPicker, AnimationCurve → CurveEditor, Gradient → GradientEditor — 팩의 힌트와 동일한 경로를 거치므로, 에디터와 브라우저가 이들에 대해 서로 다른 위젯을 고를 수 없다. IReadOnlyList<StudioCellEditorHint> All(고정 순서) · bool TryGet(typeName, out hint)(Ordinal). 호스트는 먼저 StudioUiRegistry.CellEditorHints를 참조하고 이 테이블로 폴백한다 |
StudioCellOption | sealed 클래스 | 드롭다운 후보 하나 — Value(셀에 기록되는 정본 텍스트) · Label(사람이 읽는 것; 기본값은 Value) |
StudioSurfaceContext | sealed 클래스 | 확장이 보고 그것을 통해 행동하는 유일한 이음매. 읽기: Tables · ReferenceIndex References · CodeRegistries · Tab · RecordId · Field · ActionArgument(Input 노드가 커밋한 값). 중재된 변경, 그 외에는 아무것도 없다: Action<string,string,string,string> StageCell · Action<IReadOnlyList<EdgeCellWrite>> StageCells(하나의 Undo 단계, 전부 아니면 전무) · Action<string,string> FocusRecord · Action RequestRebuild. 스테이징은 창 자신의 게이트를 통과한다 — 그래서 읽기 전용 소스, 실행 중인 파이프라인, 워크북 기반 탭은 사유와 함께 이를 차단한다(생성자는 internal: 호스트가 이를 조립한다) |
StudioUiNode — 팩토리, 읽기용 속성, URL 규칙:
- 팩토리:
Row·Label·Chip·Badge·Button·Rule·Heading·KeyValue·Table(headerRow, rows)·List·Progress·Input·Link, 그리고 이 노드를 바꾸는 대신 새 노드를 반환하는WithTooltip(text). - 읽기용 속성:
Kind·Text·Tooltip·ThemeColorSlot? Tone(결코 하드코딩된 색상이 아니므로 테마를 따른다) ·ActionId·Detail·Ratio·Url·Children. - static
bool IsAllowedUrl(url)—http/https만. 두 호스트 모두가 묻는 하나의 조건이므로, 무엇이 열기에 안전한지에 대해 서로 다르게 판단할 수 없다.
플러그인 UI 문자열 (SheetForge.Core.Model)
| 타입 | 역할 및 주요 멤버 |
|---|---|
StringOverlayRegistry | 플러그인이 등록한 UI 문자열의 수집기 그리고 조회용 오버레이다. Loc.Tr(에디터)와 t()(브라우저)는 제품 테이블보다 먼저 이를 참조한다. Register(key, language, value) · Register(key, IReadOnlyDictionary<string,string> byLanguage) · bool TryGet(key, language, out value) · RegisteredKeys. 언어 매칭과 네 가지 거부는 표 아래에 있다 |
StringOverlayRegistry — 매칭과 거부. language는 IETF 코드다("en", "ko", "zh-Hans", "pt-BR" 등), 대소문자 구분 없이 매칭된다. 조회는 요청 언어 → 영어 → 실패 순으로 대체되며, 그 대체 로직이 여기 살기 때문에 두 호스트가 동일하게 답한다.
네 가지 등록이 거부되며, 각각 조용히 실패하는 대신 개발자용 사유를 기록한다:
- 제품 내장 키 — 오버레이는 키를 추가할 수 있을 뿐, 제품 자신의 문장이나 메뉴 경로를 결코 덮어쓸 수 없다;
- 다른 팩이 이미 등록한 키+언어 조합 — 먼저 발견된 것이 이긴다. 그렇지 않으면 설치 순서가 화면을 결정할 것이다;
- 빈 키 또는 빈 값;
- 제품이 알지 못하는 언어 코드 — 결코 영어로 접혀 들어가지 않는다.
파이프라인 관찰 (SheetForge.Core.Plugins / .Model)
| 타입 | 역할 및 주요 멤버 |
|---|---|
IPipelineObserver | 읽기 전용 알림. void OnImportCompleted(PipelineRunView view) — 명시적 임포트 사이클당 한 번, 그 끝에서, 성공이든 실패든. 값을 바꾸거나 진단을 추가하는 훅은 의도적으로 없으며(그것들은 셀 타입과 IDomainValidator의 몫이다), 스테이징 사전 검증에서 실행되는 것도 없다. throw는 사유가 수집된 채로 격리된다. 임포트 출력은 바뀌지 않는다. 향후의 관찰 지점은 등록된 관찰자로부터 캐스팅되는 형제 capability 인터페이스로 도착하므로, 오늘 작성된 구현도 계속 컴파일된다 |
PipelineObserverRegistry | 추가 전용 관찰자 목록, 순서 보존. Register(IPipelineObserver) · Observers |
PipelineRunView | 관찰자가 받는 불변 스냅샷 — Success(검증, 즉 레지스트리가 조립되었는지 여부; codegen/베이크 결과는 진단 정보에서 읽힌다) · Tables(파싱된 탭들; 실패한 실행에서는 파싱될 수 없었던 탭만 빠져 있다. 부분 조립 없음은 출력 규칙이지 관찰 규칙이 아니기 때문이다) · Diagnostics(리포트가 보여주는 것과 동일한 목록) · SkippedTabs · EnumTabs. 컬렉션은 생성 시 복사되며, 생성자는 internal이라 절반만 만들어진 스냅샷이 관찰자에게 건네질 수 없다 |
코드 레지스트리 (SheetForge.Core.Graphing)
코드 안에 존재하는 참조 대상으로, 작성 표면에 잠긴 가상 탭으로 노출된다. 임포트 검증기가 아니라 데이터 스튜디오(사이드바 / 그래프 / 인스펙터)가 소비한다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
CodeRegistryCatalog | sealed 클래스 | 등록 루트. Register(CodeRegistrySource)(null / 빈 탭 이름 / 중복된 탭 이름은 예외) · TryGet(tabName, out source) · Sources · IsEmpty |
CodeRegistrySource | sealed 클래스 | 잠긴 가상 탭 하나. string TabName · IReadOnlyList<CodeRegistryEntry> Entries(등록 순서 = 표시 순서) |
CodeRegistryEntry | sealed 클래스 | 항목 하나. string Key(참조가 가리킬 수 있는 것) · string Label · IReadOnlyList<string> Raises(null은 빈 값으로 정규화됨). Core는 세 가지 모두를 불투명한 문자열로 취급한다 |
IR 읽기 모델 (SheetForge.Core.Model)
| 타입 | 역할 및 주요 멤버 |
|---|---|
SheetTable | 탭 하나의 파싱 결과물. SheetSchema Schema · IReadOnlyList<SheetRecord> Records |
SheetSchema | string TabName · Fields · TryGetField(name, out FieldSchema) · SheetStyle Style(시트의 @style 표시 메타데이터) · bool IsLocalizationSheet(@loc 마커가 있다) · IReadOnlyList<LocaleColumn> LocaleColumns(원래 컬럼 순서 그대로의 로케일 컬럼들 — 현지화 시트가 아닌 시트에서는 비어 있으며, 결코 null이 아니다) · TryGetLocaleColumn(localeCode, out LocaleColumn)(코드로 조회하며, 대소문자를 구분하지 않는다) · TryGetSourceLocale(out LocaleColumn)(첫 번째 로케일 컬럼. 하나도 없으면 false) |
SheetStyle | @style 행의 값 — 시트 하나에 대한 표시 메타데이터. string Title(사이드바 그룹 라벨) · string ColorHex(작성된 그대로의 #RRGGBB) · bool HasColor · 정적 None(스타일 없음). 코드젠, 베이크, 스키마 핑거프린트는 결코 이를 읽지 않는다 |
LocaleColumn(구조체) | 현지화 시트의 로케일 컬럼 하나 — @loc 행이 그 컬럼에 기록한 것. string Code(작성된 그대로의 코드. 코어는 철자 형태를 검증할 뿐, 그 로케일이 실재하는지는 검증하지 않는다) · string FieldName · int ColumnNumber(1부터 시작) · bool IsSource(첫 번째 로케일 컬럼 — 인라인 미리보기가 읽고 민팅이 써넣는 그 컬럼) |
SheetRecord | int RowNumber(원본, 1부터 시작) · Values(필드 → CellValue) · TryGet · 인덱서 |
FieldSchema | Name · TypeToken Type · Description · ColumnNumber · DefaultValue · AllowOverlap · IReadOnlyDictionary<string,string> MarkerValues(커스텀 마커 이름 → 이 컬럼의 셀 텍스트) |
TypeToken | 파싱된 @type 셀. RawText · TypeName · TypeArgument · TargetName · IsList · IsOptional · HasExplicitDefault · DefaultValueText · AllowsEmptyCell · IsSelfKey · IsIntId(이 탭의 자기 정수 키만 해당 — IntId@Tab 참조 형태는 RecordId@Tab과 동일하게 TargetName + ReferenceScanner.GetReferencedTab을 통해 읽힌다) · TypeToken InnerToken / IsWrapper(래퍼 타입 — 재귀적 내부) · IsCustomReference(이 컬럼이 파서가 IReferencingCellType을 구현하는 MyType@Tab임 — ReferenceScanner.GetReferencedTab이 이를 읽는 단일 판정자이며, 이것이 시그니처 변경 없이 모든 소비자가 함께 켜진 방법이다) · AssetTypeName(작성된 그대로의 AssetRef@Group<Type>의 <Type>, 제한이 없으면 null; Core는 이름만 저장한다 — 이를 해석하는 것은 IAssetTypeResolver의 몫이다 — 그리고 이는 리스트나 래퍼 안의 AssetRef 토큰에도 함께 찍힌다). 생성자의 후행 매개변수 세 개(innerToken, isCustomReference, assetTypeName)는 기본값을 가지므로 기존 호출은 그대로 컴파일되며, 이전의 8개·10개 인자 생성자는 오버로드로 남아 있어 이미 컴파일된 플러그인 어셈블리도 재빌드 없이 계속 동작한다 |
CellValue(구조체) | 타입이 지정된 셀 값 하나. null 없음(IsDefaulted가 구체화된 기본값을 표시). object Value · IsDefaulted · AsList · 정적 Of / Defaulted |
RecordId(구조체) | 키 값(Ordinal 동등성). string Value · IsEmpty |
RecordRefValue(구조체) | RecordId@Tab 셀의 값. TargetTab · Id |
IntRefValue(구조체) | IntId@Tab 셀의 값 — RecordRefValue의 정수 키 쌍둥이. string TargetTab · int Id · bool IsEmpty · 정적 Empty(tab)(아무것도 가리키지 않는 선택적 IntId@Tab?) |
LocRefValue(구조체) | LocRef@Tab 셀의 값 — RecordRefValue의 현지화 쌍둥이이며, 값만 보고도 소비자가 이것이 문자열 테이블을 가리킨다는 것을 알 수 있도록 별도 타입으로 유지된다. string TargetTab · string Key · bool IsEmpty · 정적 Empty(tab) · ReferencedKeys. IRefBearingValue를 구현하므로, 참조 스캐너는 이를 코어 참조와 정확히 똑같이 다룬다 |
AssetRefValue(구조체) | AssetRef@Group 셀의 값. Group · Key(서브 에셋 키는 parent[sub]이다) |
EnumValue(구조체) | Enum<T> 셀의 값(문자열 쌍 — CLR 변환은 베이크의 몫). EnumName · MemberName |
타입 지정 에셋 참조 (SheetForge.Core.Model)
AssetRef@Group<Type>의 <Type>은 호스트가 해석한다 — Core는 엔진도, 프로젝트의 어셈블리도 모른다 — 그리고 Core는 그 결과만 판정한다. 여기 있는 모든 것은 순수 데이터다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
IAssetTypeResolver | 인터페이스 | AssetTypeResolution Resolve(string rawName) — 이름 하나가 들어가면 판정 하나가 나온다; 같은 이름은 항상 같은 답을 받는다(구현체가 캐시할 수 있다). AssetKeyIndex와는 별도로 ImportPipeline에 주입되므로, 아직 Addressables 설정이 없는 프로젝트에서도 타입 이름이 해석된다; 리졸버가 주입되지 않으면(헤드리스, 브라우저) 타입 이름 진단은 그냥 생성되지 않는다. Editor의 구현체는 프로젝트가 로드한 UnityEngine.Object 파생 에셋 타입을 대상으로 해석한다(허용 목록 없음; 컴포넌트와 에디터 전용 타입은 제외) |
AssetTypeResolution | sealed 클래스 | 이름 하나에 대한 판정 — RawName · AssetTypeResolutionStatus Status · FullName(CLR 전체 이름, 중첩 타입은 +로; Resolved와 NotReferenceable일 때만) · AssemblyName(생성된 컴패니언 어셈블리가 참조해야 하는 어셈블리 — 어셈블리 정의 타입에는 설정되고, 엔진 모듈과 해석되지 않은 이름에는 null) · Candidates(결코 null이 아님: 모호한 후보들, 또는 알 수 없는 이름에 대한 최근접 일치 제안). 팩토리 Resolved(raw, fullName, assemblyName) · Unknown(raw, suggestions) · Ambiguous(raw, candidates) · NotReferenceable(raw, fullName, assemblyName) |
AssetTypeResolutionStatus | enum | Resolved · Unknown(그런 타입 없음) · Ambiguous(짧은 이름이 여러 타입과 일치함 — 전체 이름을 적을 것) · NotReferenceable(그 타입이 Assembly-CSharp 같은 미리 정의된 어셈블리에 있어, 생성된 코드가 참조할 수 없음) |
코드젠은 파이프라인이 만든 해석 딕셔너리를 읽어, 해석된 이름에는 AssetReferenceT<global::FullName>을 내보낸다; 그 딕셔너리에서 찾을 수 없는 이름은 결코 그대로 내보내지지 않는다 — 필드는 AssetReference로 폴백하고 AssetTypeUnresolvedFallback 경고가 수집된다. 해석된 전체 이름은 스키마 핑거프린트에도 섞여 들어간다.
시각적 값 타입 (SheetForge.Core.Model)
세 가지 내장 시각적 타입을 위한, 엔진에 의존하지 않는 값 모델이다. 각각은 불변이고 IEquatable이며, 자신만의 텍스트 형태(TryParse / Render)를 스스로 소유한다 — 시트 문법 페이지가 문서화하는 것과 같은 표기법이다 — 그래서 색상, 커브, 그라디언트를 저장하는 플러그인 타입은 두 번째 표기법을 만드는 대신 이들을 재사용할 수 있다. Editor는 이들을 UnityEngine.Color / AnimationCurve / Gradient로 베이크하고 다시 읽어들인다; 브라우저는 수학을 다시 구현하는 대신 아래의 평가기(evaluator)를 통해 이들을 샘플링한다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
ColorValue | readonly 구조체 | 네 바이트 R · G · B · A · 정적 Default(#00000000) · 정적 TryParse(text, out value, out error)(#RGB / #RGBA / #RRGGBB / #RRGGBBAA를 받아들임) · Render()(불투명하면 대문자 여섯 자리) |
CurveValue | sealed 클래스 | Keys(시간 오름차순) · PreWrap / PostWrap · 정적 Empty(키 없음 — 텍스트 형태가 없는 유일한 상태; Render()는 ""를 반환한다) · 정적 Create(keys, preWrap, postWrap) — 유일한 생성 경로: 시간순으로 정렬하고, 중복된 시간을 거부하며, CurveTangentSolver를 적용해 커브가 존재하는 순간부터 "모드가 이긴다"가 성립하게 한다 · 정적 TryParse(2/4/7/8필드 키, Once는 ClampForever의 별칭으로 받아들여짐, Infinity/-Infinity 탄젠트) · Render()(8필드 키, 필요할 때만 래핑 접미사) |
CurveKey | readonly 구조체 | Time · Value · InTangent · OutTangent · InWeight · OutWeight · CurveWeightedMode WeightedMode · CurveTangentMode LeftMode / RightMode · bool Broken; 정규화를 전혀 하지 않는 열 개 인자 생성자 |
CurveWrap | enum | ClampForever · Loop · PingPong · Default — 이름으로 나타낸 Unity의 래핑 어휘(WrapMode로의 값 매핑은 베이커의 몫이다) |
CurveTangentMode | enum | Free = 0 · Auto = 1 · Linear = 2 · Constant = 3 · ClampedAuto = 4 — AnimationUtility.TangentMode와 이름·값이 동일하므로, 베이커는 이름으로 매핑하며 Unity의 패킹된 탄젠트 비트를 결코 건드리지 않는다 |
CurveWeightedMode | [Flags] enum | None = 0 · In = 1 · Out = 2 · Both = 3 — 키의 어느 쪽이 가중(베지어) 탄젠트를 쓰는지 |
CurveTangentSolver | 정적 클래스 | CurveKey[] Apply(IReadOnlyList<CurveKey> sortedKeys) — 모드가 지시하는 탄젠트 숫자를 도출하며, 엔진의 순서대로 단계를 적용한다(자신의 쪽에 Linear → 양쪽에 ClampedAuto → 양쪽에 Auto → 자신의 쪽에 Constant), Free인 쪽과 가중치는 건드리지 않는다. CurveValue.Create가 이를 호출하므로, 호출자가 직접 부를 일은 드물다 |
CurveEvaluator | 정적 클래스 | float Evaluate(CurveValue, float time) · float[] Sample(CurveValue, int count)(count ≥ 2, 첫 키에서 마지막 키까지 균등한 간격) — 키 사이는 Hermite, 가중치 플래그가 설정된 쪽은 가중 베지어, 탄젠트가 무한이면 고정(hold), 그리고 키 범위 밖에서는 네 가지 래핑 동작; AnimationCurve.Evaluate를 기준으로 무작위 커브에 대해 검증됨 |
GradientValue | sealed 클래스 | ColorKeys · AlphaKeys(각각 1~8개, 시간 오름차순) · GradientBlend Mode · GradientColorSpace ColorSpace · 정적 Default(흰색, 완전 불투명, Blend) · 정적 Create(colorKeys, alphaKeys, mode, colorSpace)(개수와 0…1 범위를 검증하고, Unity와 마찬가지로 시간을 16비트로 양자화하며, 안정적으로 정렬한다) · 정적 TryParse(세 개 또는 네 개의 ` |
GradientColorKey | readonly 구조체 | ColorValue Color(알파는 무시됨 — 알파는 자신만의 키를 갖는다) · float Time |
GradientAlphaKey | readonly 구조체 | float Alpha · float Time |
GradientBlend | enum | Blend · Fixed · PerceptualBlend |
GradientColorSpace | enum | Uninitialized(기록되지 않음; Gamma로 읽힌다) · Gamma · Linear — PerceptualBlend에만 영향을 준다 |
GradientEvaluator | 정적 클래스 | ColorValue Evaluate(GradientValue, float time) · ColorValue[] Sample(GradientValue, int count) — 선형, 계단형, 또는 지각적(Oklab) 블렌딩이며 알파 키는 따로 블렌딩되어 바이트로 반올림된다; Gradient.Evaluate를 기준으로 무작위 그라디언트에 대해 검증됨 |
에러와 결과 (SheetForge.Core.Model / .Reporting)
| 타입 | 역할 및 주요 멤버 |
|---|---|
ImportError | 구조화된, 로케일 중립적인 에러. Code · Severity · Coordinate · ActualValue · Expected · Suggestion |
ImportErrorCode(enum, 105개) | 완전한 "왜" 카탈로그 — 이를 구성하는 계열은 표 아래에 있다. 추가 전용이다. 렌더러 테이블은 멤버 값에 따라 동작하기 때문이다 |
ImportSeverity(enum) | Error(출력을 막음) · Warning |
CellCoordinate(구조체) | 탭 · 1부터 시작하는 행 · 1부터 시작하는 컬럼 · 필드; 스프레드시트 컬럼 문자를 계산한다. ForTab / ForRow 팩토리 |
ErrorCollector | 모든 것을 수집하는 싱크. All · HasErrors · ErrorCount · Add |
ImportResult | 파이프라인 출력. 불변식: Success == false ⇔ Registry == null. Success · Registry · Diagnostics · SkippedTabs · EnumTabs(enum 정의 시트로 읽혀 결코 데이터 테이블로 파싱되지 않는 탭 — "아직 테이블이 기록되지 않음"을 뜻하는 SkippedTabs와는 별개로 유지되어, 리포트의 건너뜀 개수가 정확하게 남는다. 둘 다 그 탭들의 생성 코드·베이크된 에셋·주소를 보존하는 집합이다) · 정적 Succeeded / Failed |
ImportReport(.Reporting, 어셈블리 SheetForge.Core.Tooling) | 리포트 렌더러의 입력 — Timestamp · SourceDescription · TabCount · RecordCount · Success · Diagnostics · ErrorCount · WarningCount · SkippedTabCount(TabCount 중 몇 개가 임포트되는 대신 건너뛰어진 빈 시트였는지 — 헤더가 이를 출력하므로 탭 개수를 "모두 임포트됨"으로 착각하지 않는다) |
ImportReportText(.Reporting, 어셈블리 SheetForge.Core.Tooling, 정적) | 리포트를 제품 자신의 사람이 읽기 쉬운 문자열로 렌더링하며, 콘솔에는 아무것도 기록하지 않고 점프 링크나 머신 좌표 줄도 붙이지 않는다(그것들은 콘솔 자신의 관례에 속한다). string Render(ImportReport report, IReadOnlyDictionary<string,string> languageTable = null, string operationName = null) — 영어에는 테이블을 생략한다. 작업 이름은 생략되면 동일한 테이블에서 읽히므로 문장이 결코 두 언어를 섞지 않는다. Editor 측 호출자는 보통 현재 에디터 언어를 채워 넣는 SheetForgeActions.RenderReportText(report)를 원한다(순수 어셈블리는 EditorPrefs를 읽을 수 없다) |
ImportErrorCode — 이를 구성하는 계열:
- 마커, 스키마, 타입, 셀, 키/참조, 에셋 키;
- 소스/파일, csv/xlsx, 코드젠 식별자, addressables, baseline/export, Google/인증/Push, 템플릿;
- 플러그인 —
PluginRegistrationConflict, 그리고 어셈블리의 호환성 선언이 이 호스트가 읽는 범위를 벗어날 때의PluginIncompatible; - IntId —
DuplicateIntId, 그리고IntId@Tab참조에 대해서는UnresolvedIntId·TargetTabHasNoIntId; @overlap과DomainRuleViolation;- enum 정의 시트 —
EnumSheetMarkerConflict·DuplicateEnumName·EnumSheetEmptyColumn·InvalidEnumIdentifier·InvalidEnumUnderlyingType·InvalidEnumMemberValue; - 에러가 아니라 경고인
DropdownNotSupportedByFormat; - 타입 지정 에셋 참조 —
UnknownAssetType·AmbiguousAssetType·AssetTypeNotReferenceable(컬럼당 한 번,@type행에서), 셀마다AssetTypeMismatch, 그리고 코드젠 경고AssetTypeUnresolvedFallback.
인덱스와 유틸리티 (SheetForge.Core.Validation / .Model / .Parsing / .Unparse)
| 타입 | 역할 및 주요 멤버 |
|---|---|
TabKeyIndex | 탭 하나의 키 정보 — 문자열 키 컬럼과 그 탭의 IntId 정수 키 집합을 함께 담으므로, RecordId@Tab과 IntId@Tab 참조 둘 다 이를 대상으로 해석된다. TabName · KeyField · HasKeyColumn · Keys · Contains(id) |
KeyIndexBuilder(정적) | 키 인덱스(문자열 키와 IntId 정수 키 집합을 한 번에)를 구축하고, 키 에러를 보고하며, IntId 컬럼을 검증한다. Build(SheetTable, ErrorCollector) · ValidateIntIdColumns |
AssetKeyIndex | 그룹 → 유효 키 집합(Editor가 Addressables 카탈로그로부터 채우며, 서브 에셋 키도 포함된다. null 주입 = 에셋 검증 생략). Register(group, keys) · HasGroup · HasKey · KeysOf · GroupNames, 그리고 AssetRef@Group<Type>이 쓰는 타입 계층: RegisterTyped(group, key, satisfiedTypeFullNames)(그 키와 그것이 로드될 수 있는 전체 타입 이름의 클로저 — 자신의 타입, 베이스, 인터페이스, 자신의 서브 에셋들의 타입; 재등록은 그 클로저를 합집합한다) · HasTypeInfo(group, key) · SatisfiesType(group, key, typeFullName). 그냥 Register로 등록된 키는 클로저가 없으며, 타입 검사에서 실패하는 대신 면제된다 |
LocalizationCoverage(정적) | 현지화 시트의 로케일별 커버리지와 고아 키. 에러를 수집하는 대신 목록을 반환하는 순수 계산이다. 번역되지 않은 셀과 쓰이지 않는 키는 막아야 할 출구가 아니라 정상 상태이기 때문이다. IReadOnlyList<LocaleCoverage> Compute(SheetTable) · IReadOnlyList<string> FindOrphanKeys(locTabName, tables)(아무것도 가리키지 않는 키. 의도적으로 보수적이다 — 스캐너가 아는 모든 참조 형태를 사용으로 세므로, 살아 있는 번역이 고아로 불리는 일은 없다) |
LocaleCoverage(sealed 클래스) | 로케일 하나의 커버리지. LocaleColumn Locale · int TotalKeys · int TranslatedKeys · IReadOnlyList<string> MissingKeys(시트의 행 순서이며, 결코 null이 아니다) · bool IsComplete |
TextSuggestion(정적) | 최근접 일치 제안(범위 제한된 Levenshtein, 결정적). FindNearest · Distance · DistanceWithin |
BuiltinCellParsers(정적) | CreateDefaultRegistry() — 12개의 내장 파서(int, float, bool, string, Enum, RecordId, AssetRef, IntId, LocRef, Color, AnimationCurve, Gradient). |
CanonicalValueRenderer(정적) | 값 → 정규 셀 문자열(Export/Push). TryRender(…)(ColorValue / CurveValue / GradientValue는 자신의 Render()에 위임한다; 키가 없는 커브는 빈 셀로 렌더링된다) · RenderFloat(float)(가장 짧은 라운드트립) |
Push 계획 (SheetForge.Core.Unparse)
IPushApprover.Approve(PushPlan)가 이들을 노출하기 때문에 공개되어 있다. 순수 데이터다.
| 타입 | 역할 |
|---|---|
PushPlan(어셈블리 SheetForge.Core.Tooling, 아래 세 행도 마찬가지) | 전체 전송 계획. Tabs · HasWork |
PushTabPlan | 탭 하나: Writes · Appends · Deletes(키 + 행 번호; DeleteNotices는 키만 보는 뷰로 남는다) |
PlannedCellWrite | 셀 쓰기 하나 — 좌표, baseline 셀, 새 값/텍스트, 문자열 계열 플래그 |
PlannedRowAppend | 추가된 행 하나 — 전체 셀 텍스트 + 문자열 계열 컬럼 |
Editor 어셈블리 (SheetForge.Editor)
설정, 현지화, 구성 (SheetForge.Editor.Pipeline / .Localization)
| 타입 | 역할 및 주요 멤버 |
|---|---|
SheetForgeSettings(SO) | 설정 에셋. 필드: sourceProviderId(유일한 소스 선택 축; 비어 있으면 = 내장 LocalFile) · localFolderPath · bakeOutputFolder · generatedCodeFolder · generatedNamespace · exportFolderPath · exportFormat · spreadsheetId · googleAccessMode · serviceAccountKeyPath · gidMap(GidMapEntry { tabName, gid }의 목록). Effective* 해석된 속성. |
Loc(정적) | 현지화 진입점. Tr(key) · TrContent(…) · Table · MenuRoot 상수. Tr는 네 단계로 해석된다: 플러그인 등록 문자열(현재 언어, 그다음 영어 — 이 대체 로직은 오버레이가 소유한다, StringOverlayRegistry 참고) → 내장 테이블(현재 언어, 그다음 영어) → 키 자신. 플러그인 문자열의 등록 채널은 정확히 하나뿐이므로 "어느 등록이 이기는가"는 결코 질문거리가 되지 않는다 |
PluginRegistry(정적) | TypeCache로 플러그인을 발견한 다음, 그 후보들을 PluginComposition.Compose에 건넨다. Build · BuildValidators · BuildEdgeContributors · BuildStructuralMarkers · BuildTemplates · BuildGraphShapes · BuildCodeRegistries · BuildThemes · BuildAll(묶음) · InvalidateCache()(리로드 수명 캐시를 버린다 — SourceProviderRegistry.InvalidateCache와 동일한 관례이며, 호환성 게이트의 캐시도 함께 버려 바뀐 발견 집합이 다시 판정된다). 묶음과 슬롯 격리는 표 아래에 있다 |
ImportEvents(정적) | Editor 측 이벤트 버스 — 공개 계약: 외부 에셋이 구독할 수 있다. event Action<ImportCompletedArgs> ImportCompleted · RaiseImportCompleted(ImportCompletedArgs)는 임포트가 베이크까지 끝까지 실행됐을 때만 발생하므로, 구독자는 베이크된 에셋을 읽을 수 있다. event Action<BaselineUpdatedArgs> BaselineUpdated · RaiseBaselineUpdated(BaselineUpdatedArgs)는 시트 스냅샷이 저장될 때마다 — 검증에 실패한 실행을 포함해 — 발생하며, 이는 작성 표면이 격리된 임포트에서 자신을 새로고침하는 방법이다. 의도적으로 합쳐지지 않은 두 개의 축이다: 하나는 "시트가 움직였다"를, 다른 하나는 "에셋이 움직였다"를 뜻한다 |
BaselineUpdatedArgs(sealed) | baseline 저장 페이로드. IReadOnlyList<string> Tabs(스냅샷에 기록된 탭들) · bool Quarantined(방금 저장된 스냅샷이 검증에 실패했는지 여부) |
SheetForgeActions(정적) | 실행 파사드 — 메뉴 클릭이 실행하는 것과 동일한 사이클을 CI 스크립트, 빌드 훅, 또는 사용자 자신의 버튼에서 호출할 수 있다. RunImport() · RunExport() · RunPush() · RunHealthCheck() · RunLocalizationSync()(각각 위임한다. 설정 해석, Addressables 게이트, 상호 배제, 확인 모달, 진행 바, 코드젠→컴파일→베이크 재개는 모두 제품 안에 남는다) · bool IsBusy · bool TryBeginExclusiveScope(out IDisposable scope)(이미 무언가 실행 중이면 false + scope = null. scope가 해제하는 것이며, 두 번째 Dispose는 다른 누군가의 실행을 해제할 수 없다) · string RenderReportText(ImportReport)(현재 에디터 언어로 된 제품 자신의 문장, 콘솔 기록 없음). 완료 시맨틱스는 표 아래에 있다 |
SheetForgeEditorInfo(정적, 네임스페이스 SheetForge.Editor) | Editor 어셈블리 앵커 — const Version, Editor 측 표면을 대상으로 한 기능 게이팅을 위한 SheetForgeRuntimeInfo의 거울상 |
ImportCompletedArgs(sealed) | 구독자에게 전달되는 완료 페이로드. IReadOnlyList<string> Tabs(이번 완료로 베이크된 탭들) · string BakeFolder(Database SO 폴더). Args 객체 패턴 — 향후 필드가 추가되어도 이벤트 시그니처가 깨지지 않는다. |
GoogleSheetAccessMode(enum) | SheetsApi(인증, 쓰기 가능) · ExportUrl(인증 없음, 읽기 전용) |
ExportFormat(enum) | Tsv · Csv · Xlsx · Json · MatchSource |
PluginRegistry — 묶음과 슬롯 격리. 중첩된 PluginBundle은 조립된 PluginSet Set(열두 슬롯짜리 단일 진실 — 새로 자란 슬롯을 묶음을 넓히지 않고도 읽는 방법이다)을 노출하고, 그 위에 아홉 개의 편의용 창을 제공한다: Enums · Parsers · Validators · EdgeContributors · Markers · Templates · GraphShapes · CodeRegistries · Themes. 이전의 여섯 개·여덟 개 인자 생성자는 이후 레지스트리를 빈 값으로 기본 설정하는 오버로드로 남아 있으며, 그 계약들이 존재하기 전의 버전과 동일하게 동작한다.
격리는 이 타입이 아니라 Core의 것이다: 등록 중 예외를 던지는 플러그인은 이름과 함께 보고되고 건너뛰어지며, 다른 모든 슬롯과 플러그인은 계속 등록된다.
SheetForgeActions — 완료 시맨틱스. RunImport/RunPush는 던지고 잊는 방식이다 — 에디터 메인 스레드가 네트워크 IO에 블록될 수 없으므로 본문이 async void다. 그래서 반환은 완료를 의미하지 않는다: 이를 위해서는 ImportEvents.ImportCompleted를 구독하라. RunExport/RunHealthCheck/RunLocalizationSync는 동기적으로 완료된다 — RunLocalizationSync는 임포트 완료가 실행하는 시트 → StringTable 경로를 실행하며, Unity Localization 패키지가 없으면 설치 안내를 보여줄 뿐 아무것도 바꾸지 않는다.
소스 프로바이더 이음매 (SheetForge.Editor.Sources)
| 타입 | 역할 및 주요 멤버 |
|---|---|
ISheetSourceProvider | 프로바이더 계약. Id · DisplayNameKey · CreateTabSource(settings) · GetVisibility(settings) · CanAuthor(settings) · CreateReflectTarget(dispatcher, settings) |
ISourceReflectTarget | 쓰기 반영 대상. void Reflect() |
SourceVisibility | 어떤 설정 필드를 보여줄지 — bool 플래그 5개 |
SourceProviderRegistry(정적) | 발견/해석. All · ResolveActive(SheetForgeSettings)와 ResolveActive(string providerId)(설정 에셋 없이 id로부터 바로 해석) · TryGet · InvalidateCache |
ITabSource | fetch 추상화. Description · Task<TabSourceResult> FetchAsync() |
TabSourceResult | 탭(이름 → 원본 TSV) + 진단 정보 + 탭별 형식; 부분 출력 허용. 정적 Create |
TabSourceFormat(enum) | Tsv · Csv · Xlsx · GoogleSheet |
데이터 스튜디오 확장 지점 (SheetForge.Editor.Studio)
UIElements를 반환하거나 창 상태를 다루므로 Editor 측이다 — ISheetSourceProvider와 동일하게 정당화된 비대칭이다. 네 계약 모두 TypeCache로 발견되며(매개변수 없는 생성자, 등록 호출 없음), 모두 try/catch 안에서 호출된다. 창 자신(DataStudioWindow)은 internal이다.
데이터로 표현 가능한 것은 대신 Core의 ISheetForgeStudioPlugin 어휘에 속하며, 이는 브라우저에서도 렌더링된다. 이들은 서술이 말할 수 없는 것을 위한, 한계 없는 탈출구다.
마지막 네 항목은 계약이 아니라 마운트된 위젯이 사용할 수 있는 도구다:
- 창 자신의 읽기 전용 스킨 값 — 그래서 자신이 속한 것처럼 보일 수 있다;
- 키 드롭다운 — 그래서 셀 위젯이 내장 셀과 동일한 방식으로 키를 고른다;
- 그리고 발견 캐시 재설정 — 그래서 사용자 자신의 테스트가 프로브를 다시 발견할 수 있다.
| 타입 | 종류 | 역할 및 주요 멤버 |
|---|---|---|
IStudioGraphWidget | 인터페이스 | 그래프 캔버스 위의 도메인 스트립(Core는 하나도 제공하지 않는다). bool AppliesTo(StudioGraphContext) · VisualElement Create(StudioGraphContext)(그래프가 재구축될 때마다 다시 생성됨 — 상태를 갖지 마라. null은 아무것도 추가하지 않는다) |
StudioGraphContext | sealed 클래스 | 읽기 전용: Tab과 FocusRecordId(종착점) · SheetRecord FocusRecord(해석되지 않으면 null) · Tables · ReferenceIndex References · CodeRegistries. 시그니처 호환을 위해 남아 있는 폐지된 두 축은 **[Obsolete]**로 표시된다: ShapeId(항상 "record")와 ModeId(항상 빈 값). 둘 중 하나를 비교하는 코드는 컴파일되지만 결코 참이 되지 않으므로, 이제 컴파일러가 이를 알려준다 — 죽은 분기를 남기는 대신. 그 검사는 삭제하라. 스테이징 표면 없음 — 위젯은 표시만 한다(생성자는 internal: 창이 이를 조립한다) |
IStudioCellEditorProvider | 인터페이스 | 명명된 타입 하나에 대해 그리드 셀 하나를 그린다. string TypeName(CellParserRegistry 타입 또는 래퍼 이름과 일치, Ordinal; 비어 있으면 옵트아웃) · VisualElement CreateEditor(StudioCellEditorContext) — null을 반환하면 그 셀을 거부하고 내장 위젯이 대신 맡는다. 동일한 타입 이름에 대한 중복 주장은 경고하며 먼저 발견된 것을 유지한다 |
StudioCellEditorContext | sealed 클래스 | 셀 위젯이 받는 것: Tab · FieldName · TypeToken Type · CurrentRawText(스테이징이 적용된 정본 텍스트) · Action<string> Commit(일회성 동작 — 자신만의 undo 단계) · Action<string> CommitTyping(키 입력 연속 — 셀당 합쳐짐) · Func<string,IReadOnlyList<string>> ReferenceKeys(내장 피커가 제공하는 것과 동일한 후보 키). 두 커밋 모두 창의 스테이징 게이트를 통과한다(생성자는 internal: 창이 이를 조립한다) |
IStudioInspectorAction | 인터페이스 | 노드 인스펙터의 추가 버튼. string LabelKey(Loc 키; 등록되지 않으면 = 그대로 표시, 비어 있으면 = 타입 이름) · bool AppliesTo(StudioInspectorContext) · void Execute(StudioInspectorContext) |
StudioInspectorContext | sealed 클래스 | 읽기: Tab · RecordId · SheetRecord Record · Tables · References · CodeRegistries. 중재된 변경: Action<string,string,string,string> StageCell · Action<IReadOnlyList<EdgeCellWrite>> StageCells, 둘 다 표 아래에서 상세히 설명한다. 서비스: Action<string,int,string> FocusCell · Action RequestRebuild. AuthoringSession은 의도적으로 노출되지 않는다 |
IStudioPanelProvider | 인터페이스 | 스튜디오 오른쪽 패널의 임의의 UIToolkit 패널 — 서술형 StudioPanelDescriptor 옆의 탈출구다. string Id · string TitleKey · bool AppliesTo(StudioSurfaceContext) · VisualElement CreatePanel(StudioSurfaceContext)(null은 이번 틱에 아무것도 그리지 않음을 뜻한다). 서술형 패널을 **같은 Id**로 등록하면 각 호스트는 자신이 그릴 수 있는 것을 취한다: 에디터는 이를 선호하고 브라우저는 서술형을 그린다 — 그래서 "브라우저가 닿는 데까지는, 에디터에서는 끝까지"에 두 번째 계약이 필요 없다. 이 엘리먼트도 한 번의 재계산 틱만 살아 있으므로 상태를 갖지 않는다 |
StudioPalette | static 클래스 | 창 자신이 그릴 때 사용하는 읽기 전용 색상, 간격, 타입 값 — 그래서 사용자가 마운트하는 위젯이 헥스 코드를 하드코딩하는 대신 창과 어울린다. 각 슬롯은 읽는 시점에 해석되므로, 위젯은 밝기 모드와 컬러 프리셋을 공짜로 따른다. 값을 고르는 것(프리셋, 밝기, 기본값)은 internal로 남는다 — 위젯은 팔레트를 따를 뿐, 다시 칠하지 않는다. 멤버 목록은 표 아래에 있다 |
StudioTheme | static 클래스 | 멤버 네 개뿐: CategoryColor(category)(창이 그 카테고리에 부여하는 것과 동일한 결정적 톤) · Np(text)(리치 텍스트 라벨로의 안전한 보간) · Mono / ApplyMono(element)(모노 글꼴 정책: 키, 주소, 숫자에만 — 모노 글꼴에는 CJK 글리프가 없다). 이 타입의 나머지는 모두 internal이다 |
StudioKeyPicker | static 클래스 | 멤버 하나: Show(Rect screenAnchor, string targetTab, IReadOnlyList<string> candidates, Action<string> picked, string acceptsLabel = null) — 자신의 표기법 안에서 키에 접근해야 하는 셀 위젯을 위해, 내장 참조 셀이 여는 것과 동일한 드롭다운. 제공한 후보 중 하나의 키를 선택해 돌려준다. 레코드 생성, 셀 비워두기, 리스트 다중 토글, 어느 포트가 선택을 받을지 묻는 것은 내장 참조 셀 자신의 규칙이므로 이 파사드에는 없다. picked는 필수다(창이 만들어지기 전에 ArgumentNullException). 후보가 없고 제공할 것이 없으면 빈 목록을 여는 대신 로그를 남긴다. 창 타입 자체는 internal로 남는다 |
StudioPluginRegistry | static 클래스 | 공개 멤버 하나: InvalidateCache() — 리로드당 발견 캐시를 버려서, 사용자 자신의 테스트가 방금 활성화한 프로브가 다시 발견되게 한다(PluginRegistry와 SourceProviderRegistry가 이미 제공하던 것과 동일한 배려다 — 이것이 유일하게 빠져 있던 레지스트리였다). 발견된 목록은 internal로 남는다: 창이 마운트할 것을 외부의 그 무엇도 읽거나 대체할 수 없다 |
StudioInspectorContext — 두 스테이징 델리게이트:
- StageCell은 탭, recordId, 필드, 정본 원문 텍스트를 받는다. 창이 Undo 단계를 등록하고, 프로젝션 세대를 올리고, 논리적 주소를 스테이징한다.
- StageCells는 함께 바뀌어야 하는 여러 셀에 대해 동일한 일을 한다: 하나의 네이티브 Undo 단계, 전부 아니면 전무다. 하나라도 스테이징할 수 없으면 세션은 전혀 건드려지지 않는다.
어느 쪽이든 화면상으로는 조용한 실패이며, 오직 게이트만 스스로를 설명한다. 읽기 전용 소스, 이미 실행 중인 파이프라인, 또는 워크북 기반 탭은 콘솔에 그 사유를 기록한다. 빈 목록, 탭이나 필드가 없는 쓰기, 그리고 어떤 행으로도 해석되지 않는 레코드 키는 아무 일도 하지 않고 아무 말도 하지 않는다.
StudioPalette — 멤버:
- 33개의 색상 슬롯:
Canvas·Panel·Band·Chrome·Surface·Chip·Selection·PendingCell·Line·LineSoft·GridLine·LineHover·Text·TextMuted·TextFaint·RefText·OnAccent·Accent·AccentDim·Warning·Danger·Ok·SheetTone·CodeTone·EditedCell·NewRowCell·NewRowLine·DangerChip·DangerPanel·Scrim·Wire·WireDot·GridDot. IsDark.- 간격:
SectionSpace·RowSpace·RuleHeight·ButtonHeight·PrimaryButtonHeight·GlyphWidth. - 타입 크기:
HeadingFontSize·SectionFontSize·CaptionFontSize. FromRgb(uint)·ToHex(uint).
Push 승인 (SheetForge.Editor.Push)
| 타입 | 역할 |
|---|---|
IPushApprover | bool Approve(PushPlan, string humanSummary) · bool ApproveStructureRewrite(string, bool hasLiveConflicts) — 거부 = 전송되는 것 없음 |
AutoPushApprover | 항상 승인함(테스트/자동화용) |
작성 엔진 (SheetForge.Editor.Structure / .Pipeline / .Export)
| 타입 | 역할 및 주요 멤버 |
|---|---|
AuthoringSession | 스테이징 상태 소유자(직렬화 가능 — 공짜 Undo + 리로드 생존). Edits · IsolatedEdits · NewRows · StructOps · Reorders · TabRenames · EnumMembers(스테이징된 enum 시트 멤버 추가) · AssetRegistrations(스테이징된 Addressables 등록 — 프로젝트 수준이라 탭별 게이트에는 관여하지 않지만, 반영 진입·파기·diff 요약에는 포함된다) · HasAssetRegistrations · StageAssetRegistration(r)(같은 guid, 또는 그룹 생성이면 같은 그룹이 제자리에서 교체된다 — 마지막 의도가 이긴다; 식별자가 없는 등록은 거부된다) · RemoveAssetRegistrationsWhere(predicate) · SetStaged · ResolveBaselineEdits · RemapFieldName/RecordId/Tab · StageTabRename · EffectiveStructOps · PendingStructCount · TabNames · TryGetBaselineTable · LastProjectionResult · ClearAll(등록도 함께 지운다) |
AuthoringDispatcher | 반영 오케스트레이터. 생성자 (session, callbacks, baselines) · Reflect() · BuildProjectionResult()(부작용 없는 프로젝션 조회) · IReadOnlyDictionary<string,string> BuildProjectedTabs()(탭별 TSV로서 동일한 프로젝션 — 쓰기 반영 대상이 막 전송하려는 것, 기록하지 않고도 미리볼 수 있다) · void FinalizeReflectSuccess(IReadOnlyList<string> writtenTabs, IReadOnlyList<TabRenameEntry> committedRenames = null)(소스 자신의 쓰기 반영이 도달해야 하는 마무리: 기록한 탭에 대한 retain 정리, ClearUndo 경계, 자동 재임포트 — 내장 경로는 동일한 private 본문을 실행하므로, 외부 프로바이더도 정확히 동일하게 끝난다. 빈 목록은 스테이징을 그대로 유지하는 무동작이다) · Session · Callbacks · Baselines |
AuthoringDispatchCallbacks | 13개의 일반 뷰 관련 델리게이트 + IPushApprover — ResolveSettings · RenderReport(Action<ImportReport>, null 허용) · TriggerReimport · ConfirmKeyRenames · ConfirmTabRenames(null 허용) · ClearUndo · Rebuild · … 내장 Local/Google 대화상자 델리게이트는 선택적 BuiltInSourceDialogs 묶음에 있다 |
BuiltInSourceDialogs | 내장 Local/Google 소스 대화상자 델리게이트 14개로 이루어진 선택적 묶음, AuthoringDispatchCallbacks와는 별도다 — 외부 프로바이더는 이들이 전혀 필요 없다. NotifyLocalDone은 인자 다섯 개를 받는다; 마지막은 완료 대화상자를 위한 Addressables 등록 요약 줄이다(아무것도 스테이징되지 않았으면 null) |
BaselineStore(.Export) | 탭별 정규화된 TSV baseline 스냅샷 |
스테이징 값 타입 (SheetForge.Editor.Structure; StagedCellEdit/StagedNewRow는 SheetForge.Editor.Windows에 있음)
| 타입 | 역할 |
|---|---|
StagedCellEdit(구조체) | 스테이징된 편집 하나 — TabName · RowOrdinal · FieldName · RawText · RecordId(논리적 키) |
StagedNewRow | 스테이징된 신규 행 하나 — TabName · FieldNames · CellTexts |
StructureOp | 구조 작업 하나 — Kind · 좌표 · 텍스트 · Order 순열 |
StructureOpKind(enum) | AddColumn · RemoveColumn · AddMarker · RemoveMarker · RemoveDataRow · ReorderColumns · ReorderDataRows · RenameColumn · EditColumnType · EditColumnDesc · SetColumnOverlap · SetSheetStyle |
TabReorderEntry | 탭별 순서 변경 상태 — Tab · ColOrder · RowOrder |
TabRenameEntry(구조체) | OldName · NewName |
StagedEnumMember(구조체) | 스테이징된 "이 enum에 이 멤버를 추가" 하나 — TabName(어느 enum 시트인지; 비어 있으면 = 전부 탐색) · EnumName · Member. StructureOp가 아니라 세션 수준인 이유는 탭 이름 변경과 같다: enum 시트는 테이블도, 스키마도, 키 컬럼도 없으므로, 셀 편집의 (탭, 레코드, 필드) 주소는 "이 enum의 다음 멤버"를 명명할 수 없다. AuthoringSession.EnumMembers가 공개이기 때문에만 공개다(CS0050) |
StagedAssetRegistration(구조체) | AssetRef@Group 셀에 에셋을 드롭하거나 골라서 만들어지는, 프로젝트의 Addressables 설정에 대한 스테이징된 변경 하나 — StagedAssetRegistrationKind Kind · Guid(그 에셋; 서브 에셋은 자신의 부모를 스테이징한다) · Group · FromGroup(이동일 때만) · Address(신규 항목에는 확장자를 뺀 파일 이름; 이미 등록된 에셋은 자신의 주소를 유지한다) · AssetPath(표시용). 팩토리 Add(guid, group, address, assetPath) · Move(guid, fromGroup, group, address, assetPath) · CreateGroup(group). 시트 기록이 성공한 뒤 실행되고, 그런 다음 비워진다. AuthoringSession.AssetRegistrations가 공개이기 때문에만 공개다(CS0050), StagedEnumMember와 마찬가지로 |
StagedAssetRegistrationKind(enum) | Add · Move · CreateGroup |
TabBaselineAnchor(구조체) | TabName · Fingerprint · RecordCount |
IsolatedEdit | 재앵커링에 실패한 편집 — Edit · Reason |
IsolationReason(enum) | 외부 이름 변경 / 외부 삭제 / 키 충돌 |
작성 헬퍼 (SheetForge.Editor.Windows / .Structure)
| 타입 | 역할 |
|---|---|
KeyRenamePlanner(정적) | 키 이름 변경 + 탭 간 전파 계획. Plan(…) · 중첩 KeyRenamePlan · 형제 구조체 KeyRename |
RecordIdMinter(정적, 순수) | Id 제안. Suggest · DetectCommonPrefix · Uniquify · StagedNewRowKeys |
IntIdMinter(정적, 순수) | 신규 레코드를 위한 다음 IntId 제안 — Suggest(existingIds) → max + 1. RecordIdMinter와는 별개의 축이며, 삭제된 빈틈을 결코 재사용하지 않는다 |
ProjectionErrorMapper(정적, 순수) | 에러 좌표 → 논리적 주소. TryMap(…) · 중첩 LogicalAddress |
EphemeralSoApply(정적) | 스테이징된 값의 SO 오버레이(임시). Apply(…) · InvalidateIndex(…) · 중첩 Report / SkipReason / SkippedEdit |
Runtime 어셈블리 (SheetForge.Runtime)
autoReferenced — asmdef 참조 없이 게임 코드에서 사용 가능하다.
| 타입 | 역할 및 주요 멤버 |
|---|---|
SheetForgeDatabases(정적) | 런타임 로더 — 공인된 로드 경로. const AddressPrefix = "SheetForge/" · AddressFor(tab) · LoadAsync(tab) · LoadAsync<TDatabase>(tab) · Release(handle) / Release<TDatabase>(db). 주소 헬퍼는 평범한 문자열이며 항상 컴파일된다. LoadAsync와 Release는 오직 SHEETFORGE_ADDRESSABLES 아래에서만 존재한다 — com.unity.addressables가 설치되었을 때 설정되는 버전 정의로, 이것이 패키지 없이도 제품이 컴파일되게 하는 것이다 |
DefinitionDatabase(추상 SO) | 생성되는 모든 탭별 Database의 기반. abstract TabName · abstract Count · virtual IReadOnlyList<object> RecordsUntyped · virtual InvalidateIndex(). RecordsUntyped는 생성된 타입을 모른 채로 베이크된 탭을 열거하는 공인된 방법이다 — 모든 탭을 순회하는 두 번째 베이커나 인스펙터는 예전에는 private records 필드를 리플렉션해야 했는데, 이는 필드 이름을 선언되지 않은 계약으로 만들어 코드젠이 그 이름을 바꾸는 날 조용히 깨지게 했다. 이 목록은 읽기 전용으로 취급하라(시트가 정본이다). 기본값은 비어 있음이므로, 이 멤버가 존재하기 전의 생성 코드도 여전히 컴파일되고 실행된다 — 재임포트가 그 오버라이드를 내보낸다 |
RecordRef(구조체) | 베이크된 SO 내부의 직렬화된 참조 값(문자열 id, 조회 시 해석됨). Id · IsEmpty |
IntRef(구조체) | 베이크된 SO 내부의 직렬화된 정수 키 참조 값 — IntId@Tab 필드를 위한 RecordRef의 쌍둥이다. 0도 유효한 id이므로 hasValue 비트가 IsEmpty를 뒷받침한다. Id · IsEmpty. 코드젠은 IntId@Tab 필드를 IntRef로 내보내며, 생성된 Database의 TryGet(IntRef)가 이를 소비한다 |
LocRef(구조체) | 베이크된 SO 내부의 직렬화된 현지화 참조 — LocRef@Tab 셀이다. Table(현지화 탭이며, 이것이 곧 StringTable 컬렉션 이름이다) · Key · long KeyId(0은 "아직 해석되지 않음"을 뜻한다: 임포트는 0을 베이크하고 브리지가 테이블 동기화 후에 실제 id를 채워 넣으므로, 키 이름이 바뀌어도 참조는 살아남는다) · IsEmpty. 이것은 언제나 컴파일된다 — 생성된 코드와 베이크된 에셋은 현지화 패키지의 타입을 결코 담지 않으며, 그것이 패키지를 선택 사항으로 유지하는 것이다 |
LocRefExtensions(정적) | 멤버 하나: LocalizedString ToLocalizedString(this LocRef) — KeyId가 0이 아니면 그것으로, 아니면 키 이름으로 가리키며, 비어 있는 참조는 비어 있는 LocalizedString으로 변환된다. 이것은 com.unity.localization이 설치되어 있을 때만 존재하며, 버전 정의 SHEETFORGE_LOCALIZATION 아래에 있다 — Addressables 레이어에 SHEETFORGE_ADDRESSABLES가 쓰는 것과 같은 방식이다 |
SheetForgeRuntimeInfo(정적) | const Version |
생성된 타입 (패턴 — 프로젝트별, 출시되는 API 아님)
탭 Foo마다, 코드젠은 사용자의 generatedNamespace에 다음을 생성한다:
public sealed partial class FooDefinition // one strongly-typed field per column; @desc → doc/tooltip
public sealed partial class FooDatabase : DefinitionDatabase
{
// TabName, Count, SchemaFingerprint, Records, RecordsUntyped override,
// lazy _byId/_byIntId lookups, InvalidateIndex override
}SheetForgeDatabases.LoadAsync<FooDatabase>("Foo")로 로드한다.
두 클래스 모두 partial로 생성되므로, 파생 멤버(계산된 프로퍼티, 인터페이스 구현, 연산자)를 생성된 파일 옆의 자신만의 파일에 추가할 수 있으며, 재임포트가 이를 덮어쓰지 않는다.
한 가지 경계: 사용자의 부분에는 직렬화된 필드를 추가하지 마라. 베이크된 ScriptableObject는 매 임포트마다 시트로부터 재구축되므로, 사용자의 부분만 직렬화하는 것은 기본값으로 돌아온다 — 값이 데이터에 속한다면, 그것은 컬럼에 속한다.
(partial 키워드는 스키마만으로 계산되는 SchemaFingerprint를 건드리지 않으므로, 클래스를 partial로 만든 것이 기존 베이크를 단 하나도 무효화하지 않았다.)
그 밖의 어셈블리
-
SheetForge.Setup— 의존성 없는 Addressables 부재 부트스트랩. 공개 API 없음(모두 internal, 안내 창을 보여주기 위해서만 존재). -
SheetForge.PluginDemo(병합된 asmdef 하나 + Demo.Editor asmdef 하나; 콘텐츠 네임스페이스는SheetForge.Skills로 유지) — 참조 샘플 패키지이며 제품 API가 아니다. 다음을 담고 있다:SkillsPlugin(일곱 개의 플러그인 인터페이스 — 기본, 검증기, 엣지, 템플릿, 그래프, 코드 레지스트리, 테마);Modifier+ModifierCellParser(커스텀 셀 타입),ModifierStatEdgeContributor(엣지 기여자);ExamplePipelineAugmenter/ExampleReactiveAugmenter(캔버스 오버라이드),ExampleCodeAtoms(_Refs코드 레지스트리);ExampleStudioUi(선언적 액션, 패널, 컬럼 배지, 셀 에디터 힌트),ExampleImportObserver(파이프라인 관찰자);ExampleStageStripWidget/ExampleInspectorAction/ExampleStudioPanel(공개 팔레트로 그려지는 데이터 스튜디오 Editor 확장 지점),ExampleLocStrings(그 라벨들을 두 언어로 등록 — 메인 어셈블리에 있어, 브라우저도 이를 보여준다);- 어셈블리 수준
SheetForgePluginCompat선언; SkillRunner(런타임 소비), 기본SheetForge.Generated네임스페이스에 생성된Example*타입(격리는 별도의 네임스페이스가 아니라Example*접두사로 이루어진다).
플러그인이 없는
SheetForge.CoreDemo샘플은 asmdef가 전혀 없이(Assembly-CSharp으로 컴파일되어) 제공된다.