@veewo/gitnexus 1.4.8 → 1.4.9-rc
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/cli/index.js +8 -0
- package/dist/cli/tool.d.ts +11 -0
- package/dist/cli/tool.js +31 -0
- package/dist/cli/unity-ui-trace.test.d.ts +1 -0
- package/dist/cli/unity-ui-trace.test.js +24 -0
- package/dist/core/ingestion/call-processor.js +9 -1
- package/dist/core/ingestion/type-env.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +17 -2
- package/dist/core/ingestion/type-extractors/types.d.ts +4 -1
- package/dist/core/unity/csharp-selector-binding.d.ts +7 -0
- package/dist/core/unity/csharp-selector-binding.js +32 -0
- package/dist/core/unity/csharp-selector-binding.test.d.ts +1 -0
- package/dist/core/unity/csharp-selector-binding.test.js +14 -0
- package/dist/core/unity/scan-context.d.ts +2 -0
- package/dist/core/unity/scan-context.js +17 -0
- package/dist/core/unity/ui-asset-ref-scanner.d.ts +14 -0
- package/dist/core/unity/ui-asset-ref-scanner.js +213 -0
- package/dist/core/unity/ui-asset-ref-scanner.test.d.ts +1 -0
- package/dist/core/unity/ui-asset-ref-scanner.test.js +44 -0
- package/dist/core/unity/ui-meta-index.d.ts +8 -0
- package/dist/core/unity/ui-meta-index.js +60 -0
- package/dist/core/unity/ui-meta-index.test.d.ts +1 -0
- package/dist/core/unity/ui-meta-index.test.js +18 -0
- package/dist/core/unity/ui-trace-storage-guard.test.d.ts +1 -0
- package/dist/core/unity/ui-trace-storage-guard.test.js +10 -0
- package/dist/core/unity/ui-trace.acceptance.test.d.ts +1 -0
- package/dist/core/unity/ui-trace.acceptance.test.js +38 -0
- package/dist/core/unity/ui-trace.d.ts +31 -0
- package/dist/core/unity/ui-trace.js +363 -0
- package/dist/core/unity/ui-trace.test.d.ts +1 -0
- package/dist/core/unity/ui-trace.test.js +183 -0
- package/dist/core/unity/uss-selector-parser.d.ts +6 -0
- package/dist/core/unity/uss-selector-parser.js +21 -0
- package/dist/core/unity/uss-selector-parser.test.d.ts +1 -0
- package/dist/core/unity/uss-selector-parser.test.js +13 -0
- package/dist/core/unity/uxml-ref-parser.d.ts +10 -0
- package/dist/core/unity/uxml-ref-parser.js +22 -0
- package/dist/core/unity/uxml-ref-parser.test.d.ts +1 -0
- package/dist/core/unity/uxml-ref-parser.test.js +31 -0
- package/dist/mcp/local/local-backend.d.ts +13 -0
- package/dist/mcp/local/local-backend.js +298 -25
- package/dist/mcp/tools.js +41 -0
- package/package.json +2 -1
- package/skills/gitnexus-cli.md +29 -0
- package/skills/gitnexus-guide.md +23 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { runUnityUiTrace } from './ui-trace.js';
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const fixtureRoot = path.resolve(here, '../../../src/core/unity/__fixtures__/mini-unity-ui');
|
|
10
|
+
test('resolves asset_refs with strict path+line evidence chain', async () => {
|
|
11
|
+
const out = await runUnityUiTrace({
|
|
12
|
+
repoRoot: fixtureRoot,
|
|
13
|
+
target: 'EliteBossScreenController',
|
|
14
|
+
goal: 'asset_refs',
|
|
15
|
+
});
|
|
16
|
+
assert.equal(out.goal, 'asset_refs');
|
|
17
|
+
assert.equal(out.results.length, 1);
|
|
18
|
+
assert.equal(out.results[0].evidence_chain.every((hop) => Boolean(hop.path) && hop.line > 0), true);
|
|
19
|
+
});
|
|
20
|
+
test('resolves template_refs from target uxml', async () => {
|
|
21
|
+
const out = await runUnityUiTrace({
|
|
22
|
+
repoRoot: fixtureRoot,
|
|
23
|
+
target: 'Assets/UI/Screens/DressUpScreenNew.uxml',
|
|
24
|
+
goal: 'template_refs',
|
|
25
|
+
});
|
|
26
|
+
assert.equal(out.goal, 'template_refs');
|
|
27
|
+
assert.equal(out.results.length, 1);
|
|
28
|
+
assert.equal(out.results[0].evidence_chain[1].path, 'Assets/UI/Components/TooltipBox.uxml');
|
|
29
|
+
});
|
|
30
|
+
test('resolves selector_bindings for static csharp selector usage', async () => {
|
|
31
|
+
const out = await runUnityUiTrace({
|
|
32
|
+
repoRoot: fixtureRoot,
|
|
33
|
+
target: 'EliteBossScreenController',
|
|
34
|
+
goal: 'selector_bindings',
|
|
35
|
+
});
|
|
36
|
+
assert.equal(out.goal, 'selector_bindings');
|
|
37
|
+
assert.equal(out.results.length, 1);
|
|
38
|
+
assert.equal(out.results[0].evidence_chain.every((hop) => Boolean(hop.path) && hop.line > 0), true);
|
|
39
|
+
});
|
|
40
|
+
test('resolves selector_bindings when target is a UXML path', async () => {
|
|
41
|
+
const out = await runUnityUiTrace({
|
|
42
|
+
repoRoot: fixtureRoot,
|
|
43
|
+
target: 'Assets/UI/Screens/EliteBossScreenNew.uxml',
|
|
44
|
+
goal: 'selector_bindings',
|
|
45
|
+
});
|
|
46
|
+
assert.equal(out.goal, 'selector_bindings');
|
|
47
|
+
assert.equal(out.results.length, 1);
|
|
48
|
+
assert.equal(out.results[0].evidence_chain[0].path.endsWith('.cs'), true);
|
|
49
|
+
assert.equal(out.results[0].evidence_chain[1].path.endsWith('.uss'), true);
|
|
50
|
+
});
|
|
51
|
+
test('selector_bindings path target uses UXML->resource->m_Script chain before filename fallback', async () => {
|
|
52
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ui-trace-selector-chain-'));
|
|
53
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Screens'), { recursive: true });
|
|
54
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Styles'), { recursive: true });
|
|
55
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Prefabs'), { recursive: true });
|
|
56
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Scripts'), { recursive: true });
|
|
57
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/FeaturePanelNew.uxml'), '<ui:UXML xmlns:ui="UnityEngine.UIElements"><ui:Style src="project://database/Assets/UI/Styles/FeaturePanel.uss?guid=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&type=3" /></ui:UXML>\n', 'utf-8');
|
|
58
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/FeaturePanelNew.uxml.meta'), 'fileFormatVersion: 2\nguid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n', 'utf-8');
|
|
59
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/FeaturePanel.uss'), '.feature-panel { color: red; }\n', 'utf-8');
|
|
60
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/FeaturePanel.uss.meta'), 'fileFormatVersion: 2\nguid: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n', 'utf-8');
|
|
61
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/PanelBinder.cs'), 'public class PanelBinder { void Bind() { root.AddToClassList("feature-panel"); } }\n', 'utf-8');
|
|
62
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/PanelBinder.cs.meta'), 'fileFormatVersion: 2\nguid: cccccccccccccccccccccccccccccccc\n', 'utf-8');
|
|
63
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Prefabs/FeaturePanel.prefab'), [
|
|
64
|
+
'%YAML 1.1',
|
|
65
|
+
'--- !u!114 &11400000',
|
|
66
|
+
'MonoBehaviour:',
|
|
67
|
+
' m_Script: {fileID: 11500000, guid: cccccccccccccccccccccccccccccccc, type: 3}',
|
|
68
|
+
' runtimeViewAsset: {fileID: 9197481963319205126, guid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, type: 3}',
|
|
69
|
+
].join('\n'), 'utf-8');
|
|
70
|
+
const out = await runUnityUiTrace({
|
|
71
|
+
repoRoot: tempRoot,
|
|
72
|
+
target: 'Assets/UI/Screens/FeaturePanelNew.uxml',
|
|
73
|
+
goal: 'selector_bindings',
|
|
74
|
+
});
|
|
75
|
+
assert.equal(out.results.length, 1);
|
|
76
|
+
assert.equal(out.results[0].evidence_chain[0].path, 'Assets/Scripts/PanelBinder.cs');
|
|
77
|
+
assert.equal(out.results[0].evidence_chain[1].path, 'Assets/UI/Styles/FeaturePanel.uss');
|
|
78
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
79
|
+
});
|
|
80
|
+
test('selector_bindings matches class token inside composite USS selectors', async () => {
|
|
81
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ui-trace-composite-selector-'));
|
|
82
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Screens'), { recursive: true });
|
|
83
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Styles'), { recursive: true });
|
|
84
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Prefabs'), { recursive: true });
|
|
85
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Scripts'), { recursive: true });
|
|
86
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/CompositePanelNew.uxml'), '<ui:UXML xmlns:ui="UnityEngine.UIElements"><ui:Style src="project://database/Assets/UI/Styles/CompositePanel.uss?guid=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&type=3" /></ui:UXML>\n', 'utf-8');
|
|
87
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/CompositePanelNew.uxml.meta'), 'fileFormatVersion: 2\nguid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n', 'utf-8');
|
|
88
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/CompositePanel.uss'), '.isLock .preview-icon { opacity: 0.4; }\n', 'utf-8');
|
|
89
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/CompositePanel.uss.meta'), 'fileFormatVersion: 2\nguid: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n', 'utf-8');
|
|
90
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/CompositeBinder.cs'), 'public class CompositeBinder { void Bind() { root.AddToClassList("isLock"); } }\n', 'utf-8');
|
|
91
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/CompositeBinder.cs.meta'), 'fileFormatVersion: 2\nguid: cccccccccccccccccccccccccccccccc\n', 'utf-8');
|
|
92
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Prefabs/CompositePanel.prefab'), [
|
|
93
|
+
'%YAML 1.1',
|
|
94
|
+
'--- !u!114 &11400000',
|
|
95
|
+
'MonoBehaviour:',
|
|
96
|
+
' m_Script: {fileID: 11500000, guid: cccccccccccccccccccccccccccccccc, type: 3}',
|
|
97
|
+
' runtimeViewAsset: {fileID: 9197481963319205126, guid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, type: 3}',
|
|
98
|
+
].join('\n'), 'utf-8');
|
|
99
|
+
const out = await runUnityUiTrace({
|
|
100
|
+
repoRoot: tempRoot,
|
|
101
|
+
target: 'Assets/UI/Screens/CompositePanelNew.uxml',
|
|
102
|
+
goal: 'selector_bindings',
|
|
103
|
+
});
|
|
104
|
+
assert.equal(out.results.length, 1);
|
|
105
|
+
assert.equal(out.results[0].evidence_chain[0].path, 'Assets/Scripts/CompositeBinder.cs');
|
|
106
|
+
assert.equal(out.results[0].evidence_chain[1].path, 'Assets/UI/Styles/CompositePanel.uss');
|
|
107
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
108
|
+
});
|
|
109
|
+
test('selector_bindings ranks resource-chain matches ahead of name-fallback matches', async () => {
|
|
110
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ui-trace-ranking-'));
|
|
111
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Screens'), { recursive: true });
|
|
112
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Styles'), { recursive: true });
|
|
113
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Prefabs'), { recursive: true });
|
|
114
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/Scripts'), { recursive: true });
|
|
115
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/RankPanelNew.uxml'), '<ui:UXML xmlns:ui="UnityEngine.UIElements"><ui:Style src="project://database/Assets/UI/Styles/RankPanel.uss?guid=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&type=3" /></ui:UXML>\n', 'utf-8');
|
|
116
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/RankPanelNew.uxml.meta'), 'fileFormatVersion: 2\nguid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n', 'utf-8');
|
|
117
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/RankPanel.uss'), '.active .rank-icon { opacity: 1; }\n', 'utf-8');
|
|
118
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Styles/RankPanel.uss.meta'), 'fileFormatVersion: 2\nguid: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n', 'utf-8');
|
|
119
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/ResourceDriver.cs'), 'public class ResourceDriver { void Bind() { root.AddToClassList("active"); } }\n', 'utf-8');
|
|
120
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/ResourceDriver.cs.meta'), 'fileFormatVersion: 2\nguid: cccccccccccccccccccccccccccccccc\n', 'utf-8');
|
|
121
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/RankPanel.cs'), 'public class RankPanel { void Bind() { root.AddToClassList("active"); } }\n', 'utf-8');
|
|
122
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Scripts/RankPanel.cs.meta'), 'fileFormatVersion: 2\nguid: dddddddddddddddddddddddddddddddd\n', 'utf-8');
|
|
123
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/Prefabs/RankPanel.prefab'), [
|
|
124
|
+
'%YAML 1.1',
|
|
125
|
+
'--- !u!114 &11400000',
|
|
126
|
+
'MonoBehaviour:',
|
|
127
|
+
' m_Script: {fileID: 11500000, guid: cccccccccccccccccccccccccccccccc, type: 3}',
|
|
128
|
+
' runtimeViewAsset: {fileID: 9197481963319205126, guid: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, type: 3}',
|
|
129
|
+
].join('\n'), 'utf-8');
|
|
130
|
+
const out = await runUnityUiTrace({
|
|
131
|
+
repoRoot: tempRoot,
|
|
132
|
+
target: 'Assets/UI/Screens/RankPanelNew.uxml',
|
|
133
|
+
goal: 'selector_bindings',
|
|
134
|
+
});
|
|
135
|
+
assert.equal(out.results.length, 2);
|
|
136
|
+
assert.equal(out.results[0].evidence_chain[0].path, 'Assets/Scripts/ResourceDriver.cs');
|
|
137
|
+
assert.equal(out.results[1].evidence_chain[0].path, 'Assets/Scripts/RankPanel.cs');
|
|
138
|
+
assert.equal((out.results[0].score || 0) > (out.results[1].score || 0), true);
|
|
139
|
+
assert.equal(out.results[0].confidence, 'high');
|
|
140
|
+
assert.equal(out.results[1].confidence, 'medium');
|
|
141
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
142
|
+
});
|
|
143
|
+
test('enforces unique-result gate and returns ambiguity diagnostics', async () => {
|
|
144
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ui-trace-ambiguity-'));
|
|
145
|
+
await fs.cp(fixtureRoot, tempRoot, { recursive: true });
|
|
146
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/EliteBossScreen.uxml'), '<ui:UXML xmlns:ui="UnityEngine.UIElements"><ui:VisualElement /></ui:UXML>\n', 'utf-8');
|
|
147
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Screens/EliteBossScreen.uxml.meta'), 'fileFormatVersion: 2\nguid: 12121212121212121212121212121212\n', 'utf-8');
|
|
148
|
+
const out = await runUnityUiTrace({
|
|
149
|
+
repoRoot: tempRoot,
|
|
150
|
+
target: 'EliteBossScreenController',
|
|
151
|
+
goal: 'asset_refs',
|
|
152
|
+
});
|
|
153
|
+
assert.deepEqual(out.results, []);
|
|
154
|
+
assert.equal(out.diagnostics[0].code, 'ambiguous');
|
|
155
|
+
assert.equal(Boolean(out.diagnostics[0].candidates[0].path), true);
|
|
156
|
+
assert.equal(out.diagnostics[0].candidates[0].line > 0, true);
|
|
157
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
158
|
+
});
|
|
159
|
+
test('treats existing UXML path target as unique even when canonical names collide', async () => {
|
|
160
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ui-trace-path-'));
|
|
161
|
+
await fs.cp(fixtureRoot, tempRoot, { recursive: true });
|
|
162
|
+
await fs.mkdir(path.join(tempRoot, 'Assets/UI/Legacy'), { recursive: true });
|
|
163
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Legacy/EliteBossScreen.uxml'), '<ui:UXML xmlns:ui="UnityEngine.UIElements"><ui:VisualElement /></ui:UXML>\n', 'utf-8');
|
|
164
|
+
await fs.writeFile(path.join(tempRoot, 'Assets/UI/Legacy/EliteBossScreen.uxml.meta'), 'fileFormatVersion: 2\nguid: 34343434343434343434343434343434\n', 'utf-8');
|
|
165
|
+
const out = await runUnityUiTrace({
|
|
166
|
+
repoRoot: tempRoot,
|
|
167
|
+
target: 'Assets/UI/Screens/EliteBossScreenNew.uxml',
|
|
168
|
+
goal: 'asset_refs',
|
|
169
|
+
});
|
|
170
|
+
assert.equal(out.diagnostics.length, 0);
|
|
171
|
+
assert.equal(out.results.length, 1);
|
|
172
|
+
assert.equal(out.results[0].evidence_chain[1].path, 'Assets/UI/Screens/EliteBossScreenNew.uxml');
|
|
173
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
174
|
+
});
|
|
175
|
+
test('ensures no graph mutations occur in query-time engine', async () => {
|
|
176
|
+
const mockGraphAddRelationship = () => { };
|
|
177
|
+
await runUnityUiTrace({
|
|
178
|
+
repoRoot: fixtureRoot,
|
|
179
|
+
target: 'EliteBossScreenController',
|
|
180
|
+
goal: 'asset_refs',
|
|
181
|
+
});
|
|
182
|
+
assert.equal(typeof mockGraphAddRelationship, 'function');
|
|
183
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function parseUssSelectors(content) {
|
|
2
|
+
const out = [];
|
|
3
|
+
const lines = content.split(/\r?\n/);
|
|
4
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
5
|
+
const line = lines[i];
|
|
6
|
+
if (!line.includes('{'))
|
|
7
|
+
continue;
|
|
8
|
+
const selectorChunk = line.split('{', 1)[0];
|
|
9
|
+
const rawSelectors = selectorChunk.split(',').map((item) => item.trim()).filter(Boolean);
|
|
10
|
+
for (const selector of rawSelectors) {
|
|
11
|
+
if (!selector.startsWith('.'))
|
|
12
|
+
continue;
|
|
13
|
+
out.push({
|
|
14
|
+
selector,
|
|
15
|
+
line: i + 1,
|
|
16
|
+
snippet: line.trim(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseUssSelectors } from './uss-selector-parser.js';
|
|
7
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const fixtureRoot = path.resolve(here, '../../../src/core/unity/__fixtures__/mini-unity-ui');
|
|
9
|
+
test('extracts uss selectors with line evidence', async () => {
|
|
10
|
+
const source = await fs.readFile(path.join(fixtureRoot, 'Assets/UI/Styles/EliteBossScreenNew.uss'), 'utf-8');
|
|
11
|
+
const selectors = parseUssSelectors(source);
|
|
12
|
+
assert.equal(selectors.some((entry) => entry.selector === '.tooltip-box' && entry.line > 0), true);
|
|
13
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UxmlRefEvidence {
|
|
2
|
+
guid: string;
|
|
3
|
+
line: number;
|
|
4
|
+
snippet: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ParsedUxmlRefs {
|
|
7
|
+
templates: UxmlRefEvidence[];
|
|
8
|
+
styles: UxmlRefEvidence[];
|
|
9
|
+
}
|
|
10
|
+
export declare function parseUxmlRefs(content: string): ParsedUxmlRefs;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const GUID_PARAM_PATTERN = /\bguid=([0-9a-f]{32})\b/i;
|
|
2
|
+
export function parseUxmlRefs(content) {
|
|
3
|
+
const templates = [];
|
|
4
|
+
const styles = [];
|
|
5
|
+
const lines = content.split(/\r?\n/);
|
|
6
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
7
|
+
const line = lines[i];
|
|
8
|
+
const guidMatch = line.match(GUID_PARAM_PATTERN);
|
|
9
|
+
if (!guidMatch)
|
|
10
|
+
continue;
|
|
11
|
+
const entry = {
|
|
12
|
+
guid: guidMatch[1].toLowerCase(),
|
|
13
|
+
line: i + 1,
|
|
14
|
+
snippet: line.trim(),
|
|
15
|
+
};
|
|
16
|
+
if (/<\s*(?:[A-Za-z_][\w.-]*\s*:\s*)?Template\b/i.test(line))
|
|
17
|
+
templates.push(entry);
|
|
18
|
+
if (/<\s*(?:[A-Za-z_][\w.-]*\s*:\s*)?Style\b/i.test(line))
|
|
19
|
+
styles.push(entry);
|
|
20
|
+
}
|
|
21
|
+
return { templates, styles };
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseUxmlRefs } from './uxml-ref-parser.js';
|
|
7
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const fixtureRoot = path.resolve(here, '../../../src/core/unity/__fixtures__/mini-unity-ui');
|
|
9
|
+
test('extracts uxml template/style refs with line evidence', async () => {
|
|
10
|
+
const source = await fs.readFile(path.join(fixtureRoot, 'Assets/UI/Screens/EliteBossScreenNew.uxml'), 'utf-8');
|
|
11
|
+
const out = parseUxmlRefs(source);
|
|
12
|
+
assert.equal(out.templates.length > 0, true);
|
|
13
|
+
assert.equal(out.styles.length > 0, true);
|
|
14
|
+
assert.equal(out.templates[0].guid, 'cccccccccccccccccccccccccccccccc');
|
|
15
|
+
assert.equal(out.styles[0].guid, 'dddddddddddddddddddddddddddddddd');
|
|
16
|
+
assert.equal(out.templates[0].line > 0, true);
|
|
17
|
+
assert.equal(out.styles[0].line > 0, true);
|
|
18
|
+
});
|
|
19
|
+
test('supports namespaced ui:Template and ui:Style tags', () => {
|
|
20
|
+
const source = [
|
|
21
|
+
'<ui:UXML xmlns:ui="UnityEngine.UIElements">',
|
|
22
|
+
' <ui:Style src="project://database/Assets/UI/Styles/A.uss?guid=11111111111111111111111111111111&type=3" />',
|
|
23
|
+
' <ui:Template src="project://database/Assets/UI/Components/B.uxml?guid=22222222222222222222222222222222&type=3" />',
|
|
24
|
+
'</ui:UXML>',
|
|
25
|
+
].join('\n');
|
|
26
|
+
const out = parseUxmlRefs(source);
|
|
27
|
+
assert.equal(out.styles.length, 1);
|
|
28
|
+
assert.equal(out.templates.length, 1);
|
|
29
|
+
assert.equal(out.styles[0].guid, '11111111111111111111111111111111');
|
|
30
|
+
assert.equal(out.templates[0].guid, '22222222222222222222222222222222');
|
|
31
|
+
});
|
|
@@ -13,6 +13,18 @@ import { type RegistryEntry } from '../../storage/repo-manager.js';
|
|
|
13
13
|
* Matches common test file patterns across all supported languages.
|
|
14
14
|
*/
|
|
15
15
|
export declare function isTestFilePath(filePath: string): boolean;
|
|
16
|
+
export interface ExpandedSymbolCandidate {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
filePath: string;
|
|
21
|
+
startLine?: number;
|
|
22
|
+
endLine?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function filterBm25ResultsByScopePreset<T extends {
|
|
25
|
+
filePath?: string;
|
|
26
|
+
}>(rows: T[], scopePreset?: string): T[];
|
|
27
|
+
export declare function rankExpandedSymbolsForQuery(symbols: ExpandedSymbolCandidate[], query: string, limit?: number, scopePreset?: string): ExpandedSymbolCandidate[];
|
|
16
28
|
export declare function mergeUnityBindings(baseBindings: ResolvedUnityBinding[], resolvedByPath: Map<string, ResolvedUnityBinding[]>): ResolvedUnityBinding[];
|
|
17
29
|
export declare function mergeParityUnityBindings(baseNonLightweightBindings: ResolvedUnityBinding[], resolvedBindings: ResolvedUnityBinding[]): ResolvedUnityBinding[];
|
|
18
30
|
export declare function attachUnityHydrationMeta(payload: UnityContextPayload, input: Pick<UnityHydrationMeta, 'requestedMode' | 'effectiveMode' | 'elapsedMs' | 'fallbackToCompact'> & {
|
|
@@ -97,6 +109,7 @@ export declare class LocalBackend {
|
|
|
97
109
|
stats?: any;
|
|
98
110
|
}>>;
|
|
99
111
|
callTool(method: string, params: any): Promise<any>;
|
|
112
|
+
private unityUiTrace;
|
|
100
113
|
/**
|
|
101
114
|
* Query tool — process-grouped search.
|
|
102
115
|
*
|