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,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
- };