elspais 0.11.2__py3-none-any.whl → 0.43.5__py3-none-any.whl
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.
- elspais/__init__.py +1 -10
- elspais/{sponsors/__init__.py → associates.py} +102 -56
- elspais/cli.py +366 -69
- elspais/commands/__init__.py +9 -3
- elspais/commands/analyze.py +118 -169
- elspais/commands/changed.py +12 -23
- elspais/commands/config_cmd.py +10 -13
- elspais/commands/edit.py +33 -13
- elspais/commands/example_cmd.py +319 -0
- elspais/commands/hash_cmd.py +161 -183
- elspais/commands/health.py +1177 -0
- elspais/commands/index.py +98 -115
- elspais/commands/init.py +99 -22
- elspais/commands/reformat_cmd.py +41 -433
- elspais/commands/rules_cmd.py +2 -2
- elspais/commands/trace.py +443 -324
- elspais/commands/validate.py +193 -411
- elspais/config/__init__.py +799 -5
- elspais/{core/content_rules.py → content_rules.py} +20 -2
- elspais/docs/cli/assertions.md +67 -0
- elspais/docs/cli/commands.md +304 -0
- elspais/docs/cli/config.md +262 -0
- elspais/docs/cli/format.md +66 -0
- elspais/docs/cli/git.md +45 -0
- elspais/docs/cli/health.md +190 -0
- elspais/docs/cli/hierarchy.md +60 -0
- elspais/docs/cli/ignore.md +72 -0
- elspais/docs/cli/mcp.md +245 -0
- elspais/docs/cli/quickstart.md +58 -0
- elspais/docs/cli/traceability.md +89 -0
- elspais/docs/cli/validation.md +96 -0
- elspais/graph/GraphNode.py +383 -0
- elspais/graph/__init__.py +40 -0
- elspais/graph/annotators.py +927 -0
- elspais/graph/builder.py +1886 -0
- elspais/graph/deserializer.py +248 -0
- elspais/graph/factory.py +284 -0
- elspais/graph/metrics.py +127 -0
- elspais/graph/mutations.py +161 -0
- elspais/graph/parsers/__init__.py +156 -0
- elspais/graph/parsers/code.py +213 -0
- elspais/graph/parsers/comments.py +112 -0
- elspais/graph/parsers/config_helpers.py +29 -0
- elspais/graph/parsers/heredocs.py +225 -0
- elspais/graph/parsers/journey.py +131 -0
- elspais/graph/parsers/remainder.py +79 -0
- elspais/graph/parsers/requirement.py +347 -0
- elspais/graph/parsers/results/__init__.py +6 -0
- elspais/graph/parsers/results/junit_xml.py +229 -0
- elspais/graph/parsers/results/pytest_json.py +313 -0
- elspais/graph/parsers/test.py +305 -0
- elspais/graph/relations.py +78 -0
- elspais/graph/serialize.py +216 -0
- elspais/html/__init__.py +8 -0
- elspais/html/generator.py +731 -0
- elspais/html/templates/trace_view.html.j2 +2151 -0
- elspais/mcp/__init__.py +45 -29
- elspais/mcp/__main__.py +5 -1
- elspais/mcp/file_mutations.py +138 -0
- elspais/mcp/server.py +1998 -244
- elspais/testing/__init__.py +3 -3
- elspais/testing/config.py +3 -0
- elspais/testing/mapper.py +1 -1
- elspais/testing/scanner.py +301 -12
- elspais/utilities/__init__.py +1 -0
- elspais/utilities/docs_loader.py +115 -0
- elspais/utilities/git.py +607 -0
- elspais/{core → utilities}/hasher.py +8 -22
- elspais/utilities/md_renderer.py +189 -0
- elspais/{core → utilities}/patterns.py +56 -51
- elspais/utilities/reference_config.py +626 -0
- elspais/validation/__init__.py +19 -0
- elspais/validation/format.py +264 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
- elspais-0.43.5.dist-info/RECORD +80 -0
- elspais/config/defaults.py +0 -179
- elspais/config/loader.py +0 -494
- elspais/core/__init__.py +0 -21
- elspais/core/git.py +0 -346
- elspais/core/models.py +0 -320
- elspais/core/parser.py +0 -639
- elspais/core/rules.py +0 -509
- elspais/mcp/context.py +0 -172
- elspais/mcp/serializers.py +0 -112
- elspais/reformat/__init__.py +0 -50
- elspais/reformat/detector.py +0 -112
- elspais/reformat/hierarchy.py +0 -247
- elspais/reformat/line_breaks.py +0 -218
- elspais/reformat/prompts.py +0 -133
- elspais/reformat/transformer.py +0 -266
- elspais/trace_view/__init__.py +0 -55
- elspais/trace_view/coverage.py +0 -183
- elspais/trace_view/generators/__init__.py +0 -12
- elspais/trace_view/generators/base.py +0 -334
- elspais/trace_view/generators/csv.py +0 -118
- elspais/trace_view/generators/markdown.py +0 -170
- elspais/trace_view/html/__init__.py +0 -33
- elspais/trace_view/html/generator.py +0 -1140
- elspais/trace_view/html/templates/base.html +0 -283
- elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
- elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
- elspais/trace_view/html/templates/components/legend_modal.html +0 -69
- elspais/trace_view/html/templates/components/review_panel.html +0 -118
- elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
- elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
- elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
- elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
- elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
- elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
- elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
- elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
- elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
- elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
- elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
- elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
- elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
- elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
- elspais/trace_view/html/templates/partials/scripts.js +0 -1741
- elspais/trace_view/html/templates/partials/styles.css +0 -1756
- elspais/trace_view/models.py +0 -378
- elspais/trace_view/review/__init__.py +0 -63
- elspais/trace_view/review/branches.py +0 -1142
- elspais/trace_view/review/models.py +0 -1200
- elspais/trace_view/review/position.py +0 -591
- elspais/trace_view/review/server.py +0 -1032
- elspais/trace_view/review/status.py +0 -455
- elspais/trace_view/review/storage.py +0 -1343
- elspais/trace_view/scanning.py +0 -213
- elspais/trace_view/specs/README.md +0 -84
- elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
- elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
- elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
- elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
- elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
- elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
- elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
- elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
- elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
- elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
- elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
- elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
- elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
- elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
- elspais-0.11.2.dist-info/RECORD +0 -101
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,540 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TraceView Review Position Highlighting Module
|
|
3
|
-
*
|
|
4
|
-
* Client-side position resolution and highlighting.
|
|
5
|
-
* Handles exact, approximate, and unanchored positions.
|
|
6
|
-
*
|
|
7
|
-
* IMPLEMENTS REQUIREMENTS:
|
|
8
|
-
* REQ-tv-d00016: Review JavaScript Integration
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Ensure TraceView.review namespace exists
|
|
12
|
-
window.TraceView = window.TraceView || {};
|
|
13
|
-
TraceView.review = TraceView.review || {};
|
|
14
|
-
|
|
15
|
-
(function(review) {
|
|
16
|
-
'use strict';
|
|
17
|
-
|
|
18
|
-
// ==========================================================================
|
|
19
|
-
// Constants
|
|
20
|
-
// ==========================================================================
|
|
21
|
-
|
|
22
|
-
review.Confidence = Object.freeze({
|
|
23
|
-
EXACT: 'exact',
|
|
24
|
-
APPROXIMATE: 'approximate',
|
|
25
|
-
UNANCHORED: 'unanchored'
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// CSS classes for highlighting
|
|
29
|
-
review.HighlightClass = {
|
|
30
|
-
EXACT: 'rs-highlight-exact',
|
|
31
|
-
APPROXIMATE: 'rs-highlight-approximate',
|
|
32
|
-
UNANCHORED: 'rs-highlight-unanchored',
|
|
33
|
-
ACTIVE: 'rs-highlight-active'
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// ==========================================================================
|
|
37
|
-
// Resolved Position
|
|
38
|
-
// ==========================================================================
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Result of position resolution
|
|
42
|
-
*/
|
|
43
|
-
class ResolvedPosition {
|
|
44
|
-
constructor(data) {
|
|
45
|
-
this.originalPosition = data.originalPosition;
|
|
46
|
-
this.confidence = data.confidence;
|
|
47
|
-
this.lineNumber = data.lineNumber || null;
|
|
48
|
-
this.lineRange = data.lineRange || null;
|
|
49
|
-
this.charStart = data.charStart || null;
|
|
50
|
-
this.charEnd = data.charEnd || null;
|
|
51
|
-
this.resolvedType = data.resolvedType;
|
|
52
|
-
this.message = data.message || null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if position is exact match
|
|
57
|
-
* @returns {boolean}
|
|
58
|
-
*/
|
|
59
|
-
isExact() {
|
|
60
|
-
return this.confidence === review.Confidence.EXACT;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Check if position is approximate
|
|
65
|
-
* @returns {boolean}
|
|
66
|
-
*/
|
|
67
|
-
isApproximate() {
|
|
68
|
-
return this.confidence === review.Confidence.APPROXIMATE;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Check if position is unanchored
|
|
73
|
-
* @returns {boolean}
|
|
74
|
-
*/
|
|
75
|
-
isUnanchored() {
|
|
76
|
-
return this.confidence === review.Confidence.UNANCHORED;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get CSS class for highlighting
|
|
81
|
-
* @returns {string}
|
|
82
|
-
*/
|
|
83
|
-
getHighlightClass() {
|
|
84
|
-
switch (this.confidence) {
|
|
85
|
-
case review.Confidence.EXACT: return review.HighlightClass.EXACT;
|
|
86
|
-
case review.Confidence.APPROXIMATE: return review.HighlightClass.APPROXIMATE;
|
|
87
|
-
default: return review.HighlightClass.UNANCHORED;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
review.ResolvedPosition = ResolvedPosition;
|
|
92
|
-
|
|
93
|
-
// ==========================================================================
|
|
94
|
-
// Position Resolution Functions
|
|
95
|
-
// ==========================================================================
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get total number of lines in text
|
|
99
|
-
* @param {string} text - Text content
|
|
100
|
-
* @returns {number} Line count
|
|
101
|
-
*/
|
|
102
|
-
function getTotalLines(text) {
|
|
103
|
-
if (!text) return 0;
|
|
104
|
-
return text.split('\n').length;
|
|
105
|
-
}
|
|
106
|
-
review.getTotalLines = getTotalLines;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Get line number from character offset
|
|
110
|
-
* @param {string} text - Text content
|
|
111
|
-
* @param {number} offset - Character offset
|
|
112
|
-
* @returns {number} 1-based line number
|
|
113
|
-
*/
|
|
114
|
-
function getLineNumberFromCharOffset(text, offset) {
|
|
115
|
-
if (!text || offset < 0) return 1;
|
|
116
|
-
const before = text.substring(0, offset);
|
|
117
|
-
return before.split('\n').length;
|
|
118
|
-
}
|
|
119
|
-
review.getLineNumberFromCharOffset = getLineNumberFromCharOffset;
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get character range for a line
|
|
123
|
-
* @param {string} text - Text content
|
|
124
|
-
* @param {number} lineNum - 1-based line number
|
|
125
|
-
* @returns {Object} {start, end} character offsets
|
|
126
|
-
*/
|
|
127
|
-
function getLineCharRange(text, lineNum) {
|
|
128
|
-
if (!text || lineNum < 1) return { start: 0, end: 0 };
|
|
129
|
-
|
|
130
|
-
const lines = text.split('\n');
|
|
131
|
-
if (lineNum > lines.length) return { start: text.length, end: text.length };
|
|
132
|
-
|
|
133
|
-
let start = 0;
|
|
134
|
-
for (let i = 0; i < lineNum - 1; i++) {
|
|
135
|
-
start += lines[i].length + 1; // +1 for newline
|
|
136
|
-
}
|
|
137
|
-
const end = start + lines[lineNum - 1].length;
|
|
138
|
-
|
|
139
|
-
return { start, end };
|
|
140
|
-
}
|
|
141
|
-
review.getLineCharRange = getLineCharRange;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Find line number containing search text
|
|
145
|
-
* @param {string} text - Text content
|
|
146
|
-
* @param {string} search - Text to find
|
|
147
|
-
* @returns {number|null} 1-based line number or null
|
|
148
|
-
*/
|
|
149
|
-
function findLineInText(text, search) {
|
|
150
|
-
if (!text || !search) return null;
|
|
151
|
-
|
|
152
|
-
const idx = text.indexOf(search);
|
|
153
|
-
if (idx === -1) return null;
|
|
154
|
-
|
|
155
|
-
return getLineNumberFromCharOffset(text, idx);
|
|
156
|
-
}
|
|
157
|
-
review.findLineInText = findLineInText;
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Find context string position in text
|
|
161
|
-
* @param {string} text - Text content
|
|
162
|
-
* @param {string} context - Context to find
|
|
163
|
-
* @returns {Object|null} {start, end, line} or null
|
|
164
|
-
*/
|
|
165
|
-
function findContextInText(text, context) {
|
|
166
|
-
if (!text || !context) return null;
|
|
167
|
-
|
|
168
|
-
const idx = text.indexOf(context);
|
|
169
|
-
if (idx === -1) return null;
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
start: idx,
|
|
173
|
-
end: idx + context.length,
|
|
174
|
-
line: getLineNumberFromCharOffset(text, idx)
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
review.findContextInText = findContextInText;
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Find nth occurrence of keyword in text
|
|
181
|
-
* @param {string} text - Text content
|
|
182
|
-
* @param {string} keyword - Keyword to find
|
|
183
|
-
* @param {number} occurrence - 1-based occurrence index
|
|
184
|
-
* @returns {Object|null} {start, end, line} or null
|
|
185
|
-
*/
|
|
186
|
-
function findKeywordOccurrence(text, keyword, occurrence) {
|
|
187
|
-
if (!text || !keyword || occurrence < 1) return null;
|
|
188
|
-
|
|
189
|
-
let count = 0;
|
|
190
|
-
let idx = -1;
|
|
191
|
-
|
|
192
|
-
while (count < occurrence) {
|
|
193
|
-
idx = text.indexOf(keyword, idx + 1);
|
|
194
|
-
if (idx === -1) return null;
|
|
195
|
-
count++;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
start: idx,
|
|
200
|
-
end: idx + keyword.length,
|
|
201
|
-
line: getLineNumberFromCharOffset(text, idx)
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
review.findKeywordOccurrence = findKeywordOccurrence;
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Resolve a comment position against current content
|
|
208
|
-
* @param {CommentPosition} position - Position to resolve
|
|
209
|
-
* @param {string} currentContent - Current REQ content
|
|
210
|
-
* @param {string} currentHash - Current content hash
|
|
211
|
-
* @returns {ResolvedPosition} Resolved position
|
|
212
|
-
*/
|
|
213
|
-
function resolvePosition(position, currentContent, currentHash) {
|
|
214
|
-
// Check for exact match
|
|
215
|
-
if (position.hashWhenCreated === currentHash) {
|
|
216
|
-
return resolveExact(position, currentContent);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Hash mismatch - try fallback strategies
|
|
220
|
-
return resolveWithFallback(position, currentContent);
|
|
221
|
-
}
|
|
222
|
-
review.resolvePosition = resolvePosition;
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Resolve position with exact hash match
|
|
226
|
-
* @param {CommentPosition} position - Position
|
|
227
|
-
* @param {string} content - Content
|
|
228
|
-
* @returns {ResolvedPosition}
|
|
229
|
-
*/
|
|
230
|
-
function resolveExact(position, content) {
|
|
231
|
-
const totalLines = getTotalLines(content);
|
|
232
|
-
|
|
233
|
-
switch (position.type) {
|
|
234
|
-
case review.PositionType.LINE: {
|
|
235
|
-
if (position.lineNumber > totalLines) {
|
|
236
|
-
return new ResolvedPosition({
|
|
237
|
-
originalPosition: position,
|
|
238
|
-
confidence: review.Confidence.UNANCHORED,
|
|
239
|
-
resolvedType: 'general',
|
|
240
|
-
message: 'Line number out of range'
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
const range = getLineCharRange(content, position.lineNumber);
|
|
244
|
-
return new ResolvedPosition({
|
|
245
|
-
originalPosition: position,
|
|
246
|
-
confidence: review.Confidence.EXACT,
|
|
247
|
-
lineNumber: position.lineNumber,
|
|
248
|
-
charStart: range.start,
|
|
249
|
-
charEnd: range.end,
|
|
250
|
-
resolvedType: 'line'
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
case review.PositionType.BLOCK: {
|
|
255
|
-
const [startLine, endLine] = position.lineRange;
|
|
256
|
-
if (startLine > totalLines) {
|
|
257
|
-
return new ResolvedPosition({
|
|
258
|
-
originalPosition: position,
|
|
259
|
-
confidence: review.Confidence.UNANCHORED,
|
|
260
|
-
resolvedType: 'general',
|
|
261
|
-
message: 'Block start out of range'
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
const actualEnd = Math.min(endLine, totalLines);
|
|
265
|
-
const startRange = getLineCharRange(content, startLine);
|
|
266
|
-
const endRange = getLineCharRange(content, actualEnd);
|
|
267
|
-
return new ResolvedPosition({
|
|
268
|
-
originalPosition: position,
|
|
269
|
-
confidence: review.Confidence.EXACT,
|
|
270
|
-
lineRange: [startLine, actualEnd],
|
|
271
|
-
charStart: startRange.start,
|
|
272
|
-
charEnd: endRange.end,
|
|
273
|
-
resolvedType: 'block'
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
case review.PositionType.WORD: {
|
|
278
|
-
const found = findKeywordOccurrence(
|
|
279
|
-
content,
|
|
280
|
-
position.keyword,
|
|
281
|
-
position.keywordOccurrence || 1
|
|
282
|
-
);
|
|
283
|
-
if (!found) {
|
|
284
|
-
return new ResolvedPosition({
|
|
285
|
-
originalPosition: position,
|
|
286
|
-
confidence: review.Confidence.UNANCHORED,
|
|
287
|
-
resolvedType: 'general',
|
|
288
|
-
message: 'Keyword not found'
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
return new ResolvedPosition({
|
|
292
|
-
originalPosition: position,
|
|
293
|
-
confidence: review.Confidence.EXACT,
|
|
294
|
-
lineNumber: found.line,
|
|
295
|
-
charStart: found.start,
|
|
296
|
-
charEnd: found.end,
|
|
297
|
-
resolvedType: 'word'
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
case review.PositionType.GENERAL:
|
|
302
|
-
default:
|
|
303
|
-
return new ResolvedPosition({
|
|
304
|
-
originalPosition: position,
|
|
305
|
-
confidence: review.Confidence.EXACT,
|
|
306
|
-
resolvedType: 'general'
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Resolve position with fallback strategies (hash mismatch)
|
|
313
|
-
* @param {CommentPosition} position - Position
|
|
314
|
-
* @param {string} content - Content
|
|
315
|
-
* @returns {ResolvedPosition}
|
|
316
|
-
*/
|
|
317
|
-
function resolveWithFallback(position, content) {
|
|
318
|
-
const totalLines = getTotalLines(content);
|
|
319
|
-
|
|
320
|
-
// Strategy 1: Try line number if available and in range
|
|
321
|
-
let lineToTry = position.lineNumber;
|
|
322
|
-
if (lineToTry === null && position.lineRange) {
|
|
323
|
-
lineToTry = position.lineRange[0];
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (lineToTry !== null && lineToTry <= totalLines) {
|
|
327
|
-
const range = getLineCharRange(content, lineToTry);
|
|
328
|
-
return new ResolvedPosition({
|
|
329
|
-
originalPosition: position,
|
|
330
|
-
confidence: review.Confidence.APPROXIMATE,
|
|
331
|
-
lineNumber: lineToTry,
|
|
332
|
-
charStart: range.start,
|
|
333
|
-
charEnd: range.end,
|
|
334
|
-
resolvedType: 'line',
|
|
335
|
-
message: 'Position approximated from line number'
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Strategy 2: Try fallback context
|
|
340
|
-
if (position.fallbackContext) {
|
|
341
|
-
const found = findContextInText(content, position.fallbackContext);
|
|
342
|
-
if (found) {
|
|
343
|
-
return new ResolvedPosition({
|
|
344
|
-
originalPosition: position,
|
|
345
|
-
confidence: review.Confidence.APPROXIMATE,
|
|
346
|
-
lineNumber: found.line,
|
|
347
|
-
charStart: found.start,
|
|
348
|
-
charEnd: found.end,
|
|
349
|
-
resolvedType: 'context',
|
|
350
|
-
message: 'Position found via context string'
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Strategy 3: Try keyword search
|
|
356
|
-
if (position.keyword) {
|
|
357
|
-
const found = findKeywordOccurrence(
|
|
358
|
-
content,
|
|
359
|
-
position.keyword,
|
|
360
|
-
position.keywordOccurrence || 1
|
|
361
|
-
);
|
|
362
|
-
if (found) {
|
|
363
|
-
return new ResolvedPosition({
|
|
364
|
-
originalPosition: position,
|
|
365
|
-
confidence: review.Confidence.APPROXIMATE,
|
|
366
|
-
lineNumber: found.line,
|
|
367
|
-
charStart: found.start,
|
|
368
|
-
charEnd: found.end,
|
|
369
|
-
resolvedType: 'word',
|
|
370
|
-
message: 'Position found via keyword'
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// All fallbacks failed - return general
|
|
376
|
-
return new ResolvedPosition({
|
|
377
|
-
originalPosition: position,
|
|
378
|
-
confidence: review.Confidence.UNANCHORED,
|
|
379
|
-
resolvedType: 'general',
|
|
380
|
-
message: 'Could not resolve position'
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// ==========================================================================
|
|
385
|
-
// DOM Highlighting
|
|
386
|
-
// ==========================================================================
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Highlight a resolved position in a content element
|
|
390
|
-
* @param {Element} element - Content element
|
|
391
|
-
* @param {ResolvedPosition} position - Resolved position
|
|
392
|
-
* @param {string} threadId - Thread ID for data attribute
|
|
393
|
-
*/
|
|
394
|
-
function highlightPosition(element, position, threadId) {
|
|
395
|
-
if (!element || position.isUnanchored()) {
|
|
396
|
-
// For general/unanchored, highlight whole element border
|
|
397
|
-
if (element) {
|
|
398
|
-
element.classList.add('rs-has-comments');
|
|
399
|
-
}
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const content = element.textContent || element.innerText;
|
|
404
|
-
|
|
405
|
-
if (position.charStart !== null && position.charEnd !== null) {
|
|
406
|
-
// Wrap the specific range with a highlight span
|
|
407
|
-
highlightCharRange(element, position.charStart, position.charEnd,
|
|
408
|
-
position.getHighlightClass(), threadId);
|
|
409
|
-
} else if (position.lineNumber !== null) {
|
|
410
|
-
// Highlight whole line
|
|
411
|
-
highlightLine(element, position.lineNumber,
|
|
412
|
-
position.getHighlightClass(), threadId);
|
|
413
|
-
} else if (position.lineRange !== null) {
|
|
414
|
-
// Highlight line range
|
|
415
|
-
for (let i = position.lineRange[0]; i <= position.lineRange[1]; i++) {
|
|
416
|
-
highlightLine(element, i, position.getHighlightClass(), threadId);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
review.highlightPosition = highlightPosition;
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Highlight a character range in an element
|
|
424
|
-
* @param {Element} element - Content element
|
|
425
|
-
* @param {number} start - Start offset
|
|
426
|
-
* @param {number} end - End offset
|
|
427
|
-
* @param {string} className - CSS class
|
|
428
|
-
* @param {string} threadId - Thread ID
|
|
429
|
-
*/
|
|
430
|
-
function highlightCharRange(element, start, end, className, threadId) {
|
|
431
|
-
const text = element.textContent || element.innerText;
|
|
432
|
-
if (start >= text.length || end <= start) return;
|
|
433
|
-
|
|
434
|
-
const before = text.substring(0, start);
|
|
435
|
-
const highlight = text.substring(start, end);
|
|
436
|
-
const after = text.substring(end);
|
|
437
|
-
|
|
438
|
-
const span = document.createElement('span');
|
|
439
|
-
span.className = className;
|
|
440
|
-
span.textContent = highlight;
|
|
441
|
-
if (threadId) {
|
|
442
|
-
span.setAttribute('data-thread-id', threadId);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
element.innerHTML = '';
|
|
446
|
-
element.appendChild(document.createTextNode(before));
|
|
447
|
-
element.appendChild(span);
|
|
448
|
-
element.appendChild(document.createTextNode(after));
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Highlight a specific line in an element
|
|
453
|
-
* @param {Element} element - Content element (assuming line-based structure)
|
|
454
|
-
* @param {number} lineNum - 1-based line number
|
|
455
|
-
* @param {string} className - CSS class
|
|
456
|
-
* @param {string} threadId - Thread ID
|
|
457
|
-
*/
|
|
458
|
-
function highlightLine(element, lineNum, className, threadId) {
|
|
459
|
-
// Look for line-based children or create wrapper
|
|
460
|
-
const lines = element.querySelectorAll('.req-line, .code-line, [data-line]');
|
|
461
|
-
|
|
462
|
-
if (lines.length > 0) {
|
|
463
|
-
// Use existing line elements
|
|
464
|
-
if (lineNum <= lines.length) {
|
|
465
|
-
const line = lines[lineNum - 1];
|
|
466
|
-
line.classList.add(className);
|
|
467
|
-
if (threadId) {
|
|
468
|
-
line.setAttribute('data-thread-id', threadId);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
} else {
|
|
472
|
-
// Fallback: wrap line in content
|
|
473
|
-
const text = element.textContent || element.innerText;
|
|
474
|
-
const textLines = text.split('\n');
|
|
475
|
-
|
|
476
|
-
if (lineNum <= textLines.length) {
|
|
477
|
-
element.innerHTML = '';
|
|
478
|
-
textLines.forEach((line, i) => {
|
|
479
|
-
const lineEl = document.createElement('div');
|
|
480
|
-
lineEl.className = 'req-line';
|
|
481
|
-
lineEl.setAttribute('data-line', String(i + 1));
|
|
482
|
-
lineEl.textContent = line;
|
|
483
|
-
|
|
484
|
-
if (i + 1 === lineNum) {
|
|
485
|
-
lineEl.classList.add(className);
|
|
486
|
-
if (threadId) {
|
|
487
|
-
lineEl.setAttribute('data-thread-id', threadId);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
element.appendChild(lineEl);
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Clear all highlights from an element
|
|
499
|
-
* @param {Element} element - Content element
|
|
500
|
-
*/
|
|
501
|
-
function clearHighlights(element) {
|
|
502
|
-
if (!element) return;
|
|
503
|
-
|
|
504
|
-
// Remove highlight classes
|
|
505
|
-
element.classList.remove('rs-has-comments');
|
|
506
|
-
const highlighted = element.querySelectorAll(
|
|
507
|
-
`.${review.HighlightClass.EXACT}, .${review.HighlightClass.APPROXIMATE}, ` +
|
|
508
|
-
`.${review.HighlightClass.UNANCHORED}, .${review.HighlightClass.ACTIVE}`
|
|
509
|
-
);
|
|
510
|
-
highlighted.forEach(el => {
|
|
511
|
-
el.classList.remove(
|
|
512
|
-
review.HighlightClass.EXACT,
|
|
513
|
-
review.HighlightClass.APPROXIMATE,
|
|
514
|
-
review.HighlightClass.UNANCHORED,
|
|
515
|
-
review.HighlightClass.ACTIVE
|
|
516
|
-
);
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
review.clearHighlights = clearHighlights;
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Activate a specific highlight (make it stand out)
|
|
523
|
-
* @param {string} threadId - Thread ID to activate
|
|
524
|
-
*/
|
|
525
|
-
function activateHighlight(threadId) {
|
|
526
|
-
// Deactivate all first
|
|
527
|
-
document.querySelectorAll('.' + review.HighlightClass.ACTIVE).forEach(el => {
|
|
528
|
-
el.classList.remove(review.HighlightClass.ACTIVE);
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// Activate matching
|
|
532
|
-
if (threadId) {
|
|
533
|
-
document.querySelectorAll(`[data-thread-id="${threadId}"]`).forEach(el => {
|
|
534
|
-
el.classList.add(review.HighlightClass.ACTIVE);
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
review.activateHighlight = activateHighlight;
|
|
539
|
-
|
|
540
|
-
})(TraceView.review);
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TraceView Review Resize Module
|
|
3
|
-
* Handle resizable review panel.
|
|
4
|
-
* IMPLEMENTS REQUIREMENTS: REQ-d00092
|
|
5
|
-
*/
|
|
6
|
-
window.TraceView = window.TraceView || {};
|
|
7
|
-
TraceView.review = TraceView.review || {};
|
|
8
|
-
|
|
9
|
-
(function(review) {
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
const MIN_WIDTH = 200;
|
|
13
|
-
const MAX_WIDTH = 600;
|
|
14
|
-
const DEFAULT_WIDTH = 350;
|
|
15
|
-
const STORAGE_KEY = 'traceview_review_panel_width';
|
|
16
|
-
|
|
17
|
-
let isResizing = false;
|
|
18
|
-
let startX, startWidth;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Initialize resize functionality
|
|
22
|
-
*/
|
|
23
|
-
function initResize() {
|
|
24
|
-
const handle = document.getElementById('reviewResizeHandle');
|
|
25
|
-
const column = document.getElementById('review-column');
|
|
26
|
-
|
|
27
|
-
if (!handle || !column) return;
|
|
28
|
-
|
|
29
|
-
// Restore saved width
|
|
30
|
-
const savedWidth = localStorage.getItem(STORAGE_KEY);
|
|
31
|
-
if (savedWidth) {
|
|
32
|
-
column.style.width = savedWidth + 'px';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
handle.addEventListener('mousedown', startResize);
|
|
36
|
-
document.addEventListener('mousemove', doResize);
|
|
37
|
-
document.addEventListener('mouseup', stopResize);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Start resize operation
|
|
42
|
-
* @param {MouseEvent} e - Mouse event
|
|
43
|
-
*/
|
|
44
|
-
function startResize(e) {
|
|
45
|
-
const column = document.getElementById('review-column');
|
|
46
|
-
if (!column) return;
|
|
47
|
-
|
|
48
|
-
isResizing = true;
|
|
49
|
-
startX = e.clientX;
|
|
50
|
-
startWidth = column.offsetWidth;
|
|
51
|
-
|
|
52
|
-
document.body.style.cursor = 'col-resize';
|
|
53
|
-
document.body.style.userSelect = 'none';
|
|
54
|
-
e.preventDefault();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Handle resize movement
|
|
59
|
-
* @param {MouseEvent} e - Mouse event
|
|
60
|
-
*/
|
|
61
|
-
function doResize(e) {
|
|
62
|
-
if (!isResizing) return;
|
|
63
|
-
|
|
64
|
-
const column = document.getElementById('review-column');
|
|
65
|
-
if (!column) return;
|
|
66
|
-
|
|
67
|
-
// Panel is on the right, so dragging left increases width
|
|
68
|
-
const diff = startX - e.clientX;
|
|
69
|
-
let newWidth = startWidth + diff;
|
|
70
|
-
|
|
71
|
-
// Clamp to min/max bounds
|
|
72
|
-
newWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, newWidth));
|
|
73
|
-
column.style.width = newWidth + 'px';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Stop resize operation
|
|
78
|
-
*/
|
|
79
|
-
function stopResize() {
|
|
80
|
-
if (!isResizing) return;
|
|
81
|
-
|
|
82
|
-
isResizing = false;
|
|
83
|
-
document.body.style.cursor = '';
|
|
84
|
-
document.body.style.userSelect = '';
|
|
85
|
-
|
|
86
|
-
// Save width to localStorage
|
|
87
|
-
const column = document.getElementById('review-column');
|
|
88
|
-
if (column) {
|
|
89
|
-
localStorage.setItem(STORAGE_KEY, column.offsetWidth);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Enable or disable resize functionality
|
|
95
|
-
* @param {boolean} enable - Whether to enable resizing
|
|
96
|
-
*/
|
|
97
|
-
function resize(enable) {
|
|
98
|
-
if (enable) {
|
|
99
|
-
initResize();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Auto-initialize on DOMContentLoaded
|
|
104
|
-
document.addEventListener('DOMContentLoaded', initResize);
|
|
105
|
-
|
|
106
|
-
// Export to review namespace
|
|
107
|
-
review.resize = resize;
|
|
108
|
-
review.initResize = initResize;
|
|
109
|
-
|
|
110
|
-
})(TraceView.review);
|
|
111
|
-
|
|
112
|
-
// Update ReviewSystem alias
|
|
113
|
-
window.ReviewSystem = window.ReviewSystem || {};
|
|
114
|
-
window.ReviewSystem.resize = TraceView.review.resize;
|
|
115
|
-
window.ReviewSystem.initResize = TraceView.review.initResize;
|