elspais 0.11.1__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.
Files changed (148) hide show
  1. elspais/__init__.py +2 -11
  2. elspais/{sponsors/__init__.py → associates.py} +102 -58
  3. elspais/cli.py +395 -79
  4. elspais/commands/__init__.py +9 -3
  5. elspais/commands/analyze.py +121 -173
  6. elspais/commands/changed.py +15 -30
  7. elspais/commands/config_cmd.py +13 -16
  8. elspais/commands/edit.py +60 -44
  9. elspais/commands/example_cmd.py +319 -0
  10. elspais/commands/hash_cmd.py +167 -183
  11. elspais/commands/health.py +1177 -0
  12. elspais/commands/index.py +98 -114
  13. elspais/commands/init.py +103 -26
  14. elspais/commands/reformat_cmd.py +41 -444
  15. elspais/commands/rules_cmd.py +7 -3
  16. elspais/commands/trace.py +444 -321
  17. elspais/commands/validate.py +195 -415
  18. elspais/config/__init__.py +799 -5
  19. elspais/{core/content_rules.py → content_rules.py} +20 -3
  20. elspais/docs/cli/assertions.md +67 -0
  21. elspais/docs/cli/commands.md +304 -0
  22. elspais/docs/cli/config.md +262 -0
  23. elspais/docs/cli/format.md +66 -0
  24. elspais/docs/cli/git.md +45 -0
  25. elspais/docs/cli/health.md +190 -0
  26. elspais/docs/cli/hierarchy.md +60 -0
  27. elspais/docs/cli/ignore.md +72 -0
  28. elspais/docs/cli/mcp.md +245 -0
  29. elspais/docs/cli/quickstart.md +58 -0
  30. elspais/docs/cli/traceability.md +89 -0
  31. elspais/docs/cli/validation.md +96 -0
  32. elspais/graph/GraphNode.py +383 -0
  33. elspais/graph/__init__.py +40 -0
  34. elspais/graph/annotators.py +927 -0
  35. elspais/graph/builder.py +1886 -0
  36. elspais/graph/deserializer.py +248 -0
  37. elspais/graph/factory.py +284 -0
  38. elspais/graph/metrics.py +127 -0
  39. elspais/graph/mutations.py +161 -0
  40. elspais/graph/parsers/__init__.py +156 -0
  41. elspais/graph/parsers/code.py +213 -0
  42. elspais/graph/parsers/comments.py +112 -0
  43. elspais/graph/parsers/config_helpers.py +29 -0
  44. elspais/graph/parsers/heredocs.py +225 -0
  45. elspais/graph/parsers/journey.py +131 -0
  46. elspais/graph/parsers/remainder.py +79 -0
  47. elspais/graph/parsers/requirement.py +347 -0
  48. elspais/graph/parsers/results/__init__.py +6 -0
  49. elspais/graph/parsers/results/junit_xml.py +229 -0
  50. elspais/graph/parsers/results/pytest_json.py +313 -0
  51. elspais/graph/parsers/test.py +305 -0
  52. elspais/graph/relations.py +78 -0
  53. elspais/graph/serialize.py +216 -0
  54. elspais/html/__init__.py +8 -0
  55. elspais/html/generator.py +731 -0
  56. elspais/html/templates/trace_view.html.j2 +2151 -0
  57. elspais/mcp/__init__.py +47 -29
  58. elspais/mcp/__main__.py +5 -1
  59. elspais/mcp/file_mutations.py +138 -0
  60. elspais/mcp/server.py +2016 -247
  61. elspais/testing/__init__.py +4 -4
  62. elspais/testing/config.py +3 -0
  63. elspais/testing/mapper.py +1 -1
  64. elspais/testing/result_parser.py +25 -21
  65. elspais/testing/scanner.py +301 -12
  66. elspais/utilities/__init__.py +1 -0
  67. elspais/utilities/docs_loader.py +115 -0
  68. elspais/utilities/git.py +607 -0
  69. elspais/{core → utilities}/hasher.py +8 -22
  70. elspais/utilities/md_renderer.py +189 -0
  71. elspais/{core → utilities}/patterns.py +58 -57
  72. elspais/utilities/reference_config.py +626 -0
  73. elspais/validation/__init__.py +19 -0
  74. elspais/validation/format.py +264 -0
  75. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
  76. elspais-0.43.5.dist-info/RECORD +80 -0
  77. elspais/config/defaults.py +0 -173
  78. elspais/config/loader.py +0 -494
  79. elspais/core/__init__.py +0 -21
  80. elspais/core/git.py +0 -352
  81. elspais/core/models.py +0 -320
  82. elspais/core/parser.py +0 -640
  83. elspais/core/rules.py +0 -514
  84. elspais/mcp/context.py +0 -171
  85. elspais/mcp/serializers.py +0 -112
  86. elspais/reformat/__init__.py +0 -50
  87. elspais/reformat/detector.py +0 -119
  88. elspais/reformat/hierarchy.py +0 -246
  89. elspais/reformat/line_breaks.py +0 -220
  90. elspais/reformat/prompts.py +0 -123
  91. elspais/reformat/transformer.py +0 -264
  92. elspais/trace_view/__init__.py +0 -54
  93. elspais/trace_view/coverage.py +0 -183
  94. elspais/trace_view/generators/__init__.py +0 -12
  95. elspais/trace_view/generators/base.py +0 -329
  96. elspais/trace_view/generators/csv.py +0 -122
  97. elspais/trace_view/generators/markdown.py +0 -175
  98. elspais/trace_view/html/__init__.py +0 -31
  99. elspais/trace_view/html/generator.py +0 -1006
  100. elspais/trace_view/html/templates/base.html +0 -283
  101. elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
  102. elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
  103. elspais/trace_view/html/templates/components/legend_modal.html +0 -69
  104. elspais/trace_view/html/templates/components/review_panel.html +0 -118
  105. elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
  106. elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
  107. elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
  108. elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
  109. elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
  110. elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
  111. elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
  112. elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
  113. elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
  114. elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
  115. elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
  116. elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
  117. elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
  118. elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
  119. elspais/trace_view/html/templates/partials/scripts.js +0 -1741
  120. elspais/trace_view/html/templates/partials/styles.css +0 -1756
  121. elspais/trace_view/models.py +0 -353
  122. elspais/trace_view/review/__init__.py +0 -60
  123. elspais/trace_view/review/branches.py +0 -1149
  124. elspais/trace_view/review/models.py +0 -1205
  125. elspais/trace_view/review/position.py +0 -609
  126. elspais/trace_view/review/server.py +0 -1056
  127. elspais/trace_view/review/status.py +0 -470
  128. elspais/trace_view/review/storage.py +0 -1367
  129. elspais/trace_view/scanning.py +0 -213
  130. elspais/trace_view/specs/README.md +0 -84
  131. elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
  132. elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
  133. elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
  134. elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
  135. elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
  136. elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
  137. elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
  138. elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
  139. elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
  140. elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
  141. elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
  142. elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
  143. elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
  144. elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
  145. elspais-0.11.1.dist-info/RECORD +0 -101
  146. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
  147. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
  148. {elspais-0.11.1.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;