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.
Files changed (147) hide show
  1. elspais/__init__.py +1 -10
  2. elspais/{sponsors/__init__.py → associates.py} +102 -56
  3. elspais/cli.py +366 -69
  4. elspais/commands/__init__.py +9 -3
  5. elspais/commands/analyze.py +118 -169
  6. elspais/commands/changed.py +12 -23
  7. elspais/commands/config_cmd.py +10 -13
  8. elspais/commands/edit.py +33 -13
  9. elspais/commands/example_cmd.py +319 -0
  10. elspais/commands/hash_cmd.py +161 -183
  11. elspais/commands/health.py +1177 -0
  12. elspais/commands/index.py +98 -115
  13. elspais/commands/init.py +99 -22
  14. elspais/commands/reformat_cmd.py +41 -433
  15. elspais/commands/rules_cmd.py +2 -2
  16. elspais/commands/trace.py +443 -324
  17. elspais/commands/validate.py +193 -411
  18. elspais/config/__init__.py +799 -5
  19. elspais/{core/content_rules.py → content_rules.py} +20 -2
  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 +45 -29
  58. elspais/mcp/__main__.py +5 -1
  59. elspais/mcp/file_mutations.py +138 -0
  60. elspais/mcp/server.py +1998 -244
  61. elspais/testing/__init__.py +3 -3
  62. elspais/testing/config.py +3 -0
  63. elspais/testing/mapper.py +1 -1
  64. elspais/testing/scanner.py +301 -12
  65. elspais/utilities/__init__.py +1 -0
  66. elspais/utilities/docs_loader.py +115 -0
  67. elspais/utilities/git.py +607 -0
  68. elspais/{core → utilities}/hasher.py +8 -22
  69. elspais/utilities/md_renderer.py +189 -0
  70. elspais/{core → utilities}/patterns.py +56 -51
  71. elspais/utilities/reference_config.py +626 -0
  72. elspais/validation/__init__.py +19 -0
  73. elspais/validation/format.py +264 -0
  74. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
  75. elspais-0.43.5.dist-info/RECORD +80 -0
  76. elspais/config/defaults.py +0 -179
  77. elspais/config/loader.py +0 -494
  78. elspais/core/__init__.py +0 -21
  79. elspais/core/git.py +0 -346
  80. elspais/core/models.py +0 -320
  81. elspais/core/parser.py +0 -639
  82. elspais/core/rules.py +0 -509
  83. elspais/mcp/context.py +0 -172
  84. elspais/mcp/serializers.py +0 -112
  85. elspais/reformat/__init__.py +0 -50
  86. elspais/reformat/detector.py +0 -112
  87. elspais/reformat/hierarchy.py +0 -247
  88. elspais/reformat/line_breaks.py +0 -218
  89. elspais/reformat/prompts.py +0 -133
  90. elspais/reformat/transformer.py +0 -266
  91. elspais/trace_view/__init__.py +0 -55
  92. elspais/trace_view/coverage.py +0 -183
  93. elspais/trace_view/generators/__init__.py +0 -12
  94. elspais/trace_view/generators/base.py +0 -334
  95. elspais/trace_view/generators/csv.py +0 -118
  96. elspais/trace_view/generators/markdown.py +0 -170
  97. elspais/trace_view/html/__init__.py +0 -33
  98. elspais/trace_view/html/generator.py +0 -1140
  99. elspais/trace_view/html/templates/base.html +0 -283
  100. elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
  101. elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
  102. elspais/trace_view/html/templates/components/legend_modal.html +0 -69
  103. elspais/trace_view/html/templates/components/review_panel.html +0 -118
  104. elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
  105. elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
  106. elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
  107. elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
  108. elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
  109. elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
  110. elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
  111. elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
  112. elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
  113. elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
  114. elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
  115. elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
  116. elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
  117. elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
  118. elspais/trace_view/html/templates/partials/scripts.js +0 -1741
  119. elspais/trace_view/html/templates/partials/styles.css +0 -1756
  120. elspais/trace_view/models.py +0 -378
  121. elspais/trace_view/review/__init__.py +0 -63
  122. elspais/trace_view/review/branches.py +0 -1142
  123. elspais/trace_view/review/models.py +0 -1200
  124. elspais/trace_view/review/position.py +0 -591
  125. elspais/trace_view/review/server.py +0 -1032
  126. elspais/trace_view/review/status.py +0 -455
  127. elspais/trace_view/review/storage.py +0 -1343
  128. elspais/trace_view/scanning.py +0 -213
  129. elspais/trace_view/specs/README.md +0 -84
  130. elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
  131. elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
  132. elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
  133. elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
  134. elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
  135. elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
  136. elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
  137. elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
  138. elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
  139. elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
  140. elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
  141. elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
  142. elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
  143. elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
  144. elspais-0.11.2.dist-info/RECORD +0 -101
  145. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
  146. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
  147. {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;