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,679 +0,0 @@
1
- /**
2
- * TraceView Review Help System
3
- *
4
- * Provides:
5
- * - Contextual tooltips loaded from tooltips.json
6
- * - Onboarding wizard loaded from onboarding.json
7
- * - Help panel loaded from help-panel.json
8
- *
9
- * IMPLEMENTS REQUIREMENTS:
10
- * REQ-tv-d00016: Review JavaScript Integration
11
- */
12
-
13
- // Ensure TraceView.review namespace exists
14
- window.TraceView = window.TraceView || {};
15
- TraceView.review = TraceView.review || {};
16
-
17
- (function(review) {
18
- 'use strict';
19
-
20
- // Help data loaded from JSON files
21
- let tooltipsData = null;
22
- let onboardingData = null;
23
- let helpPanelData = null;
24
-
25
- // State
26
- let onboardingStep = 0;
27
- let helpPanelVisible = false;
28
-
29
- // ==========================================================================
30
- // Data Loading
31
- // ==========================================================================
32
-
33
- /**
34
- * Load help data from JSON files
35
- * @param {string} baseUrl - Base URL for help files
36
- */
37
- async function loadHelpData(baseUrl = '/help') {
38
- try {
39
- const [tooltipsResp, onboardingResp, helpPanelResp] = await Promise.all([
40
- fetch(`${baseUrl}/tooltips.json`),
41
- fetch(`${baseUrl}/onboarding.json`),
42
- fetch(`${baseUrl}/help-panel.json`)
43
- ]);
44
-
45
- if (tooltipsResp.ok) {
46
- tooltipsData = await tooltipsResp.json();
47
- }
48
- if (onboardingResp.ok) {
49
- onboardingData = await onboardingResp.json();
50
- }
51
- if (helpPanelResp.ok) {
52
- helpPanelData = await helpPanelResp.json();
53
- }
54
-
55
- console.log('Help data loaded successfully');
56
- } catch (e) {
57
- console.warn('Failed to load help data:', e);
58
- }
59
- }
60
-
61
- // ==========================================================================
62
- // Tooltips
63
- // ==========================================================================
64
-
65
- /**
66
- * Get tooltip content for an element
67
- * @param {string} helpId - Help ID (e.g., "header.review-mode-toggle")
68
- * @returns {object|null} Tooltip data with title and text
69
- */
70
- function getTooltip(helpId) {
71
- if (!tooltipsData?.tooltips) return null;
72
-
73
- const parts = helpId.split('.');
74
- if (parts.length !== 2) return null;
75
-
76
- const [section, id] = parts;
77
- return tooltipsData.tooltips[section]?.[id] || null;
78
- }
79
-
80
- /**
81
- * Initialize tooltips for elements with data-help-id attribute
82
- * @param {Element} container - Container to search within
83
- */
84
- function initTooltips(container = document) {
85
- container.querySelectorAll('[data-help-id]').forEach(el => {
86
- const helpId = el.getAttribute('data-help-id');
87
- const tooltip = getTooltip(helpId);
88
-
89
- if (tooltip) {
90
- // Set title attribute for native tooltip
91
- el.setAttribute('title', `${tooltip.title}: ${tooltip.text}`);
92
-
93
- // Add help indicator
94
- if (!el.querySelector('.rs-help-indicator')) {
95
- const indicator = document.createElement('span');
96
- indicator.className = 'rs-help-indicator';
97
- indicator.textContent = '?';
98
- indicator.setAttribute('title', tooltip.text);
99
- el.appendChild(indicator);
100
- }
101
- }
102
- });
103
- }
104
-
105
- // ==========================================================================
106
- // Onboarding Wizard
107
- // ==========================================================================
108
-
109
- /**
110
- * Check if onboarding should be shown
111
- * @returns {boolean} True if onboarding should be shown
112
- */
113
- function shouldShowOnboarding() {
114
- if (!onboardingData?.wizard) return false;
115
-
116
- const settings = onboardingData.wizard.settings;
117
- if (!settings.showOnFirstVisit) return false;
118
-
119
- const storageKey = settings.storageKey || 'traceview-review-onboarding-complete';
120
- return localStorage.getItem(storageKey) !== 'true';
121
- }
122
-
123
- /**
124
- * Show onboarding wizard
125
- */
126
- function showOnboarding() {
127
- if (!onboardingData?.wizard) {
128
- console.warn('Onboarding data not loaded');
129
- return;
130
- }
131
-
132
- onboardingStep = 0;
133
- renderOnboardingStep();
134
- }
135
-
136
- /**
137
- * Render current onboarding step
138
- */
139
- function renderOnboardingStep() {
140
- const wizard = onboardingData.wizard;
141
- const steps = wizard.steps;
142
-
143
- // Remove any existing overlay
144
- const existingOverlay = document.querySelector('.rs-onboarding-overlay');
145
- if (existingOverlay) {
146
- existingOverlay.remove();
147
- }
148
-
149
- // Check if completed
150
- if (onboardingStep >= steps.length) {
151
- renderOnboardingCompletion();
152
- return;
153
- }
154
-
155
- const step = steps[onboardingStep];
156
- const overlay = document.createElement('div');
157
- overlay.className = 'rs-onboarding-overlay';
158
-
159
- overlay.innerHTML = `
160
- <div class="rs-onboarding-modal rs-onboarding-${step.position || 'center'}">
161
- <div class="rs-onboarding-header">
162
- <span class="rs-onboarding-step-indicator">
163
- Step ${onboardingStep + 1} of ${steps.length}
164
- </span>
165
- ${wizard.settings.canSkip ? `
166
- <button class="rs-btn rs-btn-link rs-onboarding-skip">Skip Tour</button>
167
- ` : ''}
168
- </div>
169
- <h3 class="rs-onboarding-title">${step.title}</h3>
170
- <div class="rs-onboarding-content">
171
- ${formatMarkdownSimple(step.content)}
172
- </div>
173
- <div class="rs-onboarding-actions">
174
- ${step.buttons.back ? `
175
- <button class="rs-btn rs-onboarding-back">${step.buttons.back}</button>
176
- ` : ''}
177
- ${step.buttons.next ? `
178
- <button class="rs-btn rs-btn-primary rs-onboarding-next">${step.buttons.next}</button>
179
- ` : ''}
180
- </div>
181
- </div>
182
- `;
183
-
184
- document.body.appendChild(overlay);
185
-
186
- // Highlight target element if specified
187
- if (step.highlight) {
188
- const target = document.querySelector(step.highlight);
189
- if (target) {
190
- target.classList.add('rs-onboarding-highlight');
191
- }
192
- }
193
-
194
- // Bind events
195
- overlay.querySelector('.rs-onboarding-next')?.addEventListener('click', nextOnboardingStep);
196
- overlay.querySelector('.rs-onboarding-back')?.addEventListener('click', prevOnboardingStep);
197
- overlay.querySelector('.rs-onboarding-skip')?.addEventListener('click', completeOnboarding);
198
- }
199
-
200
- /**
201
- * Render onboarding completion screen
202
- */
203
- function renderOnboardingCompletion() {
204
- const completion = onboardingData.wizard.completion;
205
- const overlay = document.createElement('div');
206
- overlay.className = 'rs-onboarding-overlay';
207
-
208
- overlay.innerHTML = `
209
- <div class="rs-onboarding-modal rs-onboarding-center">
210
- <h3 class="rs-onboarding-title">${completion.title}</h3>
211
- <div class="rs-onboarding-content">
212
- ${formatMarkdownSimple(completion.content)}
213
- </div>
214
- <div class="rs-onboarding-actions">
215
- <button class="rs-btn rs-btn-primary rs-onboarding-close">${completion.buttons.close}</button>
216
- ${completion.buttons.docs ? `
217
- <button class="rs-btn rs-onboarding-docs">${completion.buttons.docs}</button>
218
- ` : ''}
219
- </div>
220
- </div>
221
- `;
222
-
223
- document.body.appendChild(overlay);
224
-
225
- overlay.querySelector('.rs-onboarding-close')?.addEventListener('click', completeOnboarding);
226
- overlay.querySelector('.rs-onboarding-docs')?.addEventListener('click', () => {
227
- window.open('docs/traceview-review-user-guide.md', '_blank');
228
- completeOnboarding();
229
- });
230
- }
231
-
232
- function nextOnboardingStep() {
233
- clearOnboardingHighlights();
234
- onboardingStep++;
235
- renderOnboardingStep();
236
- }
237
-
238
- function prevOnboardingStep() {
239
- clearOnboardingHighlights();
240
- onboardingStep = Math.max(0, onboardingStep - 1);
241
- renderOnboardingStep();
242
- }
243
-
244
- function completeOnboarding() {
245
- clearOnboardingHighlights();
246
- const overlay = document.querySelector('.rs-onboarding-overlay');
247
- if (overlay) {
248
- overlay.remove();
249
- }
250
-
251
- // Mark as completed
252
- const storageKey = onboardingData.wizard.settings.storageKey || 'traceview-review-onboarding-complete';
253
- localStorage.setItem(storageKey, 'true');
254
- }
255
-
256
- function clearOnboardingHighlights() {
257
- document.querySelectorAll('.rs-onboarding-highlight').forEach(el => {
258
- el.classList.remove('rs-onboarding-highlight');
259
- });
260
- }
261
-
262
- // ==========================================================================
263
- // Help Panel
264
- // ==========================================================================
265
-
266
- /**
267
- * Toggle help panel visibility
268
- */
269
- function toggleHelpPanel() {
270
- if (helpPanelVisible) {
271
- hideHelpPanel();
272
- } else {
273
- showHelpPanel();
274
- }
275
- }
276
-
277
- /**
278
- * Show help panel
279
- */
280
- function showHelpPanel() {
281
- if (!helpPanelData?.helpPanel) {
282
- console.warn('Help panel data not loaded');
283
- return;
284
- }
285
-
286
- // Remove existing panel
287
- const existing = document.querySelector('.rs-help-panel');
288
- if (existing) {
289
- existing.remove();
290
- }
291
-
292
- const panel = helpPanelData.helpPanel;
293
- const panelEl = document.createElement('div');
294
- panelEl.className = 'rs-help-panel';
295
-
296
- let sectionsHtml = panel.sections.map(section => `
297
- <div class="rs-help-section" data-section-id="${section.id}">
298
- <h4 class="rs-help-section-header" ${panel.settings.collapsible ? 'style="cursor: pointer;"' : ''}>
299
- <span>${section.title}</span>
300
- ${panel.settings.collapsible ? '<span class="rs-help-expand-icon">V</span>' : ''}
301
- </h4>
302
- <div class="rs-help-section-content" ${panel.settings.defaultExpanded?.includes(section.id) ? '' : 'style="display: none;"'}>
303
- ${section.items.map(item => `
304
- <div class="rs-help-item" data-item-id="${item.id}">
305
- <div class="rs-help-question">${item.question}</div>
306
- <div class="rs-help-answer">${formatMarkdownSimple(item.answer)}</div>
307
- </div>
308
- `).join('')}
309
- </div>
310
- </div>
311
- `).join('');
312
-
313
- panelEl.innerHTML = `
314
- <div class="rs-help-panel-header">
315
- <h3>${panel.title}</h3>
316
- <button class="rs-btn rs-btn-sm rs-help-close">x</button>
317
- </div>
318
- ${panel.settings.searchable ? `
319
- <div class="rs-help-search">
320
- <input type="text" class="rs-help-search-input" placeholder="Search help...">
321
- </div>
322
- ` : ''}
323
- <div class="rs-help-sections">
324
- ${sectionsHtml}
325
- </div>
326
- <div class="rs-help-footer">
327
- <p>${panel.footer.text}</p>
328
- <div class="rs-help-links">
329
- ${panel.footer.links.map(link => `
330
- <a href="${link.url}" class="rs-help-link">${link.label}</a>
331
- `).join('')}
332
- </div>
333
- </div>
334
- `;
335
-
336
- document.body.appendChild(panelEl);
337
- helpPanelVisible = true;
338
-
339
- // Bind events
340
- panelEl.querySelector('.rs-help-close').addEventListener('click', hideHelpPanel);
341
-
342
- // Collapsible sections
343
- if (panel.settings.collapsible) {
344
- panelEl.querySelectorAll('.rs-help-section-header').forEach(header => {
345
- header.addEventListener('click', () => {
346
- const content = header.nextElementSibling;
347
- const icon = header.querySelector('.rs-help-expand-icon');
348
- if (content.style.display === 'none') {
349
- content.style.display = 'block';
350
- if (icon) icon.textContent = 'V';
351
- } else {
352
- content.style.display = 'none';
353
- if (icon) icon.textContent = '>';
354
- }
355
- });
356
- });
357
- }
358
-
359
- // Search functionality
360
- if (panel.settings.searchable) {
361
- const searchInput = panelEl.querySelector('.rs-help-search-input');
362
- searchInput.addEventListener('input', (e) => {
363
- const query = e.target.value.toLowerCase();
364
- filterHelpItems(panelEl, query);
365
- });
366
- }
367
- }
368
-
369
- /**
370
- * Hide help panel
371
- */
372
- function hideHelpPanel() {
373
- const panel = document.querySelector('.rs-help-panel');
374
- if (panel) {
375
- panel.remove();
376
- }
377
- helpPanelVisible = false;
378
- }
379
-
380
- /**
381
- * Filter help items based on search query
382
- */
383
- function filterHelpItems(panelEl, query) {
384
- panelEl.querySelectorAll('.rs-help-item').forEach(item => {
385
- const question = item.querySelector('.rs-help-question').textContent.toLowerCase();
386
- const answer = item.querySelector('.rs-help-answer').textContent.toLowerCase();
387
-
388
- if (query === '' || question.includes(query) || answer.includes(query)) {
389
- item.style.display = 'block';
390
- } else {
391
- item.style.display = 'none';
392
- }
393
- });
394
-
395
- // Show/hide sections based on whether they have visible items
396
- panelEl.querySelectorAll('.rs-help-section').forEach(section => {
397
- const visibleItems = section.querySelectorAll('.rs-help-item[style="display: block;"], .rs-help-item:not([style])');
398
- const content = section.querySelector('.rs-help-section-content');
399
- if (query && visibleItems.length > 0) {
400
- content.style.display = 'block';
401
- }
402
- });
403
- }
404
-
405
- // ==========================================================================
406
- // Utilities
407
- // ==========================================================================
408
-
409
- /**
410
- * Simple markdown-like formatting
411
- */
412
- function formatMarkdownSimple(text) {
413
- if (!text) return '';
414
-
415
- // Bold
416
- text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
417
-
418
- // Code blocks
419
- text = text.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
420
-
421
- // Inline code
422
- text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
423
-
424
- // Line breaks
425
- text = text.replace(/\n/g, '<br>');
426
-
427
- return text;
428
- }
429
-
430
- /**
431
- * Create help button for header
432
- */
433
- function createHelpButton() {
434
- const btn = document.createElement('button');
435
- btn.className = 'rs-btn rs-btn-sm rs-help-btn';
436
- btn.innerHTML = '?';
437
- btn.title = 'Help';
438
- btn.addEventListener('click', toggleHelpPanel);
439
- return btn;
440
- }
441
-
442
- /**
443
- * Create replay onboarding button
444
- */
445
- function createReplayOnboardingButton() {
446
- const btn = document.createElement('button');
447
- btn.className = 'rs-btn rs-btn-sm rs-replay-onboarding-btn';
448
- btn.textContent = 'Tour';
449
- btn.title = 'Replay onboarding tour';
450
- btn.addEventListener('click', () => {
451
- // Reset onboarding state
452
- const storageKey = onboardingData?.wizard?.settings?.storageKey || 'traceview-review-onboarding-complete';
453
- localStorage.removeItem(storageKey);
454
- showOnboarding();
455
- });
456
- return btn;
457
- }
458
-
459
- // ==========================================================================
460
- // Help Menu
461
- // ==========================================================================
462
-
463
- let helpMenuOpen = false;
464
-
465
- /**
466
- * Create help menu dropdown
467
- * @returns {HTMLElement} The complete help menu container
468
- */
469
- function createHelpMenu() {
470
- const container = document.createElement('div');
471
- container.className = 'rs-help-menu-container';
472
- container.id = 'rs-help-menu';
473
-
474
- container.innerHTML = `
475
- <button class="rs-help-menu-btn" id="rs-help-menu-btn">
476
- <span>? Help</span>
477
- <span class="rs-menu-arrow">V</span>
478
- </button>
479
- <div class="rs-help-menu-dropdown" id="rs-help-menu-dropdown">
480
- <div class="rs-help-menu-section">
481
- <button class="rs-help-menu-item" id="rs-menu-tour">
482
- <span class="rs-menu-icon">[T]</span>
483
- <span class="rs-menu-label">Take Tour</span>
484
- </button>
485
- <button class="rs-help-menu-item" id="rs-menu-help-panel">
486
- <span class="rs-menu-icon">[?]</span>
487
- <span class="rs-menu-label">Help Panel</span>
488
- <span class="rs-menu-shortcut">?</span>
489
- </button>
490
- </div>
491
- <div class="rs-help-menu-section">
492
- <div class="rs-help-menu-section-label">Documentation</div>
493
- <a href="docs/traceview-review-quick-start.md" target="_blank" class="rs-help-menu-item">
494
- <span class="rs-menu-icon">[Q]</span>
495
- <span class="rs-menu-label">Quick Start</span>
496
- </a>
497
- <a href="docs/traceview-review-user-guide.md" target="_blank" class="rs-help-menu-item">
498
- <span class="rs-menu-icon">[U]</span>
499
- <span class="rs-menu-label">User Guide</span>
500
- </a>
501
- </div>
502
- </div>
503
- `;
504
-
505
- // Bind events after DOM is created
506
- const btn = container.querySelector('#rs-help-menu-btn');
507
- const dropdown = container.querySelector('#rs-help-menu-dropdown');
508
-
509
- btn.addEventListener('click', (e) => {
510
- e.stopPropagation();
511
- toggleHelpMenu(btn, dropdown);
512
- });
513
-
514
- container.querySelector('#rs-menu-tour').addEventListener('click', () => {
515
- closeHelpMenu(btn, dropdown);
516
- // Reset onboarding state and show
517
- const storageKey = onboardingData?.wizard?.settings?.storageKey || 'traceview-review-onboarding-complete';
518
- localStorage.removeItem(storageKey);
519
- showOnboarding();
520
- });
521
-
522
- container.querySelector('#rs-menu-help-panel').addEventListener('click', () => {
523
- closeHelpMenu(btn, dropdown);
524
- showHelpPanel();
525
- });
526
-
527
- // Close menu when clicking outside
528
- document.addEventListener('click', (e) => {
529
- if (!container.contains(e.target)) {
530
- closeHelpMenu(btn, dropdown);
531
- }
532
- });
533
-
534
- // Close menu on ESC key
535
- document.addEventListener('keydown', (e) => {
536
- if (e.key === 'Escape' && helpMenuOpen) {
537
- closeHelpMenu(btn, dropdown);
538
- }
539
- });
540
-
541
- return container;
542
- }
543
-
544
- /**
545
- * Toggle help menu open/closed
546
- */
547
- function toggleHelpMenu(btn, dropdown) {
548
- helpMenuOpen = !helpMenuOpen;
549
- btn.classList.toggle('open', helpMenuOpen);
550
- dropdown.classList.toggle('open', helpMenuOpen);
551
- }
552
-
553
- /**
554
- * Close help menu
555
- */
556
- function closeHelpMenu(btn, dropdown) {
557
- helpMenuOpen = false;
558
- btn?.classList.remove('open');
559
- dropdown?.classList.remove('open');
560
- }
561
-
562
- // ==========================================================================
563
- // Initialization
564
- // ==========================================================================
565
-
566
- /**
567
- * Initialize help system
568
- * @param {object} options - Configuration options
569
- */
570
- async function init(options = {}) {
571
- const baseUrl = options.helpBaseUrl || '/help';
572
-
573
- await loadHelpData(baseUrl);
574
-
575
- // Initialize tooltips
576
- initTooltips(document);
577
-
578
- // Show onboarding if first visit
579
- if (shouldShowOnboarding()) {
580
- showOnboarding();
581
- }
582
-
583
- console.log('Help system initialized');
584
- }
585
-
586
- // ==========================================================================
587
- // Exports
588
- // ==========================================================================
589
-
590
- review.help = {
591
- init,
592
- loadHelpData,
593
- initTooltips,
594
- getTooltip,
595
- showOnboarding,
596
- showHelpPanel,
597
- hideHelpPanel,
598
- toggleHelpPanel,
599
- createHelpButton,
600
- createReplayOnboardingButton,
601
- createHelpMenu
602
- };
603
-
604
- })(TraceView.review);
605
-
606
- // Add ReviewSystem alias with help functions (REQ-d00092)
607
- window.ReviewSystem = window.ReviewSystem || {};
608
- var RS = window.ReviewSystem;
609
-
610
- // Help menu toggle from button (for HTML onclick handlers)
611
- function toggleHelpMenuFromBtn(btn) {
612
- const dropdown = document.getElementById('rs-help-menu-dropdown');
613
- const isOpen = dropdown?.classList.toggle('open');
614
- btn?.classList.toggle('open', isOpen);
615
- }
616
-
617
- // Filter help content (for search input)
618
- function filterHelpContent(query) {
619
- const searchQuery = (query || '').toLowerCase();
620
- const panel = document.querySelector('.rs-help-panel');
621
- if (!panel) return;
622
-
623
- panel.querySelectorAll('.rs-help-item').forEach(item => {
624
- const question = item.querySelector('.rs-help-question')?.textContent?.toLowerCase() || '';
625
- const answer = item.querySelector('.rs-help-answer')?.textContent?.toLowerCase() || '';
626
-
627
- if (searchQuery === '' || question.includes(searchQuery) || answer.includes(searchQuery)) {
628
- item.style.display = 'block';
629
- } else {
630
- item.style.display = 'none';
631
- }
632
- });
633
- }
634
-
635
- // Render help panel content
636
- function renderHelpPanelContent(container) {
637
- // Placeholder - content populated dynamically
638
- if (container && TraceView.review.help) {
639
- TraceView.review.help.showHelpPanel();
640
- }
641
- }
642
-
643
- // Setup global help handlers for keyboard shortcuts
644
- function setupGlobalHelpHandlers() {
645
- document.addEventListener('keydown', function(e) {
646
- if (e.key === '?' && !e.target.matches('input, textarea')) {
647
- e.preventDefault();
648
- if (TraceView.review.help) {
649
- TraceView.review.help.toggleHelpPanel();
650
- }
651
- }
652
- if (e.key === 'Escape') {
653
- // Close any open help panels/menus
654
- const dropdown = document.getElementById('rs-help-menu-dropdown');
655
- const btn = document.getElementById('rs-help-menu-btn');
656
- if (dropdown?.classList.contains('open')) {
657
- dropdown.classList.remove('open');
658
- btn?.classList.remove('open');
659
- }
660
- }
661
- });
662
- }
663
-
664
- // Initialize global handlers on DOMContentLoaded
665
- document.addEventListener('DOMContentLoaded', setupGlobalHelpHandlers);
666
-
667
- // Export to RS.help namespace
668
- RS.help = {
669
- init: TraceView.review.help.init,
670
- toggleHelpPanel: TraceView.review.help.toggleHelpPanel,
671
- toggleHelpMenuFromBtn: toggleHelpMenuFromBtn,
672
- startTour: TraceView.review.help.showOnboarding,
673
- filterHelpContent: filterHelpContent,
674
- showOnboarding: TraceView.review.help.showOnboarding,
675
- renderHelpPanelContent: renderHelpPanelContent,
676
- hideHelpPanel: TraceView.review.help.hideHelpPanel,
677
- showHelpPanel: TraceView.review.help.showHelpPanel,
678
- setupGlobalHelpHandlers: setupGlobalHelpHandlers
679
- };