@vgai/editor-sdk 0.5.14 → 0.5.16
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.
- package/package.json +2 -2
- package/src/client.ts +211 -25
- package/src/contributions.ts +7 -0
- package/src/editor-view.ts +0 -1
- package/src/index.ts +7 -1
- package/src/types.ts +31 -16
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/editor-sdk",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.16",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@types/three": "^0.180.0",
|
|
26
|
-
"@vgai/sdk": "0.5.
|
|
26
|
+
"@vgai/sdk": "0.5.16"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@vgai/engine": "*",
|
package/src/client.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
ProjectTemplate,
|
|
27
27
|
ProjectToolCatalog,
|
|
28
28
|
ProjectToolOutcome,
|
|
29
|
+
RagdollGenerationResult,
|
|
29
30
|
RecentProject,
|
|
30
31
|
ShadingMode,
|
|
31
32
|
StoryCaptureOptions,
|
|
@@ -103,6 +104,23 @@ export class EditorCommandError extends Error {
|
|
|
103
104
|
*/
|
|
104
105
|
const COMMAND_DEADLINE_MS = 150_000;
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Ceiling on {@link EditorClient.getUnresolvedConsole}. The CLI drains this
|
|
109
|
+
* on every verb, including ones that never wait for a command envelope, so
|
|
110
|
+
* a silent hang here would become a silent hang on `vgai examples`. The
|
|
111
|
+
* server route is a plain in-process GET; 1.5s is already longer than it
|
|
112
|
+
* should ever take.
|
|
113
|
+
*/
|
|
114
|
+
const CONSOLE_DRAIN_TIMEOUT_MS = 1_500;
|
|
115
|
+
|
|
116
|
+
/** Context accompanying one {@link EditorClient} response observation. */
|
|
117
|
+
export interface EditorEnvelopeObservation {
|
|
118
|
+
/** True only when this body came from the console-ledger endpoint and its
|
|
119
|
+
* `entries` field is therefore the complete named console set. Command
|
|
120
|
+
* payloads may also own an unrelated `entries` field. */
|
|
121
|
+
readonly unresolvedConsoleComplete: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
/**
|
|
107
125
|
* The GAME DEBUG PLANE, as a contribution's client sees it.
|
|
108
126
|
*
|
|
@@ -130,6 +148,27 @@ export interface HistoryStep {
|
|
|
130
148
|
readonly redoLabel: string | null;
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
/** What `open()` acknowledges: the workspace document the scene-table entry
|
|
152
|
+
* resolved to, and the title its tab now carries — the game's own word for
|
|
153
|
+
* that composition, not a filename. */
|
|
154
|
+
export interface OpenedDocument {
|
|
155
|
+
readonly documentId: string;
|
|
156
|
+
readonly title: string;
|
|
157
|
+
/**
|
|
158
|
+
* The GAME's own answer, present only when opening navigated a running game
|
|
159
|
+
* (a scene the adapter declares reachable through the game's scenes
|
|
160
|
+
* contract, or a native swap-slot remount): what was asked for, and which
|
|
161
|
+
* scene the game reports it is in once its own navigation settled. `current`
|
|
162
|
+
* can differ from `requested` — that is the game's reading, not a host claim.
|
|
163
|
+
*/
|
|
164
|
+
readonly scene?: { readonly requested: string; readonly current: string | null };
|
|
165
|
+
/**
|
|
166
|
+
* Present when opening restarted play at a native swap-slot key rather than
|
|
167
|
+
* navigating a live contract — the slot is a module-level const.
|
|
168
|
+
*/
|
|
169
|
+
readonly restart?: true;
|
|
170
|
+
}
|
|
171
|
+
|
|
133
172
|
export interface GameDebugDoor {
|
|
134
173
|
/**
|
|
135
174
|
* Read ONE registered state provider by name (`'bot.tester'`). `undefined`
|
|
@@ -165,9 +204,14 @@ export class EditorClient {
|
|
|
165
204
|
* The observer must not throw; anything it raises is swallowed, because a
|
|
166
205
|
* reporting hook may never break the command it is reporting on.
|
|
167
206
|
*/
|
|
168
|
-
private readonly onEnvelope:
|
|
169
|
-
|
|
170
|
-
|
|
207
|
+
private readonly onEnvelope:
|
|
208
|
+
| ((body: unknown, observation: EditorEnvelopeObservation) => void)
|
|
209
|
+
| null;
|
|
210
|
+
|
|
211
|
+
constructor(opts?: {
|
|
212
|
+
url?: string;
|
|
213
|
+
onEnvelope?: (body: unknown, observation: EditorEnvelopeObservation) => void;
|
|
214
|
+
}) {
|
|
171
215
|
if (opts !== undefined && (typeof opts !== 'object' || opts === null || Array.isArray(opts))) {
|
|
172
216
|
throw new TypeError(
|
|
173
217
|
'EditorClient options must be an object. Use new EditorClient({ url: "http://127.0.0.1:20173" }), not new EditorClient("...").',
|
|
@@ -223,8 +267,7 @@ export class EditorClient {
|
|
|
223
267
|
true,
|
|
224
268
|
);
|
|
225
269
|
}
|
|
226
|
-
const data = (await
|
|
227
|
-
this.observe(data);
|
|
270
|
+
const data = (await this.readJson(res)) as { ok: boolean; error?: string; code?: string } & T;
|
|
228
271
|
if (!data.ok) {
|
|
229
272
|
throw new EditorCommandError(
|
|
230
273
|
data.error ?? `Editor command failed: ${res.status}`,
|
|
@@ -543,6 +586,11 @@ export class EditorClient {
|
|
|
543
586
|
await this.command({ type: 'open-asset-tab', path, kind });
|
|
544
587
|
}
|
|
545
588
|
|
|
589
|
+
/** Generate a project-owned native Rapier ragdoll from a rigged model asset. */
|
|
590
|
+
async generateRagdoll(assetPath: string): Promise<RagdollGenerationResult> {
|
|
591
|
+
return this.command<RagdollGenerationResult>({ type: 'generate-ragdoll', assetPath });
|
|
592
|
+
}
|
|
593
|
+
|
|
546
594
|
async closeAsset(key: string): Promise<void> {
|
|
547
595
|
await this.command({ type: 'close-asset-tab', key });
|
|
548
596
|
}
|
|
@@ -586,6 +634,15 @@ export class EditorClient {
|
|
|
586
634
|
return data.subject;
|
|
587
635
|
}
|
|
588
636
|
|
|
637
|
+
/** Run one verb exposed by the active Inspector subject, by its id. */
|
|
638
|
+
async runInspectionAction(actionId: string): Promise<InspectedInspection> {
|
|
639
|
+
const data = await this.command<{ subject: InspectedInspection }>({
|
|
640
|
+
type: 'run-inspection-action',
|
|
641
|
+
actionId,
|
|
642
|
+
});
|
|
643
|
+
return data.subject;
|
|
644
|
+
}
|
|
645
|
+
|
|
589
646
|
/**
|
|
590
647
|
* The HIERARCHY PANEL's actual rendered row tree, as data.
|
|
591
648
|
*
|
|
@@ -630,6 +687,57 @@ export class EditorClient {
|
|
|
630
687
|
return { subject: data.subject, write: data.write };
|
|
631
688
|
}
|
|
632
689
|
|
|
690
|
+
/**
|
|
691
|
+
* REMOVE one editable path's authored override — the other half of the write
|
|
692
|
+
* door, and the only one that can express byte-ABSENCE.
|
|
693
|
+
*
|
|
694
|
+
* {@link setInspectionField} writes a VALUE, so reverting a property an
|
|
695
|
+
* authoring gesture ADDED puts the default back EXPLICITLY and leaves the
|
|
696
|
+
* source one attribute heavier than it started. This drops the property, so
|
|
697
|
+
* whatever governs it in its absence takes over — the same `io.remove` the
|
|
698
|
+
* Inspector's revert arrow calls, the same persistence pipe, the same awaited
|
|
699
|
+
* `{ destination, persisted }` ack.
|
|
700
|
+
*
|
|
701
|
+
* Rejects with `code: 'REMOVAL_UNAVAILABLE'` when the field does not declare
|
|
702
|
+
* itself removable or the lane implements no removal door. That refusal is a
|
|
703
|
+
* MISSING SEAM, not a failed removal, and it is coded rather than phrased
|
|
704
|
+
* precisely so a caller can grade the two differently.
|
|
705
|
+
*/
|
|
706
|
+
async removeInspectionField(path: string): Promise<InspectedFieldWrite> {
|
|
707
|
+
const data = await this.command<InspectedFieldWrite>({
|
|
708
|
+
type: 'remove-inspection-field',
|
|
709
|
+
path,
|
|
710
|
+
});
|
|
711
|
+
return { subject: data.subject, write: data.write };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* OPEN one piece of the adapter's SCENE TABLE by id — a scene, a prefab, or
|
|
716
|
+
* a story state, because the table makes them siblings (they differ only in
|
|
717
|
+
* instance site). The ids are exactly what `getState().adapter.scenes.entries`
|
|
718
|
+
* reports, so the table is both the menu and the address space.
|
|
719
|
+
*
|
|
720
|
+
* With a game LIVE in the session, opening a scene the adapter declares
|
|
721
|
+
* reachable through that game's own scenes contract NAVIGATES it — the same
|
|
722
|
+
* switch the editor's own scene picker makes — and the answer carries the
|
|
723
|
+
* game's own reading (`scene`).
|
|
724
|
+
*
|
|
725
|
+
* Rejects with a coded reason rather than prose: `SCENE_NOT_FOUND` (and it
|
|
726
|
+
* names the ids that DO exist), `SCENE_NOT_OPENABLE` carrying the adapter's
|
|
727
|
+
* own declared reason for a scene it says nothing can reach,
|
|
728
|
+
* `SCENE_NAVIGATION_NOT_RUNNING` for a live-only scene with no game running,
|
|
729
|
+
* `SCENE_CONTRACT_UNAVAILABLE` / `SCENE_NOT_IN_CONTRACT` (naming the ids the
|
|
730
|
+
* game itself publishes) / `SCENE_SWITCH_FAILED` when the running game's own
|
|
731
|
+
* navigation cannot take it, `SCENE_NOT_OPENABLE_LIVE` when this session has
|
|
732
|
+
* no remount for a native swap-slot scene,
|
|
733
|
+
* `SCENE_TABLE_UNAVAILABLE` before the adapter has loaded, and
|
|
734
|
+
* `SCENE_DOCUMENT_NOT_MOUNTED` when the host has no document for a piece the
|
|
735
|
+
* table says is openable — a host gap, not a table statement.
|
|
736
|
+
*/
|
|
737
|
+
async open(id: string): Promise<OpenedDocument> {
|
|
738
|
+
return this.command<OpenedDocument>({ type: 'open', id });
|
|
739
|
+
}
|
|
740
|
+
|
|
633
741
|
/**
|
|
634
742
|
* Undo / redo one project transaction — the same queue the keyboard shortcut
|
|
635
743
|
* drives. `moved` is false when there was nothing left in that direction.
|
|
@@ -728,12 +836,16 @@ export class EditorClient {
|
|
|
728
836
|
headers: { 'Content-Type': 'application/json' },
|
|
729
837
|
body: JSON.stringify({ name, location, template, ...(exampleId ? { exampleId } : {}) }),
|
|
730
838
|
});
|
|
839
|
+
const data = (await this.readJson(res)) as {
|
|
840
|
+
ok?: boolean;
|
|
841
|
+
error?: string;
|
|
842
|
+
path?: string;
|
|
843
|
+
config?: ProjectInfo['config'];
|
|
844
|
+
};
|
|
731
845
|
if (!res.ok) {
|
|
732
|
-
|
|
733
|
-
throw new Error(body.error ?? `Create project failed: ${res.status}`);
|
|
846
|
+
throw new Error(data.error ?? `Create project failed: ${res.status}`);
|
|
734
847
|
}
|
|
735
|
-
|
|
736
|
-
return { path: data.path, config: data.config };
|
|
848
|
+
return { path: data.path as string, config: data.config as ProjectInfo['config'] };
|
|
737
849
|
}
|
|
738
850
|
|
|
739
851
|
async openProject(path: string): Promise<void> {
|
|
@@ -742,23 +854,23 @@ export class EditorClient {
|
|
|
742
854
|
headers: { 'Content-Type': 'application/json' },
|
|
743
855
|
body: JSON.stringify({ path }),
|
|
744
856
|
});
|
|
857
|
+
const body = (await this.readJson(res)) as { error?: string };
|
|
745
858
|
if (!res.ok) {
|
|
746
|
-
const body = (await res.json()) as { error?: string };
|
|
747
859
|
throw new Error(body.error ?? `Open project failed: ${res.status}`);
|
|
748
860
|
}
|
|
749
861
|
}
|
|
750
862
|
|
|
751
863
|
async getProject(): Promise<ProjectInfo | null> {
|
|
752
864
|
const res = await fetch(`${this.baseUrl}/__editor/project`);
|
|
753
|
-
|
|
754
|
-
|
|
865
|
+
const data = (await this.readJson(res)) as { project: ProjectInfo | null; error?: string };
|
|
866
|
+
if (!res.ok) throw new Error(data.error ?? `Failed to get project: ${res.status}`);
|
|
755
867
|
return data.project;
|
|
756
868
|
}
|
|
757
869
|
|
|
758
870
|
async listRecentProjects(): Promise<RecentProject[]> {
|
|
759
871
|
const res = await fetch(`${this.baseUrl}/__editor/recent-projects`);
|
|
760
|
-
|
|
761
|
-
|
|
872
|
+
const data = (await this.readJson(res)) as { projects: RecentProject[]; error?: string };
|
|
873
|
+
if (!res.ok) throw new Error(data.error ?? `Failed to list projects: ${res.status}`);
|
|
762
874
|
return data.projects;
|
|
763
875
|
}
|
|
764
876
|
|
|
@@ -769,8 +881,9 @@ export class EditorClient {
|
|
|
769
881
|
* editor browser merely because they were listed. */
|
|
770
882
|
async listProjectTools(): Promise<ProjectToolCatalog> {
|
|
771
883
|
const res = await fetch(`${this.baseUrl}/__editor/project-tools`);
|
|
884
|
+
const body = await this.readJson(res);
|
|
772
885
|
if (!res.ok) throw new Error(`Failed to list project tools: ${res.status}`);
|
|
773
|
-
return
|
|
886
|
+
return body as ProjectToolCatalog;
|
|
774
887
|
}
|
|
775
888
|
|
|
776
889
|
/** Execute one Node-hosted project tool through the shared validated
|
|
@@ -793,7 +906,7 @@ export class EditorClient {
|
|
|
793
906
|
...(options.instance !== undefined ? { instance: options.instance } : {}),
|
|
794
907
|
}),
|
|
795
908
|
});
|
|
796
|
-
const body = (await
|
|
909
|
+
const body = (await this.readJson(res)) as ProjectToolOutcome;
|
|
797
910
|
if (!body || typeof body !== 'object' || typeof body.ok !== 'boolean') {
|
|
798
911
|
throw new Error(`Project tool returned an invalid response (${res.status}).`);
|
|
799
912
|
}
|
|
@@ -806,8 +919,9 @@ export class EditorClient {
|
|
|
806
919
|
* request/result shapes remain on their registered operations. */
|
|
807
920
|
async listGenerationJobs(): Promise<GenerationJobsDocument> {
|
|
808
921
|
const res = await fetch(`${this.baseUrl}/__editor/generations`);
|
|
922
|
+
const body = await this.readJson(res);
|
|
809
923
|
if (!res.ok) throw new Error(`Failed to list generation jobs: ${res.status}`);
|
|
810
|
-
return
|
|
924
|
+
return body as GenerationJobsDocument;
|
|
811
925
|
}
|
|
812
926
|
|
|
813
927
|
/** Forget operational job state. Accepted provenance and project assets
|
|
@@ -816,8 +930,9 @@ export class EditorClient {
|
|
|
816
930
|
const res = await fetch(`${this.baseUrl}/__editor/generations/${encodeURIComponent(id)}`, {
|
|
817
931
|
method: 'DELETE',
|
|
818
932
|
});
|
|
819
|
-
|
|
820
|
-
|
|
933
|
+
const body = (await this.readJson(res)) as { removed?: boolean; error?: string };
|
|
934
|
+
if (!res.ok) throw new Error(body.error ?? `Failed to forget generation job: ${res.status}`);
|
|
935
|
+
return body.removed === true;
|
|
821
936
|
}
|
|
822
937
|
|
|
823
938
|
// --- Logs ---
|
|
@@ -826,10 +941,10 @@ export class EditorClient {
|
|
|
826
941
|
Array<{ t: number; level: string; msg: string; source?: string }>
|
|
827
942
|
> {
|
|
828
943
|
const res = await fetch(`${this.baseUrl}/__editor/log-entries`);
|
|
829
|
-
|
|
830
|
-
const data = (await res.json()) as {
|
|
944
|
+
const data = (await this.readJson(res)) as {
|
|
831
945
|
entries: Array<{ t: number; level: string; msg: string; source?: string }>;
|
|
832
946
|
};
|
|
947
|
+
if (!res.ok) return [];
|
|
833
948
|
return data.entries;
|
|
834
949
|
}
|
|
835
950
|
|
|
@@ -837,17 +952,88 @@ export class EditorClient {
|
|
|
837
952
|
|
|
838
953
|
async getState(): Promise<EditorState> {
|
|
839
954
|
const res = await fetch(`${this.baseUrl}/__editor/state`);
|
|
955
|
+
const state = (await this.readJson(res)) as EditorState;
|
|
840
956
|
if (!res.ok) throw new Error(`Failed to get editor state: ${res.status} ${res.statusText}`);
|
|
841
|
-
const state = (await res.json()) as EditorState;
|
|
842
|
-
this.observe(state);
|
|
843
957
|
return state;
|
|
844
958
|
}
|
|
845
959
|
|
|
960
|
+
/**
|
|
961
|
+
* The complete unresolved console set the session is holding right now.
|
|
962
|
+
*
|
|
963
|
+
* Command envelopes only carry COUNTS (`unresolvedConsole` on
|
|
964
|
+
* `commandResponseFor`). The named conditions live on GET `/__editor/console`.
|
|
965
|
+
* This is the method that turns "a command that exits before an envelope
|
|
966
|
+
* arrives" into a real reading: the CLI calls it at start and at exit
|
|
967
|
+
* through the same {@link onEnvelope} observer every other response uses.
|
|
968
|
+
* A session that does not answer within {@link CONSOLE_DRAIN_TIMEOUT_MS} is
|
|
969
|
+
* a thrown error the caller treats as "nothing learned", never a hang.
|
|
970
|
+
*/
|
|
971
|
+
async getUnresolvedConsole(opts?: { all?: boolean }): Promise<unknown> {
|
|
972
|
+
const res = await fetch(
|
|
973
|
+
`${this.baseUrl}/__editor/console${opts?.all === true ? '?all=1' : ''}`,
|
|
974
|
+
{ signal: AbortSignal.timeout(CONSOLE_DRAIN_TIMEOUT_MS) },
|
|
975
|
+
);
|
|
976
|
+
const body = await this.readJson(res, true);
|
|
977
|
+
if (!res.ok) {
|
|
978
|
+
throw new Error(`Failed to read unresolved console: ${res.status}`);
|
|
979
|
+
}
|
|
980
|
+
return body;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/** Acknowledge one named console condition. The response is observed and
|
|
984
|
+
* hydrated through the same path as every other client response. */
|
|
985
|
+
async acknowledgeConsole(input: {
|
|
986
|
+
readonly id: string;
|
|
987
|
+
readonly reason: string;
|
|
988
|
+
readonly by: string;
|
|
989
|
+
}): Promise<unknown> {
|
|
990
|
+
const res = await fetch(`${this.baseUrl}/__editor/console/ack`, {
|
|
991
|
+
method: 'POST',
|
|
992
|
+
headers: { 'Content-Type': 'application/json' },
|
|
993
|
+
body: JSON.stringify(input),
|
|
994
|
+
signal: AbortSignal.timeout(CONSOLE_DRAIN_TIMEOUT_MS * 4),
|
|
995
|
+
});
|
|
996
|
+
const body = await this.readJson(res);
|
|
997
|
+
return body;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** Parse one JSON body and hand it to {@link onEnvelope}.
|
|
1001
|
+
*
|
|
1002
|
+
* A command/state envelope carries current counts but not the named set. If
|
|
1003
|
+
* an observer is installed, do the bounded console GET before resolving the
|
|
1004
|
+
* original request. That makes a subsequent `process.exit()` safe: the
|
|
1005
|
+
* observer has already received every condition and occurrence count. */
|
|
1006
|
+
private async readJson(res: Response, consoleComplete = false): Promise<unknown> {
|
|
1007
|
+
const body: unknown = await res.json();
|
|
1008
|
+
this.observe(body, { unresolvedConsoleComplete: consoleComplete });
|
|
1009
|
+
if (
|
|
1010
|
+
this.onEnvelope !== null &&
|
|
1011
|
+
!consoleComplete &&
|
|
1012
|
+
body !== null &&
|
|
1013
|
+
typeof body === 'object' &&
|
|
1014
|
+
'unresolvedConsole' in body
|
|
1015
|
+
) {
|
|
1016
|
+
try {
|
|
1017
|
+
const consoleRes = await fetch(`${this.baseUrl}/__editor/console`, {
|
|
1018
|
+
signal: AbortSignal.timeout(CONSOLE_DRAIN_TIMEOUT_MS),
|
|
1019
|
+
});
|
|
1020
|
+
if (consoleRes.ok) {
|
|
1021
|
+
const consoleBody: unknown = await consoleRes.json();
|
|
1022
|
+
this.observe(consoleBody, { unresolvedConsoleComplete: true });
|
|
1023
|
+
}
|
|
1024
|
+
} catch {
|
|
1025
|
+
// The original response remains authoritative. A reporting follow-up
|
|
1026
|
+
// may degrade to its count-only envelope, never break the command.
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return body;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
846
1032
|
/** Hand one response body to {@link onEnvelope}, never letting it throw. */
|
|
847
|
-
private observe(body: unknown): void {
|
|
1033
|
+
private observe(body: unknown, observation: EditorEnvelopeObservation): void {
|
|
848
1034
|
if (this.onEnvelope === null) return;
|
|
849
1035
|
try {
|
|
850
|
-
this.onEnvelope(body);
|
|
1036
|
+
this.onEnvelope(body, observation);
|
|
851
1037
|
} catch {
|
|
852
1038
|
// A reporting hook may never break the command it is reporting on.
|
|
853
1039
|
}
|
package/src/contributions.ts
CHANGED
|
@@ -40,6 +40,13 @@ export interface ToolContributionNode {
|
|
|
40
40
|
*/
|
|
41
41
|
export interface ToolObject3DPreviewSource {
|
|
42
42
|
readonly root: Object3D;
|
|
43
|
+
/**
|
|
44
|
+
* Optional authored roots for the document hierarchy when `root` is a
|
|
45
|
+
* presentation/lifetime container rather than authored content itself.
|
|
46
|
+
* Framing, ticking and disposal still own `root`; this list only scopes the
|
|
47
|
+
* shared Hierarchy. Omitted means `root`, preserving the native tree.
|
|
48
|
+
*/
|
|
49
|
+
readonly hierarchyRoots?: readonly Object3D[];
|
|
43
50
|
readonly animations?: readonly AnimationClip[];
|
|
44
51
|
/**
|
|
45
52
|
* The source's OWN tick, and the host drives it EXACTLY ONCE per mount — for
|
package/src/editor-view.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,7 +4,12 @@ export type {
|
|
|
4
4
|
GenerationJobStatus,
|
|
5
5
|
GenerationJobsDocument,
|
|
6
6
|
} from '@vgai/sdk/generations';
|
|
7
|
-
export type {
|
|
7
|
+
export type {
|
|
8
|
+
EditorEnvelopeObservation,
|
|
9
|
+
GameDebugDoor,
|
|
10
|
+
HistoryStep,
|
|
11
|
+
OpenedDocument,
|
|
12
|
+
} from './client.js';
|
|
8
13
|
export { EditorClient, EditorCommandError } from './client.js';
|
|
9
14
|
export type {
|
|
10
15
|
ToolAssetInspectorContributionMatch,
|
|
@@ -73,6 +78,7 @@ export type {
|
|
|
73
78
|
ProjectToolCatalogEntry,
|
|
74
79
|
ProjectToolContribution,
|
|
75
80
|
ProjectToolOutcome,
|
|
81
|
+
RagdollGenerationResult,
|
|
76
82
|
RecentProject,
|
|
77
83
|
ShadingMode,
|
|
78
84
|
ShotSetPoseRotation,
|
package/src/types.ts
CHANGED
|
@@ -9,15 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export type ViewportTab = 'edit' | 'play';
|
|
11
11
|
|
|
12
|
-
export type AssetKind =
|
|
13
|
-
| 'model'
|
|
14
|
-
| 'image'
|
|
15
|
-
| 'audio'
|
|
16
|
-
| 'animation'
|
|
17
|
-
| 'json'
|
|
18
|
-
| 'prefab'
|
|
19
|
-
| 'material'
|
|
20
|
-
| 'source';
|
|
12
|
+
export type AssetKind = 'model' | 'image' | 'audio' | 'animation' | 'json' | 'prefab' | 'source';
|
|
21
13
|
|
|
22
14
|
export interface HelperVisibility {
|
|
23
15
|
bounds: boolean;
|
|
@@ -58,6 +50,16 @@ export interface ViewportCapture {
|
|
|
58
50
|
mimeType: 'image/png';
|
|
59
51
|
}
|
|
60
52
|
|
|
53
|
+
/** Result of the editor's native Asset Lab ragdoll-generation operation. */
|
|
54
|
+
export interface RagdollGenerationResult {
|
|
55
|
+
capability: 'added' | 'present';
|
|
56
|
+
componentPath: string;
|
|
57
|
+
storyPath: string;
|
|
58
|
+
documentId: string;
|
|
59
|
+
bodies: number;
|
|
60
|
+
joints: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
61
63
|
/**
|
|
62
64
|
* Unit 4 (live-front-door wave) — the RUNNING GAME's pixels, as captured by
|
|
63
65
|
* the `bridge-screenshot` relay op (`command-listener.ts`'s
|
|
@@ -599,9 +601,13 @@ export interface EditorState {
|
|
|
599
601
|
projectName?: string | null;
|
|
600
602
|
/**
|
|
601
603
|
* The open project's ADAPTER, resolved (ARCHITECTURE-CORE §The editor
|
|
602
|
-
* protocol). `source: '
|
|
603
|
-
* supplied the binding table
|
|
604
|
-
*
|
|
604
|
+
* protocol). `source` names WHOSE declaration is running: `'project'` = the
|
|
605
|
+
* project's own `vgai.adapter.ts` supplied the binding table (and it always
|
|
606
|
+
* outranks the registry); `'registry'` = the HOST's in-tree ingest registry
|
|
607
|
+
* supplied it, matched on this project's ingest root id, with `modulePath`
|
|
608
|
+
* naming the repo file — a binding the project did not ship, stated rather
|
|
609
|
+
* than inferred; `'native'` = it declared none and got `nativeAdapter()` —
|
|
610
|
+
* the declared native default, not a silent fallback.
|
|
605
611
|
*
|
|
606
612
|
* `null`/absent means NOBODY HAS LOOKED YET (no project open, or the load
|
|
607
613
|
* has not finished), which is deliberately distinct from a loaded adapter
|
|
@@ -614,7 +620,7 @@ export interface EditorState {
|
|
|
614
620
|
* contract: everything in it has already been through JSON.
|
|
615
621
|
*/
|
|
616
622
|
adapter?: {
|
|
617
|
-
source: '
|
|
623
|
+
source: 'project' | 'registry' | 'native';
|
|
618
624
|
modulePath: string | null;
|
|
619
625
|
regions: {
|
|
620
626
|
id: string;
|
|
@@ -790,14 +796,17 @@ export interface InspectedField {
|
|
|
790
796
|
/** The value shown is the declared default — the document does not carry it. */
|
|
791
797
|
defaulted?: boolean;
|
|
792
798
|
readonly?: boolean;
|
|
799
|
+
/** The same reason shown by the Inspector and returned by a refused write. */
|
|
800
|
+
readonlyReason?: string;
|
|
793
801
|
resettable?: boolean;
|
|
794
802
|
revertsTo?: string;
|
|
795
803
|
group?: string;
|
|
796
804
|
options?: readonly unknown[];
|
|
797
805
|
}
|
|
798
806
|
|
|
799
|
-
/** A section's content.
|
|
800
|
-
*
|
|
807
|
+
/** A section's content. Custom RENDERING remains opaque — the wire never
|
|
808
|
+
* introspects React — while any ordinary descriptor channel that chrome owns
|
|
809
|
+
* remains visible as `fields`, including its write-refusal reasons. The two
|
|
801
810
|
* opaque kinds are distinguished because "this subject has a live preview"
|
|
802
811
|
* is a real fact about it: `custom` is a contributed block, `preview` is the
|
|
803
812
|
* subject's own square view of itself.
|
|
@@ -809,7 +818,13 @@ export interface InspectedField {
|
|
|
809
818
|
* quaternion behind them is not on this wire). */
|
|
810
819
|
export type InspectedSectionBody =
|
|
811
820
|
| { kind: 'fields'; fields: readonly InspectedField[] }
|
|
812
|
-
| {
|
|
821
|
+
| {
|
|
822
|
+
kind: 'custom';
|
|
823
|
+
id: string;
|
|
824
|
+
title: string;
|
|
825
|
+
data?: Record<string, unknown>;
|
|
826
|
+
fields?: readonly InspectedField[];
|
|
827
|
+
}
|
|
813
828
|
| { kind: 'preview'; id: string; title: string };
|
|
814
829
|
|
|
815
830
|
export interface InspectedSection {
|