spec-up-t 1.4.1-beta.3 ā 1.5.0
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.
- package/assets/compiled/body.js +2 -2
- package/assets/compiled/head.css +2 -2
- package/assets/css/embedded-libraries/bootstrap.min.css +1 -1
- package/assets/css/header-navbar.css +4 -4
- package/assets/js/github-issues.js +3 -3
- package/examples/read-console-messages.js +102 -0
- package/gulpfile.js +42 -1
- package/index.js +49 -1
- package/package.json +2 -1
- package/src/health-check.js +47 -629
- package/src/install-from-boilerplate/config-scripts-keys.js +1 -1
- package/src/markdown-it/README.md +2 -14
- package/src/markdown-it/index.js +1 -7
- package/src/parsers/template-tag-parser.js +19 -3
- package/src/pipeline/postprocessing/definition-list-postprocessor.js +4 -2
- package/src/pipeline/references/collect-external-references.js +65 -8
- package/src/pipeline/references/external-references-service.js +55 -10
- package/src/pipeline/references/fetch-terms-from-index.js +62 -1
- package/src/pipeline/references/process-xtrefs-data.js +34 -0
- package/src/pipeline/references/xtref-utils.js +22 -3
- package/src/pipeline/rendering/render-spec-document.js +0 -1
- package/src/run-healthcheck.js +177 -0
- package/src/utils/logger.js +15 -1
- package/src/utils/message-collector.js +144 -0
- package/templates/template.html +6 -6
- package/test/message-collector.test.js +286 -0
- package/src/markdown-it/link-enhancement.js +0 -98
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check Integration Script for spec-up-t
|
|
3
|
+
*
|
|
4
|
+
* This script integrates the spec-up-t-healthcheck tool to validate consuming projects.
|
|
5
|
+
* It runs FROM spec-up-t (menu option 7) but checks the CONSUMING project (current directory).
|
|
6
|
+
*
|
|
7
|
+
* Usage (from consuming project):
|
|
8
|
+
* npm run healthCheck
|
|
9
|
+
*
|
|
10
|
+
* Or via spec-up-t menu:
|
|
11
|
+
* npm run menu -> [7] Run health check
|
|
12
|
+
*
|
|
13
|
+
* Options:
|
|
14
|
+
* --format text|json|html Output format (default: html)
|
|
15
|
+
* --output <file> Output file path
|
|
16
|
+
* --checks <checks> Comma-separated list of specific checks
|
|
17
|
+
*
|
|
18
|
+
* Examples:
|
|
19
|
+
* node run-healthcheck.js
|
|
20
|
+
* node run-healthcheck.js --format json
|
|
21
|
+
* node run-healthcheck.js --format html --output health-report.html
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs').promises;
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse command line arguments
|
|
29
|
+
* Handles both direct node invocations and npm script invocations
|
|
30
|
+
*
|
|
31
|
+
* @returns {Object} Parsed arguments
|
|
32
|
+
*/
|
|
33
|
+
function parseArgs() {
|
|
34
|
+
const args = process.argv.slice(2);
|
|
35
|
+
const options = {
|
|
36
|
+
format: 'html', // Changed default to HTML for better user experience
|
|
37
|
+
output: null,
|
|
38
|
+
checks: null
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < args.length; i++) {
|
|
42
|
+
if (args[i] === '--format' && args[i + 1]) {
|
|
43
|
+
options.format = args[i + 1];
|
|
44
|
+
i++;
|
|
45
|
+
} else if (args[i] === '--output' && args[i + 1]) {
|
|
46
|
+
options.output = args[i + 1];
|
|
47
|
+
i++;
|
|
48
|
+
} else if (args[i] === '--checks' && args[i + 1]) {
|
|
49
|
+
options.checks = args[i + 1].split(',');
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return options;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Main execution function
|
|
59
|
+
*
|
|
60
|
+
* Runs health checks on the CONSUMING PROJECT (current working directory).
|
|
61
|
+
* This script is part of spec-up-t but validates the project that uses spec-up-t.
|
|
62
|
+
*/
|
|
63
|
+
async function main() {
|
|
64
|
+
try {
|
|
65
|
+
console.log('š„ Spec-Up-T Health Check\n');
|
|
66
|
+
console.log('š Checking consuming project in:', process.cwd());
|
|
67
|
+
console.log('');
|
|
68
|
+
|
|
69
|
+
// Parse command line arguments
|
|
70
|
+
const options = parseArgs();
|
|
71
|
+
|
|
72
|
+
// Import the health check tool
|
|
73
|
+
const {
|
|
74
|
+
createProvider,
|
|
75
|
+
runHealthChecks,
|
|
76
|
+
formatResultsAsText,
|
|
77
|
+
formatResultsAsJson,
|
|
78
|
+
formatResultsAsHtml
|
|
79
|
+
} = await import('spec-up-t-healthcheck');
|
|
80
|
+
|
|
81
|
+
// Create a provider for the CURRENT WORKING DIRECTORY (the consuming project)
|
|
82
|
+
const provider = createProvider(process.cwd());
|
|
83
|
+
|
|
84
|
+
// Run health checks on the consuming project
|
|
85
|
+
console.log('š Running health checks on consuming project...');
|
|
86
|
+
const healthCheckOptions = {};
|
|
87
|
+
if (options.checks) {
|
|
88
|
+
healthCheckOptions.checks = options.checks;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const results = await runHealthChecks(provider, healthCheckOptions);
|
|
92
|
+
|
|
93
|
+
// Display summary
|
|
94
|
+
console.log('\nš Health Check Summary:');
|
|
95
|
+
console.log(` Total Checks: ${results.summary.total}`);
|
|
96
|
+
console.log(` ā
Passed: ${results.summary.passed}`);
|
|
97
|
+
console.log(` ā Failed: ${results.summary.failed}`);
|
|
98
|
+
console.log(` ā ļø Warnings: ${results.summary.warnings}`);
|
|
99
|
+
console.log(` āļø Skipped: ${results.summary.skipped}`);
|
|
100
|
+
console.log(` š Health Score: ${results.summary.score}%\n`);
|
|
101
|
+
|
|
102
|
+
// Format results based on requested format
|
|
103
|
+
let output;
|
|
104
|
+
let defaultFilename;
|
|
105
|
+
|
|
106
|
+
switch (options.format) {
|
|
107
|
+
case 'json':
|
|
108
|
+
output = formatResultsAsJson(results);
|
|
109
|
+
defaultFilename = 'health-report.json';
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'html':
|
|
113
|
+
output = formatResultsAsHtml(results, {
|
|
114
|
+
title: 'Spec-Up-T Project Health Check',
|
|
115
|
+
repositoryUrl: results.provider.repoPath ?
|
|
116
|
+
`file://${results.provider.repoPath}` : undefined
|
|
117
|
+
});
|
|
118
|
+
defaultFilename = 'health-report.html';
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
case 'text':
|
|
122
|
+
default:
|
|
123
|
+
output = formatResultsAsText(results);
|
|
124
|
+
defaultFilename = 'health-report.txt';
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Determine output path
|
|
129
|
+
const outputPath = options.output || defaultFilename;
|
|
130
|
+
|
|
131
|
+
// Save to file
|
|
132
|
+
await fs.writeFile(outputPath, output);
|
|
133
|
+
console.log(`š Report saved to: ${outputPath}`);
|
|
134
|
+
|
|
135
|
+
// For HTML format, try to open in browser
|
|
136
|
+
if (options.format === 'html' && !options.output) {
|
|
137
|
+
try {
|
|
138
|
+
const { openHtmlFile } = require('./utils/file-opener');
|
|
139
|
+
await openHtmlFile(path.resolve(outputPath));
|
|
140
|
+
console.log('š Opening report in browser...');
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.log('ā¹ļø Tip: Open the report manually in your browser');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// For text format, also print to console
|
|
147
|
+
if (options.format === 'text') {
|
|
148
|
+
console.log('\n' + output);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Exit with appropriate code
|
|
152
|
+
if (results.summary.hasErrors) {
|
|
153
|
+
console.error('\nā Health check completed with errors');
|
|
154
|
+
console.error(' Please review the report for details');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
} else if (results.summary.hasWarnings) {
|
|
157
|
+
console.warn('\nā ļø Health check completed with warnings');
|
|
158
|
+
process.exit(0);
|
|
159
|
+
} else {
|
|
160
|
+
console.log('\nā
Health check completed successfully');
|
|
161
|
+
console.log(' All checks passed!');
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('\nā Health check failed:', error.message);
|
|
167
|
+
console.error('\nDetails:', error.stack);
|
|
168
|
+
console.error('\nTroubleshooting:');
|
|
169
|
+
console.error(' - Ensure you are running this from a consuming project directory');
|
|
170
|
+
console.error(' - Check that spec-up-t-healthcheck is properly installed');
|
|
171
|
+
console.error(' - Verify the consuming project has a valid package.json');
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Run the script
|
|
177
|
+
main();
|
package/src/utils/logger.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
+
const messageCollector = require('./message-collector');
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Logger utility with color-coded console output
|
|
5
6
|
* Provides consistent logging across the spec-up-t application
|
|
7
|
+
*
|
|
8
|
+
* All messages are automatically collected when message collection is active,
|
|
9
|
+
* allowing healthchecks and other tools to consume the output in JSON format.
|
|
6
10
|
*/
|
|
7
11
|
class Logger {
|
|
8
12
|
/**
|
|
@@ -10,6 +14,7 @@ class Logger {
|
|
|
10
14
|
*/
|
|
11
15
|
static success(message, ...args) {
|
|
12
16
|
console.log(chalk.green('ā
'), chalk.green(message), ...args);
|
|
17
|
+
messageCollector.addMessage('success', message, args);
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -17,6 +22,7 @@ class Logger {
|
|
|
17
22
|
*/
|
|
18
23
|
static error(message, ...args) {
|
|
19
24
|
console.log(chalk.red('ā'), chalk.red(message), ...args);
|
|
25
|
+
messageCollector.addMessage('error', message, args);
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
/**
|
|
@@ -24,6 +30,7 @@ class Logger {
|
|
|
24
30
|
*/
|
|
25
31
|
static warn(message, ...args) {
|
|
26
32
|
console.log(chalk.yellow('š”'), chalk.yellow(message), ...args);
|
|
33
|
+
messageCollector.addMessage('warn', message, args);
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
/**
|
|
@@ -31,6 +38,7 @@ class Logger {
|
|
|
31
38
|
*/
|
|
32
39
|
static info(message, ...args) {
|
|
33
40
|
console.log(chalk.blue('š'), chalk.blue(message), ...args);
|
|
41
|
+
messageCollector.addMessage('info', message, args);
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
/**
|
|
@@ -38,6 +46,7 @@ class Logger {
|
|
|
38
46
|
*/
|
|
39
47
|
static process(message, ...args) {
|
|
40
48
|
console.log(chalk.cyan('š'), chalk.cyan(message), ...args);
|
|
49
|
+
messageCollector.addMessage('process', message, args);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
/**
|
|
@@ -45,6 +54,7 @@ class Logger {
|
|
|
45
54
|
*/
|
|
46
55
|
static debug(message, ...args) {
|
|
47
56
|
console.log(chalk.gray('š'), chalk.gray(message), ...args);
|
|
57
|
+
messageCollector.addMessage('debug', message, args);
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
@@ -52,6 +62,7 @@ class Logger {
|
|
|
52
62
|
*/
|
|
53
63
|
static highlight(message, ...args) {
|
|
54
64
|
console.log(chalk.blue('āØ'), chalk.blue(message), ...args);
|
|
65
|
+
messageCollector.addMessage('highlight', message, args);
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
/**
|
|
@@ -59,6 +70,7 @@ class Logger {
|
|
|
59
70
|
*/
|
|
60
71
|
static separator() {
|
|
61
72
|
console.log(chalk.gray('ā'.repeat(60)));
|
|
73
|
+
messageCollector.addMessage('separator', 'ā'.repeat(60), []);
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
/**
|
|
@@ -67,7 +79,9 @@ class Logger {
|
|
|
67
79
|
static progress(current, total, message) {
|
|
68
80
|
const percentage = Math.round((current / total) * 100);
|
|
69
81
|
const bar = 'ā'.repeat(Math.floor(percentage / 5)) + 'ā'.repeat(20 - Math.floor(percentage / 5));
|
|
70
|
-
|
|
82
|
+
const progressMessage = `[${bar}] ${percentage}% ${message}`;
|
|
83
|
+
console.log(chalk.cyan(`š ${progressMessage}`));
|
|
84
|
+
messageCollector.addMessage('progress', progressMessage, [current, total]);
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Message collector for capturing console output from menu operations
|
|
3
|
+
*
|
|
4
|
+
* This module wraps the Logger to intercept and store all console messages
|
|
5
|
+
* produced during menu [1] (render) and menu [4] (collect external references)
|
|
6
|
+
* operations, storing them in JSON format for consumption by healthchecks.
|
|
7
|
+
*
|
|
8
|
+
* The captured messages are stored in `.cache/console-messages.json` with
|
|
9
|
+
* structured metadata including:
|
|
10
|
+
* - timestamp: ISO timestamp of when the message was logged
|
|
11
|
+
* - type: success, error, warn, info, process, highlight, debug, separator
|
|
12
|
+
* - message: the actual message text
|
|
13
|
+
* - operation: which menu operation triggered this (render or collectExternalReferences)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs-extra');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Message collection state
|
|
21
|
+
*/
|
|
22
|
+
const messageStore = {
|
|
23
|
+
messages: [],
|
|
24
|
+
currentOperation: null,
|
|
25
|
+
isCollecting: false
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Start collecting messages for a specific operation
|
|
30
|
+
* @param {string} operation - Operation name ('render' or 'collectExternalReferences')
|
|
31
|
+
*/
|
|
32
|
+
function startCollecting(operation) {
|
|
33
|
+
messageStore.isCollecting = true;
|
|
34
|
+
messageStore.currentOperation = operation;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Stop collecting messages
|
|
39
|
+
*/
|
|
40
|
+
function stopCollecting() {
|
|
41
|
+
messageStore.isCollecting = false;
|
|
42
|
+
messageStore.currentOperation = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Add a message to the collection
|
|
47
|
+
* @param {string} type - Message type (success, error, warn, etc.)
|
|
48
|
+
* @param {string} message - Message text
|
|
49
|
+
* @param {Array} args - Additional arguments passed to logger
|
|
50
|
+
*/
|
|
51
|
+
function addMessage(type, message, args = []) {
|
|
52
|
+
if (!messageStore.isCollecting) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const messageEntry = {
|
|
57
|
+
timestamp: new Date().toISOString(),
|
|
58
|
+
type,
|
|
59
|
+
message: String(message),
|
|
60
|
+
operation: messageStore.currentOperation,
|
|
61
|
+
additionalData: args.length > 0 ? args.map(arg => String(arg)) : undefined
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
messageStore.messages.push(messageEntry);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Save collected messages to JSON file
|
|
69
|
+
* Each run creates a fresh file, replacing any existing messages.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} [outputPath] - Optional custom output path
|
|
72
|
+
* @returns {Promise<string>} Path to the saved file
|
|
73
|
+
*/
|
|
74
|
+
async function saveMessages(outputPath) {
|
|
75
|
+
const cacheDir = path.join(process.cwd(), '.cache');
|
|
76
|
+
const defaultPath = path.join(cacheDir, 'console-messages.json');
|
|
77
|
+
const filePath = outputPath || defaultPath;
|
|
78
|
+
|
|
79
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
80
|
+
|
|
81
|
+
const output = {
|
|
82
|
+
metadata: {
|
|
83
|
+
generatedAt: new Date().toISOString(),
|
|
84
|
+
totalMessages: messageStore.messages.length,
|
|
85
|
+
operations: [...new Set(messageStore.messages.map(m => m.operation))],
|
|
86
|
+
messagesByType: messageStore.messages.reduce((acc, msg) => {
|
|
87
|
+
acc[msg.type] = (acc[msg.type] || 0) + 1;
|
|
88
|
+
return acc;
|
|
89
|
+
}, {})
|
|
90
|
+
},
|
|
91
|
+
messages: messageStore.messages
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
await fs.writeJson(filePath, output, { spaces: 2 });
|
|
95
|
+
|
|
96
|
+
return filePath;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Clear all collected messages
|
|
101
|
+
*/
|
|
102
|
+
function clearMessages() {
|
|
103
|
+
messageStore.messages = [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get current messages (without saving)
|
|
108
|
+
* @returns {Array} Array of message objects
|
|
109
|
+
*/
|
|
110
|
+
function getMessages() {
|
|
111
|
+
return [...messageStore.messages];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get statistics about collected messages
|
|
116
|
+
* @returns {Object} Statistics object
|
|
117
|
+
*/
|
|
118
|
+
function getStatistics() {
|
|
119
|
+
return {
|
|
120
|
+
total: messageStore.messages.length,
|
|
121
|
+
byType: messageStore.messages.reduce((acc, msg) => {
|
|
122
|
+
acc[msg.type] = (acc[msg.type] || 0) + 1;
|
|
123
|
+
return acc;
|
|
124
|
+
}, {}),
|
|
125
|
+
byOperation: messageStore.messages.reduce((acc, msg) => {
|
|
126
|
+
if (msg.operation) {
|
|
127
|
+
acc[msg.operation] = (acc[msg.operation] || 0) + 1;
|
|
128
|
+
}
|
|
129
|
+
return acc;
|
|
130
|
+
}, {}),
|
|
131
|
+
isCollecting: messageStore.isCollecting,
|
|
132
|
+
currentOperation: messageStore.currentOperation
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
startCollecting,
|
|
138
|
+
stopCollecting,
|
|
139
|
+
addMessage,
|
|
140
|
+
saveMessages,
|
|
141
|
+
clearMessages,
|
|
142
|
+
getMessages,
|
|
143
|
+
getStatistics
|
|
144
|
+
};
|
package/templates/template.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<meta name="author" content="${author}">
|
|
9
9
|
<meta property="spec-up-t:github-repo-info" content="${githubRepoInfo}">
|
|
10
10
|
<link rel="icon" href="${specFavicon}" type="image/x-icon">
|
|
11
|
-
<meta name="generator" content="Spec-Up-T"
|
|
11
|
+
<meta name="generator" content="Spec-Up-T">
|
|
12
12
|
<title>${title}</title>
|
|
13
13
|
|
|
14
14
|
${assetsHead}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.min.css" rel="stylesheet">
|
|
20
20
|
</head>
|
|
21
21
|
|
|
22
|
-
<body
|
|
22
|
+
<body>
|
|
23
23
|
<!-- Skip to content link for accessibility -->
|
|
24
24
|
<a href="#content" class="screen-reader-text">Skip to content</a>
|
|
25
25
|
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
</button>
|
|
129
129
|
|
|
130
130
|
<a id="logo" href="${specLogoLink}">
|
|
131
|
-
<img class="d-print-none m-1" src="${specLogo}" alt=""
|
|
131
|
+
<img class="d-print-none m-1" src="${specLogo}" alt="">
|
|
132
132
|
</a>
|
|
133
133
|
|
|
134
134
|
<!-- Spacer to push the following elements to the right -->
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
<div class="d-flex align-items-center service-menu d-print-none">
|
|
139
139
|
|
|
140
140
|
<!-- Settings side menu -->
|
|
141
|
-
<button id="repo_settings"
|
|
141
|
+
<button id="repo_settings" class="btn btn-sm btn-outline-secondary ms-2" type="button"
|
|
142
142
|
data-bs-toggle="offcanvas" data-bs-target="#offcanvasSettings" aria-controls="offcanvasSettings">
|
|
143
143
|
<svg class="bi" width="1em" height="1em" aria-hidden="true">
|
|
144
144
|
<use href="#gear-wide-connected"></use>
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
|
|
250
250
|
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasSettings" aria-labelledby="offcanvasSettingsLabel">
|
|
251
251
|
<div class="offcanvas-header">
|
|
252
|
-
<h5 class="offcanvas-title mt-0" id="
|
|
252
|
+
<h5 class="offcanvas-title mt-0" id="offcanvasSettingsLabel">Settings</h5>
|
|
253
253
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
|
254
254
|
</div>
|
|
255
255
|
<div class="offcanvas-body p-0">
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
aria-label="Enter GitHub Token">
|
|
260
260
|
Enter GitHub Token
|
|
261
261
|
</button>
|
|
262
|
-
<button id="repo_issues" issue-count animate class="btn btn-menu-item m-0 mb-1 border-start-0 border-end-0"
|
|
262
|
+
<button id="repo_issues" data-issue-count data-animate class="btn btn-menu-item m-0 mb-1 border-start-0 border-end-0"
|
|
263
263
|
type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasIssues"
|
|
264
264
|
aria-controls="offcanvasIssues">
|
|
265
265
|
Issues
|