@vgai/editor 0.4.0 → 0.4.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.
- package/dist/assets/{axe-BJjvBeOZ.js → axe-N4cNyzfs.js} +1 -1
- package/dist/assets/{browserAll-Dn0pgD87.js → browserAll-BIBT0Z2H.js} +1 -1
- package/dist/assets/{create-runtime-BF9OD-U8.js → create-runtime-DxLhdk4U.js} +1 -1
- package/dist/assets/{game-u6c2Patu.js → game-4NbNRROm.js} +1 -1
- package/dist/assets/{game-umrHIqFB.js → game-B3UH5LjS.js} +1 -1
- package/dist/assets/{game-BR0GBNgF.js → game-ByxRLJi1.js} +1 -1
- package/dist/assets/{game-DeTGdgYt.js → game-CJ5BqOYG.js} +1 -1
- package/dist/assets/{game-DQxhCYsZ.js → game-CSJm1kwB.js} +1 -1
- package/dist/assets/{game-CLNm2g0y.js → game-D9Mo4L3Q.js} +1 -1
- package/dist/assets/{game-B346oj_f.js → game-DJmU9BQF.js} +1 -1
- package/dist/assets/{game-BCNN63uV.js → game-ZqGLgxRj.js} +1 -1
- package/dist/assets/{game-loop-t5W_oqxz.js → game-loop-9YP76JB1.js} +1 -1
- package/dist/assets/{game-source-DzE9taML.js → game-source-DLKG9pmj.js} +1 -1
- package/dist/assets/{index-DbQzGLxv.js → index-8A4JLNIp.js} +902 -892
- package/dist/assets/{index-CgcH1RpF.js → index-Brzr17q0.js} +1 -1
- package/dist/assets/{index-D3tcNB2T.js → index-CnKtvrru.js} +1 -1
- package/dist/assets/{index-Fc8Ezr20.js → index-D-WR11Jw.js} +1 -1
- package/dist/assets/index-DiUacMXR.css +1 -0
- package/dist/assets/{ingest-siblings-CW_xbvrF.js → ingest-siblings-Cmx39OBK.js} +1 -1
- package/dist/assets/{mount-manifest-DgUscyaD.js → mount-manifest-kzdZgkkc.js} +1 -1
- package/dist/assets/{react-18-C4tVCii_.js → react-18-DvWsdwa_.js} +1 -1
- package/dist/assets/webworkerAll-C9GR-KrR.js +1 -0
- package/dist/assets/{world-documents-BSaA5YIF.js → world-documents-DM_aIMAW.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/packaged.mjs +27125 -1075
- package/package.json +2 -2
- package/server/asset-history-snapshots.ts +189 -0
- package/server/asset-library-routes.ts +404 -53
- package/server/catalog-import-recipe.ts +97 -0
- package/server/catalog-reimport.ts +141 -0
- package/server/cloud-asset-catalog.ts +71 -5
- package/server/dev.ts +1 -1
- package/server/editor-server.ts +106 -51
- package/server/editor-sse.ts +5 -0
- package/server/generative-execution-context.ts +45 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/packaged.ts +1 -1
- package/server/project-hmr-files.ts +3 -5
- package/server/project-output-writer.ts +440 -36
- package/server/project-tools.ts +529 -0
- package/server/server-utils.ts +14 -11
- package/src/EditorContext.tsx +4 -4
- package/src/action-registry.ts +12 -0
- package/src/asset-editor-context.tsx +9 -0
- package/src/asset-events.ts +3 -15
- package/src/asset-selection.ts +3 -0
- package/src/asset-workflow/asset-capabilities.ts +40 -0
- package/src/asset-workflow/asset-import-jobs.ts +108 -0
- package/src/asset-workflow/asset-library-query.ts +8 -1
- package/src/asset-workflow/asset-workflow-quality.ts +2 -0
- package/src/asset-workflow/import-contract.ts +2 -0
- package/src/asset-workflow/model-inspection.ts +92 -1
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +1 -0
- package/src/asset-workflow/project-asset-health.ts +3 -6
- package/src/asset-workflow/project-asset-operations.ts +119 -4
- package/src/command-listener.ts +37 -9
- package/src/components/ApplicationMenus.tsx +13 -15
- package/src/components/AssetBrowser.tsx +301 -26
- package/src/components/AssetImportDetails.tsx +207 -92
- package/src/components/AssetImportJobStatus.tsx +55 -0
- package/src/components/CenterDocuments.tsx +28 -1
- package/src/components/CommandPalette.tsx +2 -2
- package/src/components/DefaultEditorLayout.tsx +4 -0
- package/src/components/InspectorToolSection.tsx +12 -10
- package/src/components/OnlineAssetBrowser.tsx +197 -11
- package/src/components/ToolHost.tsx +8 -8
- package/src/components/ViewportOverlay.tsx +13 -26
- package/src/components/ViewportPanel.tsx +19 -14
- package/src/components/ViewportShadingMenu.tsx +103 -0
- package/src/components/WorkspaceDock.tsx +103 -17
- package/src/components/asset-documents.tsx +38 -11
- package/src/components/asset-editor-persistence.ts +300 -5
- package/src/components/asset-selection-section.tsx +83 -9
- package/src/components/asset-viewers/AudioViewer.tsx +4 -7
- package/src/components/asset-viewers/ModelViewer.tsx +168 -5
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +52 -12
- package/src/components/asset-viewers/SourceAssetViewer.tsx +7 -26
- package/src/components/asset-workflow.css +30 -3
- package/src/components/primitives/editor-icons.ts +1 -0
- package/src/components/project-tool-documents.tsx +298 -0
- package/src/components/status-contributions.tsx +38 -0
- package/src/components/tool-documents.tsx +20 -17
- package/src/components/tool-schema-form.tsx +258 -0
- package/src/components/workspace-dock.css +16 -0
- package/src/editor-api.ts +159 -22
- package/src/editor-store.ts +6 -5
- package/src/editor-viewport.ts +12 -7
- package/src/game-viewport-state.ts +28 -0
- package/src/model-thumbnail.ts +97 -9
- package/src/play-mode.ts +30 -3
- package/src/project-tool-discovery.ts +5 -5
- package/src/{project-operations.ts → project-tools.ts} +39 -26
- package/src/scene-sync.ts +0 -67
- package/src/storage/handle-storage.ts +5 -1
- package/src/storage/http-storage.ts +7 -2
- package/src/storage/mem-storage.ts +7 -1
- package/src/storage/types.ts +2 -0
- package/src/theme-css.ts +17 -7
- package/src/tool-loader.ts +152 -241
- package/src/viewport-shading-boundary.ts +15 -0
- package/src/widgets/index.ts +2 -1
- package/src/workspace-dock-controller.ts +8 -0
- package/src/workspace-document-registry.ts +4 -4
- package/src/workspace-state-persistence.ts +36 -10
- package/template/.claude/skills/editor/SKILL.md +13 -3
- package/template/AGENTS.md +100 -83
- package/template/package.json +16 -0
- package/template/public/scenes/default.vscn.json +1 -2
- package/template/scripts/playtest.ts +7 -37
- package/template/server/rooms/game-room.ts +2 -2
- package/template/src/data/README.md +3 -2
- package/template/src/data/tuning.data.json +1 -3
- package/template/src/data/tuning.schema.json +0 -12
- package/template/src/data/tuning.schema.ts +2 -9
- package/template/src/data/tuning.ts +1 -1
- package/template/src/scripts/registry.ts +0 -3
- package/template/src/tools/README.md +151 -16
- package/template/src/tools/autoplay.tool.ts +31 -0
- package/template/src/tools/autoplay.ts +15 -0
- package/template/src/tools/tuning.document.tsx +107 -0
- package/template/src/tools/tuning.tool.ts +24 -0
- package/template/src/tools/tuning.ts +9 -0
- package/template/src/ui/Button.stories.tsx +1 -1
- package/template/tests/acceptance/example.spec.ts +4 -59
- package/template/vgai.game.json +1 -1
- package/dist/assets/index-bAGhEnnO.css +0 -1
- package/dist/assets/webworkerAll-BA4aq7xq.js +0 -1
- package/dist/project-scripts.bundle.js +0 -3917
- package/server/project-operations.ts +0 -343
- package/src/components/project-operation-documents.tsx +0 -226
- package/template/src/operations/README.md +0 -88
- package/template/src/scripts/components/data-spinner.ts +0 -86
- package/template/src/scripts/components/spin-lap-math.ts +0 -27
- package/template/src/tools/example.tool.tsx +0 -97
- package/template/src/tools/spin.tool.tsx +0 -101
- package/template/tests/logic/example.test.ts +0 -31
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { connectToolFileEvents } from './asset-events';
|
|
2
2
|
import { getEditorMode } from './editor-mode';
|
|
3
3
|
import { onProjectChange } from './project-manager';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface ProjectToolCatalogEntry {
|
|
6
6
|
name: string;
|
|
7
7
|
summary: string;
|
|
8
8
|
description: string;
|
|
@@ -16,58 +16,71 @@ export interface ProjectOperationCatalogEntry {
|
|
|
16
16
|
supportsDryRun: boolean;
|
|
17
17
|
longRunning: boolean;
|
|
18
18
|
permission: { risk: 'read' | 'write' | 'destructive'; summary: string };
|
|
19
|
+
contributions: ProjectToolContribution[];
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export
|
|
22
|
-
|
|
22
|
+
export type ToolContributionPoint =
|
|
23
|
+
| 'workspace.document'
|
|
24
|
+
| 'selection.inspector'
|
|
25
|
+
| 'workspace.utility';
|
|
26
|
+
|
|
27
|
+
export interface ProjectToolContribution {
|
|
28
|
+
id: string;
|
|
29
|
+
point: ToolContributionPoint;
|
|
30
|
+
title: string;
|
|
31
|
+
entryPath: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ProjectToolCatalog {
|
|
35
|
+
tools: ProjectToolCatalogEntry[];
|
|
23
36
|
loadErrors: Array<{ sourcePath: string; message: string }>;
|
|
24
37
|
}
|
|
25
38
|
|
|
26
|
-
export type
|
|
39
|
+
export type ProjectToolOutcome =
|
|
27
40
|
| { ok: true; data: unknown }
|
|
28
41
|
| { ok: false; error: { code: string; message: string; data?: unknown; issues?: unknown[] } };
|
|
29
42
|
|
|
30
|
-
const EMPTY_CATALOG:
|
|
43
|
+
const EMPTY_CATALOG: ProjectToolCatalog = { tools: [], loadErrors: [] };
|
|
31
44
|
let catalog = EMPTY_CATALOG;
|
|
32
45
|
let version = 0;
|
|
33
46
|
const listeners = new Set<() => void>();
|
|
34
47
|
|
|
35
|
-
function publish(next:
|
|
48
|
+
function publish(next: ProjectToolCatalog): void {
|
|
36
49
|
catalog = next;
|
|
37
50
|
version++;
|
|
38
51
|
for (const listener of listeners) listener();
|
|
39
52
|
}
|
|
40
53
|
|
|
41
|
-
export function
|
|
54
|
+
export function getProjectTools(): ProjectToolCatalog {
|
|
42
55
|
return catalog;
|
|
43
56
|
}
|
|
44
57
|
|
|
45
|
-
export function
|
|
58
|
+
export function projectToolsVersion(): number {
|
|
46
59
|
return version;
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
export function
|
|
62
|
+
export function subscribeProjectTools(listener: () => void): () => void {
|
|
50
63
|
listeners.add(listener);
|
|
51
64
|
return () => listeners.delete(listener);
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
export async function
|
|
67
|
+
export async function refreshProjectTools(): Promise<ProjectToolCatalog> {
|
|
55
68
|
if (getEditorMode() === 'browser') {
|
|
56
69
|
publish(EMPTY_CATALOG);
|
|
57
70
|
return EMPTY_CATALOG;
|
|
58
71
|
}
|
|
59
72
|
try {
|
|
60
|
-
const response = await fetch('/__editor/project-
|
|
73
|
+
const response = await fetch('/__editor/project-tools');
|
|
61
74
|
if (!response.ok) throw new Error(`catalog request failed (${response.status})`);
|
|
62
|
-
const next = (await response.json()) as
|
|
75
|
+
const next = (await response.json()) as ProjectToolCatalog;
|
|
63
76
|
publish(next);
|
|
64
77
|
return next;
|
|
65
78
|
} catch (error) {
|
|
66
79
|
const next = {
|
|
67
|
-
|
|
80
|
+
tools: [],
|
|
68
81
|
loadErrors: [
|
|
69
82
|
{
|
|
70
|
-
sourcePath: '
|
|
83
|
+
sourcePath: 'package.json#vgai.tools',
|
|
71
84
|
message: error instanceof Error ? error.message : String(error),
|
|
72
85
|
},
|
|
73
86
|
],
|
|
@@ -77,35 +90,35 @@ export async function refreshProjectOperations(): Promise<ProjectOperationCatalo
|
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
export async function
|
|
93
|
+
export async function runProjectTool(
|
|
81
94
|
name: string,
|
|
82
95
|
input: unknown,
|
|
83
96
|
options: { confirm?: boolean } = {},
|
|
84
|
-
): Promise<
|
|
85
|
-
const response = await fetch('/__editor/project-
|
|
97
|
+
): Promise<ProjectToolOutcome> {
|
|
98
|
+
const response = await fetch('/__editor/project-tools/run', {
|
|
86
99
|
method: 'POST',
|
|
87
100
|
headers: { 'Content-Type': 'application/json' },
|
|
88
101
|
body: JSON.stringify({ name, input, confirm: options.confirm === true }),
|
|
89
102
|
});
|
|
90
|
-
const outcome = (await response.json()) as
|
|
103
|
+
const outcome = (await response.json()) as ProjectToolOutcome;
|
|
91
104
|
if (!outcome || typeof outcome !== 'object' || typeof outcome.ok !== 'boolean') {
|
|
92
|
-
throw new Error(`
|
|
105
|
+
throw new Error(`Tool request returned an invalid response (${response.status}).`);
|
|
93
106
|
}
|
|
94
107
|
return outcome;
|
|
95
108
|
}
|
|
96
109
|
|
|
97
|
-
/** Editor-init lifecycle for Node-side
|
|
98
|
-
export function
|
|
99
|
-
void
|
|
100
|
-
const stopProjectWatch = onProjectChange(() => void
|
|
101
|
-
const stopFileWatch =
|
|
110
|
+
/** Editor-init lifecycle for the Node-side registered tool catalog. */
|
|
111
|
+
export function startProjectToolCatalog(): () => void {
|
|
112
|
+
void refreshProjectTools();
|
|
113
|
+
const stopProjectWatch = onProjectChange(() => void refreshProjectTools());
|
|
114
|
+
const stopFileWatch = connectToolFileEvents(() => void refreshProjectTools());
|
|
102
115
|
return () => {
|
|
103
116
|
stopProjectWatch();
|
|
104
117
|
stopFileWatch();
|
|
105
118
|
};
|
|
106
119
|
}
|
|
107
120
|
|
|
108
|
-
export function
|
|
121
|
+
export function __resetProjectToolsForTest(): void {
|
|
109
122
|
catalog = EMPTY_CATALOG;
|
|
110
123
|
version = 0;
|
|
111
124
|
listeners.clear();
|
package/src/scene-sync.ts
CHANGED
|
@@ -14,7 +14,6 @@ import * as THREE from 'three';
|
|
|
14
14
|
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
|
15
15
|
import type { BatchedRenderer, ParticleSystem } from 'three.quarks';
|
|
16
16
|
import { readProjectTextFile } from './editor-api';
|
|
17
|
-
import type { ShadingMode } from './editor-store';
|
|
18
17
|
import { createEntityObject, disposeEntityAnimation } from './entity-factory';
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -231,7 +230,6 @@ export function unregisterParticles(
|
|
|
231
230
|
export function buildPreview(
|
|
232
231
|
doc: SceneFile,
|
|
233
232
|
scene: THREE.Scene,
|
|
234
|
-
shadingMode: ShadingMode = 'solid',
|
|
235
233
|
renderer?: THREE.WebGLRenderer,
|
|
236
234
|
batchedRenderer?: BatchedRenderer | null,
|
|
237
235
|
): Map<string, THREE.Object3D> {
|
|
@@ -340,11 +338,6 @@ export function buildPreview(
|
|
|
340
338
|
// Apply environment
|
|
341
339
|
applyEnvironment(doc.environment, scene, renderer);
|
|
342
340
|
|
|
343
|
-
// Apply shading mode override
|
|
344
|
-
if (shadingMode !== 'solid') {
|
|
345
|
-
applyShadingOverride(scene, shadingMode);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
341
|
return objectMap;
|
|
349
342
|
}
|
|
350
343
|
|
|
@@ -530,63 +523,3 @@ async function loadEnvTexture(
|
|
|
530
523
|
return null;
|
|
531
524
|
}
|
|
532
525
|
}
|
|
533
|
-
|
|
534
|
-
function asMaterialArray(m: THREE.Material | THREE.Material[]): THREE.Material[] {
|
|
535
|
-
return Array.isArray(m) ? m : [m];
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Apply (and revert) the viewport shading override on a single mesh.
|
|
540
|
-
*
|
|
541
|
-
* Reversible across all three modes so the toolbar toggle can re-shade live
|
|
542
|
-
* meshes without a full scene rebuild (ED1):
|
|
543
|
-
* - solid: restore the original material(s), clear wireframe.
|
|
544
|
-
* - wireframe: restore original material(s), set wireframe on.
|
|
545
|
-
* - unlit: swap in MeshBasicMaterial(s) built from the original color.
|
|
546
|
-
*
|
|
547
|
-
* The original material is stashed in `userData['__shadeOrig']` the first time
|
|
548
|
-
* the mesh is touched; unlit overrides are stashed in `__shadeUnlit` so they
|
|
549
|
-
* can be disposed when leaving unlit.
|
|
550
|
-
*/
|
|
551
|
-
function applyShadingToMesh(mesh: THREE.Mesh, mode: ShadingMode): void {
|
|
552
|
-
if (getUserData(mesh, '__shadeOrig') === undefined) {
|
|
553
|
-
setUserData(mesh, '__shadeOrig', mesh.material);
|
|
554
|
-
}
|
|
555
|
-
const orig = getUserData(mesh, '__shadeOrig') as THREE.Material | THREE.Material[];
|
|
556
|
-
const unlit = getUserData(mesh, '__shadeUnlit') as THREE.Material[] | undefined;
|
|
557
|
-
|
|
558
|
-
if (mode === 'unlit') {
|
|
559
|
-
if (unlit) {
|
|
560
|
-
mesh.material = Array.isArray(orig) ? unlit : unlit[0]!;
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const created = asMaterialArray(orig).map((m) => {
|
|
564
|
-
const color = (m as THREE.MeshStandardMaterial).color?.clone() ?? new THREE.Color(0x888888);
|
|
565
|
-
return new THREE.MeshBasicMaterial({ color });
|
|
566
|
-
});
|
|
567
|
-
setUserData(mesh, '__shadeUnlit', created);
|
|
568
|
-
mesh.material = Array.isArray(orig) ? created : created[0]!;
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// solid or wireframe: restore originals and drop any unlit override.
|
|
573
|
-
mesh.material = orig;
|
|
574
|
-
if (unlit) {
|
|
575
|
-
for (const m of unlit) m.dispose();
|
|
576
|
-
deleteUserData(mesh, '__shadeUnlit');
|
|
577
|
-
}
|
|
578
|
-
const wire = mode === 'wireframe';
|
|
579
|
-
for (const m of asMaterialArray(orig)) {
|
|
580
|
-
if ('wireframe' in m) (m as THREE.MeshStandardMaterial).wireframe = wire;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/** Apply viewport shading override to all entity meshes. Reversible (ED1). */
|
|
585
|
-
export function applyShadingOverride(scene: THREE.Scene, mode: ShadingMode): void {
|
|
586
|
-
scene.traverse((obj) => {
|
|
587
|
-
if (getUserData(obj, 'entityId') === undefined) return;
|
|
588
|
-
const mesh = obj as THREE.Mesh;
|
|
589
|
-
if (!mesh.isMesh || !mesh.material) return;
|
|
590
|
-
applyShadingToMesh(mesh, mode);
|
|
591
|
-
});
|
|
592
|
-
}
|
|
@@ -97,7 +97,11 @@ export abstract class HandleStorage implements StorageBackend {
|
|
|
97
97
|
path: base ? `${base}/${name}` : name,
|
|
98
98
|
type: isDir ? 'dir' : 'file',
|
|
99
99
|
};
|
|
100
|
-
if (!isDir)
|
|
100
|
+
if (!isDir) {
|
|
101
|
+
const file = await (child as FileSystemFileHandle).getFile();
|
|
102
|
+
entry.size = file.size;
|
|
103
|
+
entry.mtime = file.lastModified;
|
|
104
|
+
}
|
|
101
105
|
entries.push(entry);
|
|
102
106
|
}
|
|
103
107
|
// Directories first, then alphabetical — matches the server's /assets route.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (write) routes; reads come from Vite's `/@fs/` static serving.
|
|
9
9
|
*
|
|
10
10
|
* Route mapping (paths are relative to `public/`):
|
|
11
|
-
* list → GET /__editor/assets?dir → { entries: [{name,type,size}] }
|
|
11
|
+
* list → GET /__editor/assets?dir → { entries: [{name,type,size,mtime}] }
|
|
12
12
|
* write → POST /__editor/save-file ({path, content, encoding?})
|
|
13
13
|
* read → GET /@fs/{projectRoot}/public/{path}
|
|
14
14
|
* watch → EventSource /__editor/events (chokidar-backed)
|
|
@@ -27,6 +27,7 @@ interface ServerAssetEntry {
|
|
|
27
27
|
name: string;
|
|
28
28
|
type: 'file' | 'directory';
|
|
29
29
|
size: number;
|
|
30
|
+
mtime: number;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export class HttpStorage implements StorageBackend {
|
|
@@ -69,8 +70,10 @@ export class HttpStorage implements StorageBackend {
|
|
|
69
70
|
name: e.name,
|
|
70
71
|
path: normalize(dir ? `${dir}/${e.name}` : e.name),
|
|
71
72
|
type: e.type === 'directory' ? 'dir' : 'file',
|
|
73
|
+
mtime: e.mtime,
|
|
72
74
|
};
|
|
73
75
|
if (e.type === 'file') out.size = e.size;
|
|
76
|
+
out.mtime = e.mtime;
|
|
74
77
|
return out;
|
|
75
78
|
});
|
|
76
79
|
}
|
|
@@ -87,7 +90,9 @@ export class HttpStorage implements StorageBackend {
|
|
|
87
90
|
return null;
|
|
88
91
|
}
|
|
89
92
|
const hit = entries.find((e) => e.name === base);
|
|
90
|
-
return hit
|
|
93
|
+
return hit
|
|
94
|
+
? { path: hit.path, type: hit.type, size: hit.size ?? 0, mtime: hit.mtime ?? 0 }
|
|
95
|
+
: null;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
async exists(path: string): Promise<boolean> {
|
|
@@ -78,8 +78,14 @@ export class MemStorage implements StorageBackend {
|
|
|
78
78
|
for (const [path, node] of this.nodes) {
|
|
79
79
|
const [parent, base] = dirAndBase(path);
|
|
80
80
|
if (parent !== dn || !base) continue;
|
|
81
|
-
const entry: DirEntry = {
|
|
81
|
+
const entry: DirEntry = {
|
|
82
|
+
name: base,
|
|
83
|
+
path,
|
|
84
|
+
type: node.type === 'dir' ? 'dir' : 'file',
|
|
85
|
+
mtime: node.mtime,
|
|
86
|
+
};
|
|
82
87
|
if (node.type === 'file') entry.size = node.data.length;
|
|
88
|
+
entry.mtime = node.mtime;
|
|
83
89
|
entries.push(entry);
|
|
84
90
|
}
|
|
85
91
|
entries.sort((a, b) => {
|
package/src/storage/types.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface DirEntry {
|
|
|
44
44
|
type: NodeType;
|
|
45
45
|
/** Byte length for files (omitted for directories). */
|
|
46
46
|
size?: number;
|
|
47
|
+
/** Last-modified time, epoch ms (omitted if the backend cannot report it). */
|
|
48
|
+
mtime?: number;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export type WatchEventType = 'create' | 'update' | 'remove';
|
package/src/theme-css.ts
CHANGED
|
@@ -125,6 +125,12 @@ ${themeDeclarations}
|
|
|
125
125
|
|
|
126
126
|
.vgai-btn {
|
|
127
127
|
display: inline-flex;
|
|
128
|
+
/* Inline controls should keep their intrinsic size even when authored as
|
|
129
|
+
* direct children of a CSS grid. Grid's default align-items: stretch
|
|
130
|
+
* otherwise turns a compact action into a full-width slab. A caller that
|
|
131
|
+
* deliberately wants a wide button can still set width: 100%. */
|
|
132
|
+
width: max-content;
|
|
133
|
+
max-width: 100%;
|
|
128
134
|
align-items: center;
|
|
129
135
|
justify-content: center;
|
|
130
136
|
gap: var(--vgai-space-2);
|
|
@@ -829,6 +835,10 @@ ${themeDeclarations}
|
|
|
829
835
|
.vgai-tab-badge,
|
|
830
836
|
.vgai-badge {
|
|
831
837
|
display: inline-flex;
|
|
838
|
+
/* Badges are labels, never layout surfaces. Keep them from stretching into
|
|
839
|
+
* long capsules when a contribution places one directly in a grid. */
|
|
840
|
+
width: max-content;
|
|
841
|
+
max-width: 100%;
|
|
832
842
|
min-width: 15px;
|
|
833
843
|
height: 15px;
|
|
834
844
|
align-items: center;
|
|
@@ -952,14 +962,14 @@ ${themeDeclarations}
|
|
|
952
962
|
.vgai-drop-indicator[data-position='inside'] { inset: 1px; border-radius: var(--vgai-radius-sm); }
|
|
953
963
|
|
|
954
964
|
.vgai-model-tree-item { text-align: left; }
|
|
955
|
-
.vgai-
|
|
956
|
-
display:
|
|
965
|
+
.vgai-btn.vgai-project-tool-card {
|
|
966
|
+
display: grid;
|
|
957
967
|
width: 100%;
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
968
|
+
height: auto;
|
|
969
|
+
align-items: start;
|
|
970
|
+
justify-content: normal;
|
|
971
|
+
gap: var(--vgai-space-2);
|
|
972
|
+
padding-block: var(--vgai-space-3);
|
|
963
973
|
text-align: left;
|
|
964
974
|
}
|
|
965
975
|
|