hale-commenting-system 2.2.95 → 3.0.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/package.json +9 -8
- package/scripts/integrate.js +64 -430
- package/src/app/commenting-system/components/CommentOverlay.tsx +4 -3
- package/src/app/commenting-system/components/CommentPanel.tsx +11 -64
- package/src/app/commenting-system/components/FloatingWidget.tsx +105 -39
- package/src/app/commenting-system/contexts/CommentContext.tsx +11 -4
- package/src/app/commenting-system/types/index.ts +1 -0
- package/src/app/routes.tsx +0 -4
- package/tsconfig.json +1 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -23
- package/.github/workflows/ci.yaml +0 -51
- package/src/app/__snapshots__/app.test.tsx.snap +0 -524
- package/src/app/app.test.tsx +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hale-commenting-system",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A commenting system for PatternFly React applications that allows designers and developers to add comments directly on design pages, sync with GitHub Issues, and link Jira tickets.",
|
|
5
5
|
"repository": "https://github.com/patternfly/patternfly-react-seed.git",
|
|
6
6
|
"homepage": "https://www.npmjs.com/package/hale-commenting-system",
|
|
@@ -71,19 +71,20 @@
|
|
|
71
71
|
"webpack-dev-server": "^5.2.1",
|
|
72
72
|
"webpack-merge": "^6.0.1"
|
|
73
73
|
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@patternfly/react-core": "^6.0.0",
|
|
76
|
+
"@patternfly/react-icons": "^6.0.0",
|
|
77
|
+
"react": "^18.0.0",
|
|
78
|
+
"react-dom": "^18.0.0",
|
|
79
|
+
"react-router-dom": "^7.0.0"
|
|
80
|
+
},
|
|
74
81
|
"dependencies": {
|
|
75
82
|
"@babel/generator": "^7.23.0",
|
|
76
83
|
"@babel/parser": "^7.23.0",
|
|
77
84
|
"@babel/traverse": "^7.23.0",
|
|
78
85
|
"@babel/types": "^7.23.0",
|
|
79
|
-
"@patternfly/react-core": "^6.4.0",
|
|
80
|
-
"@patternfly/react-icons": "^6.4.0",
|
|
81
|
-
"@patternfly/react-styles": "^6.4.0",
|
|
82
86
|
"inquirer": "^8.2.6",
|
|
83
|
-
"node-fetch": "^2.7.0"
|
|
84
|
-
"react": "^18",
|
|
85
|
-
"react-dom": "^18",
|
|
86
|
-
"sirv-cli": "^3.0.0"
|
|
87
|
+
"node-fetch": "^2.7.0"
|
|
87
88
|
},
|
|
88
89
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
89
90
|
}
|
package/scripts/integrate.js
CHANGED
|
@@ -512,41 +512,6 @@ JIRA_API_TOKEN=
|
|
|
512
512
|
}
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
-
function createCommentsComponent() {
|
|
516
|
-
const cwd = process.cwd();
|
|
517
|
-
const commentsDir = path.join(cwd, 'src', 'app', 'Comments');
|
|
518
|
-
const commentsFile = path.join(commentsDir, 'Comments.tsx');
|
|
519
|
-
|
|
520
|
-
// Check if already exists
|
|
521
|
-
if (fs.existsSync(commentsFile)) {
|
|
522
|
-
return; // Already exists, skip
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Create directory if it doesn't exist
|
|
526
|
-
if (!fs.existsSync(commentsDir)) {
|
|
527
|
-
fs.mkdirSync(commentsDir, { recursive: true });
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Read the Comments component from the package and modify the import
|
|
531
|
-
// The file is in the package at src/app/Comments/Comments.tsx
|
|
532
|
-
const scriptDir = __dirname || path.dirname(require.resolve('./integrate.js'));
|
|
533
|
-
const packageCommentsFile = path.join(scriptDir, '..', 'src', 'app', 'Comments', 'Comments.tsx');
|
|
534
|
-
|
|
535
|
-
let commentsComponentContent;
|
|
536
|
-
if (fs.existsSync(packageCommentsFile)) {
|
|
537
|
-
// Read from package and replace import path
|
|
538
|
-
commentsComponentContent = fs.readFileSync(packageCommentsFile, 'utf8')
|
|
539
|
-
.replace(/from ['"]@app\/commenting-system['"]/g, "from 'hale-commenting-system'");
|
|
540
|
-
} else {
|
|
541
|
-
// Fallback: create a minimal version (shouldn't happen if package is properly built)
|
|
542
|
-
console.log(' ⚠️ Comments component not found in package, skipping creation');
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
fs.writeFileSync(commentsFile, commentsComponentContent);
|
|
547
|
-
console.log(' ✅ Created Comments component');
|
|
548
|
-
}
|
|
549
|
-
|
|
550
515
|
function integrateWebpackMiddleware() {
|
|
551
516
|
const cwd = process.cwd();
|
|
552
517
|
const webpackDevPath = path.join(cwd, 'webpack.dev.js');
|
|
@@ -1035,19 +1000,13 @@ function modifyIndexTsx(filePath) {
|
|
|
1035
1000
|
}
|
|
1036
1001
|
}
|
|
1037
1002
|
|
|
1038
|
-
function
|
|
1003
|
+
function modifyAppLayoutTsx(filePath) {
|
|
1039
1004
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
1040
|
-
|
|
1041
|
-
// Check if
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
if (content.includes("path: '/comments'") || content.includes('path: "/comments"')) {
|
|
1046
|
-
console.log(' ⚠️ Already integrated (Comments route found)');
|
|
1047
|
-
return false;
|
|
1048
|
-
}
|
|
1049
|
-
// If Comments group exists but has no routes, continue to add them
|
|
1050
|
-
console.log(' ℹ️ Comments group exists but has no routes, adding route...');
|
|
1005
|
+
|
|
1006
|
+
// Check if already integrated
|
|
1007
|
+
if (content.includes('CommentPanel') && content.includes('CommentOverlay')) {
|
|
1008
|
+
console.log(' ⚠️ Already integrated (CommentPanel and CommentOverlay found)');
|
|
1009
|
+
return false;
|
|
1051
1010
|
}
|
|
1052
1011
|
|
|
1053
1012
|
try {
|
|
@@ -1056,392 +1015,87 @@ function modifyRoutesTsx(filePath) {
|
|
|
1056
1015
|
plugins: ['typescript', 'jsx', 'decorators-legacy', 'classProperties']
|
|
1057
1016
|
});
|
|
1058
1017
|
|
|
1059
|
-
let
|
|
1018
|
+
let hasCommentingImport = false;
|
|
1060
1019
|
|
|
1020
|
+
// Check if commenting system imports exist
|
|
1061
1021
|
traverse(ast, {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1022
|
+
ImportDeclaration(path) {
|
|
1023
|
+
const source = path.node.source.value;
|
|
1024
|
+
if (source.includes('hale-commenting-system')) {
|
|
1025
|
+
hasCommentingImport = true;
|
|
1065
1026
|
}
|
|
1066
1027
|
}
|
|
1067
1028
|
});
|
|
1068
1029
|
|
|
1069
|
-
if
|
|
1070
|
-
|
|
1071
|
-
let
|
|
1072
|
-
let
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
const source = path.node.source.value;
|
|
1077
|
-
if (source.includes('Comments') || source.includes('@app/Comments')) {
|
|
1078
|
-
hasCommentsImport = true;
|
|
1079
|
-
// Get the imported name
|
|
1080
|
-
path.node.specifiers.forEach(spec => {
|
|
1081
|
-
if (spec.type === 'ImportSpecifier' && spec.imported.name === 'Comments') {
|
|
1082
|
-
commentsImportName = spec.local.name;
|
|
1083
|
-
}
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
// Add Comments import if missing
|
|
1090
|
-
if (!hasCommentsImport) {
|
|
1091
|
-
let lastImportIndex = -1;
|
|
1092
|
-
for (let i = ast.program.body.length - 1; i >= 0; i--) {
|
|
1093
|
-
if (ast.program.body[i].type === 'ImportDeclaration') {
|
|
1094
|
-
lastImportIndex = i;
|
|
1095
|
-
break;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
const importIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
|
|
1099
|
-
|
|
1100
|
-
const commentsImport = types.importDeclaration(
|
|
1101
|
-
[types.importSpecifier(types.identifier('Comments'), types.identifier('Comments'))],
|
|
1102
|
-
types.stringLiteral('@app/Comments/Comments')
|
|
1103
|
-
);
|
|
1104
|
-
|
|
1105
|
-
ast.program.body.splice(importIndex, 0, commentsImport);
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
// Check if Comments route group already exists
|
|
1109
|
-
let existingCommentsGroup = null;
|
|
1110
|
-
let existingCommentsRoutes = null;
|
|
1111
|
-
|
|
1112
|
-
for (const element of routesArray.elements) {
|
|
1113
|
-
if (element.type === 'ObjectExpression') {
|
|
1114
|
-
const labelProp = element.properties.find(
|
|
1115
|
-
prop => prop.key && prop.key.name === 'label' &&
|
|
1116
|
-
prop.value && prop.value.value === 'Comments'
|
|
1117
|
-
);
|
|
1118
|
-
if (labelProp) {
|
|
1119
|
-
existingCommentsGroup = element;
|
|
1120
|
-
const routesProp = element.properties.find(
|
|
1121
|
-
prop => prop.key && prop.key.name === 'routes'
|
|
1122
|
-
);
|
|
1123
|
-
if (routesProp && routesProp.value.type === 'ArrayExpression') {
|
|
1124
|
-
existingCommentsRoutes = routesProp.value;
|
|
1125
|
-
}
|
|
1126
|
-
break;
|
|
1127
|
-
}
|
|
1030
|
+
// Add imports if missing
|
|
1031
|
+
if (!hasCommentingImport) {
|
|
1032
|
+
let lastImportIndex = -1;
|
|
1033
|
+
for (let i = ast.program.body.length - 1; i >= 0; i--) {
|
|
1034
|
+
if (ast.program.body[i].type === 'ImportDeclaration') {
|
|
1035
|
+
lastImportIndex = i;
|
|
1036
|
+
break;
|
|
1128
1037
|
}
|
|
1129
1038
|
}
|
|
1039
|
+
const importIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
|
|
1130
1040
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1041
|
+
const commentingImport = types.importDeclaration(
|
|
1042
|
+
[
|
|
1043
|
+
types.importSpecifier(types.identifier('CommentPanel'), types.identifier('CommentPanel')),
|
|
1044
|
+
types.importSpecifier(types.identifier('CommentOverlay'), types.identifier('CommentOverlay'))
|
|
1045
|
+
],
|
|
1046
|
+
types.stringLiteral('hale-commenting-system')
|
|
1136
1047
|
);
|
|
1137
1048
|
|
|
1138
|
-
|
|
1139
|
-
types.objectProperty(types.identifier('element'), commentsRouteElement),
|
|
1140
|
-
types.objectProperty(types.identifier('exact'), types.booleanLiteral(true)),
|
|
1141
|
-
types.objectProperty(types.identifier('label'), types.stringLiteral('View all')),
|
|
1142
|
-
types.objectProperty(types.identifier('path'), types.stringLiteral('/comments')),
|
|
1143
|
-
types.objectProperty(types.identifier('title'), types.stringLiteral('Hale Commenting System | Comments'))
|
|
1144
|
-
]);
|
|
1145
|
-
|
|
1146
|
-
if (existingCommentsGroup && existingCommentsRoutes) {
|
|
1147
|
-
// Add route to existing Comments group
|
|
1148
|
-
existingCommentsRoutes.elements.push(commentsRouteItem);
|
|
1149
|
-
} else {
|
|
1150
|
-
// Create new Comments route group
|
|
1151
|
-
const commentsRoute = types.objectExpression([
|
|
1152
|
-
types.objectProperty(types.identifier('label'), types.stringLiteral('Comments')),
|
|
1153
|
-
types.objectProperty(types.identifier('routes'), types.arrayExpression([commentsRouteItem]))
|
|
1154
|
-
]);
|
|
1155
|
-
routesArray.elements.push(commentsRoute);
|
|
1156
|
-
}
|
|
1049
|
+
ast.program.body.splice(importIndex, 0, commentingImport);
|
|
1157
1050
|
}
|
|
1158
1051
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
// Check if already integrated - look for the comprehensive integration
|
|
1176
|
-
if (content.includes('useComments') && content.includes('useGitHubAuth') &&
|
|
1177
|
-
content.includes('setCommentsEnabled') && content.includes('setFloatingWidgetMode')) {
|
|
1178
|
-
console.log(' ⚠️ Already integrated (full commenting system controls found)');
|
|
1179
|
-
return false;
|
|
1180
|
-
}
|
|
1052
|
+
// Find Page component and wrap its children
|
|
1053
|
+
let pageComponentFound = false;
|
|
1054
|
+
traverse(ast, {
|
|
1055
|
+
JSXElement(path) {
|
|
1056
|
+
const openingElement = path.node.openingElement;
|
|
1057
|
+
if (openingElement.name && openingElement.name.name === 'Page') {
|
|
1058
|
+
pageComponentFound = true;
|
|
1059
|
+
const children = path.node.children;
|
|
1060
|
+
|
|
1061
|
+
// Check if already wrapped
|
|
1062
|
+
if (children.length > 0 &&
|
|
1063
|
+
children.some(child =>
|
|
1064
|
+
child.type === 'JSXElement' &&
|
|
1065
|
+
child.openingElement.name.name === 'CommentPanel')) {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1181
1068
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
const patternflyMatch = content.match(patternflyImportRegex);
|
|
1188
|
-
|
|
1189
|
-
if (patternflyMatch) {
|
|
1190
|
-
// Find the import statement
|
|
1191
|
-
const importMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-core['"]/);
|
|
1192
|
-
if (importMatch) {
|
|
1193
|
-
const imports = importMatch[1];
|
|
1194
|
-
// Add Switch if not present
|
|
1195
|
-
if (!imports.includes('Switch')) {
|
|
1196
|
-
const newImports = imports.trim() + ',\n Switch';
|
|
1197
|
-
content = content.replace(
|
|
1198
|
-
/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-core['"]/,
|
|
1199
|
-
`import {${newImports}\n} from '@patternfly/react-core'`
|
|
1069
|
+
// Create CommentOverlay element
|
|
1070
|
+
const commentOverlay = types.jsxElement(
|
|
1071
|
+
types.jsxOpeningElement(types.jsxIdentifier('CommentOverlay'), [], true),
|
|
1072
|
+
null,
|
|
1073
|
+
[]
|
|
1200
1074
|
);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
1075
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
const importMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-icons['"]/);
|
|
1211
|
-
if (importMatch) {
|
|
1212
|
-
const imports = importMatch[1];
|
|
1213
|
-
// Add icons if not present
|
|
1214
|
-
let newImports = imports.trim();
|
|
1215
|
-
if (!imports.includes('ExternalLinkAltIcon')) {
|
|
1216
|
-
newImports += ', ExternalLinkAltIcon';
|
|
1217
|
-
}
|
|
1218
|
-
if (!imports.includes('GithubIcon')) {
|
|
1219
|
-
newImports += ', GithubIcon';
|
|
1220
|
-
}
|
|
1221
|
-
if (newImports !== imports.trim()) {
|
|
1222
|
-
content = content.replace(
|
|
1223
|
-
/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-icons['"]/,
|
|
1224
|
-
`import { ${newImports} } from '@patternfly/react-icons'`
|
|
1076
|
+
// Create CommentPanel wrapping existing children
|
|
1077
|
+
const commentPanel = types.jsxElement(
|
|
1078
|
+
types.jsxOpeningElement(types.jsxIdentifier('CommentPanel'), []),
|
|
1079
|
+
types.jsxClosingElement(types.jsxIdentifier('CommentPanel')),
|
|
1080
|
+
[commentOverlay, ...children]
|
|
1225
1081
|
);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
1082
|
|
|
1230
|
-
|
|
1231
|
-
if (!content.includes('hale-commenting-system')) {
|
|
1232
|
-
// Find where to insert (after other imports)
|
|
1233
|
-
const lastImportMatch = content.match(/import[^;]*;(?=\s*(?:interface|const|export|function))/g);
|
|
1234
|
-
if (lastImportMatch) {
|
|
1235
|
-
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
1236
|
-
const insertPos = content.indexOf(lastImport) + lastImport.length;
|
|
1237
|
-
const commentingImport = `\nimport { CommentOverlay, CommentPanel, useComments, useGitHubAuth } from "hale-commenting-system";`;
|
|
1238
|
-
content = content.slice(0, insertPos) + commentingImport + content.slice(insertPos);
|
|
1239
|
-
}
|
|
1240
|
-
} else {
|
|
1241
|
-
// Update existing import to include all needed items
|
|
1242
|
-
const commentingImportMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+["']hale-commenting-system["']/);
|
|
1243
|
-
if (commentingImportMatch) {
|
|
1244
|
-
const imports = commentingImportMatch[1];
|
|
1245
|
-
let newImports = imports.split(',').map(i => i.trim());
|
|
1246
|
-
|
|
1247
|
-
const needed = ['CommentOverlay', 'CommentPanel', 'useComments', 'useGitHubAuth'];
|
|
1248
|
-
needed.forEach(item => {
|
|
1249
|
-
if (!newImports.includes(item)) {
|
|
1250
|
-
newImports.push(item);
|
|
1251
|
-
}
|
|
1252
|
-
});
|
|
1253
|
-
|
|
1254
|
-
content = content.replace(
|
|
1255
|
-
/import\s+\{[^}]+\}\s+from\s+["']hale-commenting-system["']/,
|
|
1256
|
-
`import { ${newImports.join(', ')} } from "hale-commenting-system"`
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// Step 2: Add hooks to the component
|
|
1262
|
-
// Find the AppLayout function/component
|
|
1263
|
-
const componentMatch = content.match(/(const\s+AppLayout[^=]+=\s*\([^)]*\)\s*=>\s*\{)/);
|
|
1264
|
-
if (componentMatch) {
|
|
1265
|
-
const componentStart = content.indexOf(componentMatch[0]);
|
|
1266
|
-
const afterComponentStart = componentStart + componentMatch[0].length;
|
|
1267
|
-
|
|
1268
|
-
// Check if hooks are already added
|
|
1269
|
-
if (!content.includes('const { commentsEnabled, setCommentsEnabled')) {
|
|
1270
|
-
// Find where to insert hooks (after existing useState declarations)
|
|
1271
|
-
const stateMatch = content.slice(afterComponentStart).match(/const\s+\[[^\]]+\]\s*=\s*React\.useState/);
|
|
1272
|
-
let hookInsertPos;
|
|
1273
|
-
|
|
1274
|
-
if (stateMatch) {
|
|
1275
|
-
const statePos = content.indexOf(stateMatch[0], afterComponentStart);
|
|
1276
|
-
const semicolonPos = content.indexOf(';', statePos);
|
|
1277
|
-
hookInsertPos = semicolonPos + 1;
|
|
1278
|
-
} else {
|
|
1279
|
-
hookInsertPos = afterComponentStart;
|
|
1083
|
+
path.node.children = [commentPanel];
|
|
1280
1084
|
}
|
|
1281
|
-
|
|
1282
|
-
const hooks = `
|
|
1283
|
-
const { commentsEnabled, setCommentsEnabled, drawerPinnedOpen, setDrawerPinnedOpen, floatingWidgetMode, setFloatingWidgetMode } = useComments();
|
|
1284
|
-
const { isAuthenticated, user, login, logout } = useGitHubAuth();
|
|
1285
|
-
`;
|
|
1286
|
-
content = content.slice(0, hookInsertPos) + hooks + content.slice(hookInsertPos);
|
|
1287
1085
|
}
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
// Step 3: Add the special renderNavGroup logic
|
|
1291
|
-
// Replace the simple arrow function with a block function that has special Comments handling
|
|
1292
|
-
if (!content.includes("group.label === 'Comments'")) {
|
|
1293
|
-
// Find the renderNavGroup function - handle both arrow expression () => (...) and block () => {...}
|
|
1294
|
-
const arrowFuncMatch = content.match(/const\s+renderNavGroup\s*=\s*\(([^)]+)\)\s*=>\s*\(/);
|
|
1295
|
-
|
|
1296
|
-
if (arrowFuncMatch) {
|
|
1297
|
-
const params = arrowFuncMatch[1];
|
|
1298
|
-
|
|
1299
|
-
// Find the entire function including the closing parenthesis and semicolon
|
|
1300
|
-
const funcStart = content.indexOf(arrowFuncMatch[0]);
|
|
1301
|
-
const afterArrow = funcStart + arrowFuncMatch[0].length;
|
|
1302
|
-
|
|
1303
|
-
// Find matching closing paren and semicolon
|
|
1304
|
-
let depth = 1;
|
|
1305
|
-
let endPos = afterArrow;
|
|
1306
|
-
for (let i = afterArrow; i < content.length; i++) {
|
|
1307
|
-
if (content.charAt(i) === '(') depth++;
|
|
1308
|
-
if (content.charAt(i) === ')') {
|
|
1309
|
-
depth--;
|
|
1310
|
-
if (depth === 0) {
|
|
1311
|
-
// Found the closing paren, now find the semicolon
|
|
1312
|
-
endPos = i + 1;
|
|
1313
|
-
while (endPos < content.length && content.charAt(endPos).trim() === '') endPos++;
|
|
1314
|
-
if (content.charAt(endPos) === ';') endPos++;
|
|
1315
|
-
break;
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// Extract the original NavExpandable JSX (we'll use it as the default case)
|
|
1321
|
-
const originalBody = content.slice(funcStart + arrowFuncMatch[0].length - 1, endPos - 1); // Remove opening ( and closing );
|
|
1322
|
-
|
|
1323
|
-
// Create the new block function
|
|
1324
|
-
const newFunction = `const renderNavGroup = (${params}) => {
|
|
1325
|
-
// Special handling for Comments group
|
|
1326
|
-
if (group.label === 'Comments') {
|
|
1327
|
-
return (
|
|
1328
|
-
<NavExpandable
|
|
1329
|
-
key={\`\${group.label}-\${groupIndex}\`}
|
|
1330
|
-
id={\`\${group.label}-\${groupIndex}\`}
|
|
1331
|
-
title="Hale Commenting System"
|
|
1332
|
-
isActive={group.routes.some((route) => route.path === location.pathname)}
|
|
1333
|
-
>
|
|
1334
|
-
<NavItem
|
|
1335
|
-
onClick={(e) => {
|
|
1336
|
-
e.stopPropagation();
|
|
1337
|
-
setFloatingWidgetMode(!floatingWidgetMode);
|
|
1338
|
-
if (!floatingWidgetMode) {
|
|
1339
|
-
setDrawerPinnedOpen(false);
|
|
1340
|
-
}
|
|
1341
|
-
}}
|
|
1342
|
-
style={{ cursor: 'pointer' }}
|
|
1343
|
-
>
|
|
1344
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
|
1345
|
-
<ExternalLinkAltIcon />
|
|
1346
|
-
<span>{floatingWidgetMode ? 'Close widget' : 'Pop out'}</span>
|
|
1347
|
-
</div>
|
|
1348
|
-
</NavItem>
|
|
1349
|
-
<NavItem>
|
|
1350
|
-
<div
|
|
1351
|
-
data-comment-controls
|
|
1352
|
-
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1353
|
-
>
|
|
1354
|
-
<span>Enable Comments</span>
|
|
1355
|
-
<Switch
|
|
1356
|
-
id="comments-enabled-switch"
|
|
1357
|
-
isChecked={commentsEnabled}
|
|
1358
|
-
onChange={(_event, checked) => {
|
|
1359
|
-
setCommentsEnabled(checked);
|
|
1360
|
-
if (checked) {
|
|
1361
|
-
setDrawerPinnedOpen(true);
|
|
1362
|
-
}
|
|
1363
|
-
}}
|
|
1364
|
-
aria-label="Enable or disable comments"
|
|
1365
|
-
/>
|
|
1366
|
-
</div>
|
|
1367
|
-
</NavItem>
|
|
1368
|
-
<NavItem>
|
|
1369
|
-
<div
|
|
1370
|
-
data-comment-controls
|
|
1371
|
-
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1372
|
-
>
|
|
1373
|
-
<span>Page info drawer</span>
|
|
1374
|
-
<Switch
|
|
1375
|
-
id="page-info-drawer-switch"
|
|
1376
|
-
isChecked={drawerPinnedOpen}
|
|
1377
|
-
onChange={(_event, checked) => setDrawerPinnedOpen(checked)}
|
|
1378
|
-
aria-label="Pin page info drawer open"
|
|
1379
|
-
/>
|
|
1380
|
-
</div>
|
|
1381
|
-
</NavItem>
|
|
1382
|
-
<NavItem>
|
|
1383
|
-
<div
|
|
1384
|
-
data-comment-controls
|
|
1385
|
-
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1386
|
-
>
|
|
1387
|
-
{isAuthenticated ? (
|
|
1388
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
1389
|
-
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '6px' }}>
|
|
1390
|
-
<GithubIcon />
|
|
1391
|
-
{user?.login ? \`@\${user.login}\` : 'Signed in'}
|
|
1392
|
-
</span>
|
|
1393
|
-
<Button variant="link" isInline onClick={logout}>
|
|
1394
|
-
Sign out
|
|
1395
|
-
</Button>
|
|
1396
|
-
</div>
|
|
1397
|
-
) : (
|
|
1398
|
-
<Button variant="link" isInline icon={<GithubIcon />} onClick={login}>
|
|
1399
|
-
Sign in with GitHub
|
|
1400
|
-
</Button>
|
|
1401
|
-
)}
|
|
1402
|
-
</div>
|
|
1403
|
-
</NavItem>
|
|
1404
|
-
{group.routes.map((route, idx) => route.label && renderNavItem(route, idx))}
|
|
1405
|
-
</NavExpandable>
|
|
1406
|
-
);
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
// Default handling for other groups
|
|
1410
|
-
return ${originalBody};
|
|
1411
|
-
};`;
|
|
1086
|
+
});
|
|
1412
1087
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1088
|
+
if (!pageComponentFound) {
|
|
1089
|
+
console.error(' ❌ Could not find Page component in AppLayout.tsx');
|
|
1090
|
+
return false;
|
|
1416
1091
|
}
|
|
1417
1092
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
if (pageMatch) {
|
|
1423
|
-
const pageStart = content.indexOf(pageMatch[0]);
|
|
1424
|
-
const pageEnd = content.indexOf('</Page>', pageStart);
|
|
1425
|
-
|
|
1426
|
-
if (pageEnd > pageStart) {
|
|
1427
|
-
// Extract children between Page tags
|
|
1428
|
-
const pageOpenTagEnd = content.indexOf('>', pageStart) + 1;
|
|
1429
|
-
const children = content.slice(pageOpenTagEnd, pageEnd);
|
|
1430
|
-
|
|
1431
|
-
// Wrap with CommentPanel and add CommentOverlay
|
|
1432
|
-
const wrappedChildren = `
|
|
1433
|
-
<CommentPanel>
|
|
1434
|
-
<CommentOverlay />
|
|
1435
|
-
${children.trim()}
|
|
1436
|
-
</CommentPanel>
|
|
1437
|
-
`;
|
|
1438
|
-
|
|
1439
|
-
content = content.slice(0, pageOpenTagEnd) + wrappedChildren + content.slice(pageEnd);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1093
|
+
const output = generate(ast, {
|
|
1094
|
+
retainLines: false,
|
|
1095
|
+
compact: false
|
|
1096
|
+
}, content);
|
|
1443
1097
|
|
|
1444
|
-
fs.writeFileSync(filePath,
|
|
1098
|
+
fs.writeFileSync(filePath, output.code);
|
|
1445
1099
|
return true;
|
|
1446
1100
|
} catch (error) {
|
|
1447
1101
|
console.error(` ❌ Error modifying ${filePath}:`, error.message);
|
|
@@ -1821,15 +1475,13 @@ async function main() {
|
|
|
1821
1475
|
|
|
1822
1476
|
// Step 5: Integrate into project
|
|
1823
1477
|
console.log('\n🔧 Step 5: Integrating into PatternFly Seed project...\n');
|
|
1824
|
-
|
|
1478
|
+
|
|
1825
1479
|
console.log('This will modify the following files:');
|
|
1826
1480
|
console.log(' • src/app/index.tsx');
|
|
1827
|
-
console.log(' • src/app/routes.tsx');
|
|
1828
1481
|
console.log(' • src/app/AppLayout/AppLayout.tsx');
|
|
1829
1482
|
console.log(' • webpack.dev.js\n');
|
|
1830
1483
|
|
|
1831
1484
|
const indexPath = findFile('index.tsx');
|
|
1832
|
-
const routesPath = findFile('routes.tsx');
|
|
1833
1485
|
const appLayoutPath = findFile('AppLayout/AppLayout.tsx') || findFile('AppLayout.tsx');
|
|
1834
1486
|
|
|
1835
1487
|
if (!indexPath) {
|
|
@@ -1837,11 +1489,6 @@ async function main() {
|
|
|
1837
1489
|
rl.close();
|
|
1838
1490
|
process.exit(1);
|
|
1839
1491
|
}
|
|
1840
|
-
if (!routesPath) {
|
|
1841
|
-
console.error('❌ Could not find src/app/routes.tsx');
|
|
1842
|
-
rl.close();
|
|
1843
|
-
process.exit(1);
|
|
1844
|
-
}
|
|
1845
1492
|
if (!appLayoutPath) {
|
|
1846
1493
|
console.error('❌ Could not find src/app/AppLayout/AppLayout.tsx');
|
|
1847
1494
|
rl.close();
|
|
@@ -1862,19 +1509,6 @@ async function main() {
|
|
|
1862
1509
|
skippedCount++;
|
|
1863
1510
|
}
|
|
1864
1511
|
|
|
1865
|
-
// Create Comments component first (needed for routes)
|
|
1866
|
-
console.log('\n📝 Creating Comments component...');
|
|
1867
|
-
createCommentsComponent();
|
|
1868
|
-
|
|
1869
|
-
// Modify routes.tsx
|
|
1870
|
-
console.log(`\n📝 ${routesPath}`);
|
|
1871
|
-
if (modifyRoutesTsx(routesPath)) {
|
|
1872
|
-
console.log(' ✅ Added Comments route');
|
|
1873
|
-
successCount++;
|
|
1874
|
-
} else {
|
|
1875
|
-
skippedCount++;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
1512
|
// Modify AppLayout.tsx
|
|
1879
1513
|
console.log(`\n📝 ${appLayoutPath}`);
|
|
1880
1514
|
if (modifyAppLayoutTsx(appLayoutPath)) {
|
|
@@ -16,7 +16,7 @@ export const CommentOverlay: React.FunctionComponent = () => {
|
|
|
16
16
|
const handlePageClick = (e: MouseEvent) => {
|
|
17
17
|
if (!commentsEnabled) return;
|
|
18
18
|
|
|
19
|
-
// Check if clicking on a pin or any interactive element
|
|
19
|
+
// Check if clicking on a pin or any interactive element (including FloatingWidget)
|
|
20
20
|
const target = e.target as HTMLElement;
|
|
21
21
|
if (
|
|
22
22
|
target.closest('button') ||
|
|
@@ -26,9 +26,10 @@ export const CommentOverlay: React.FunctionComponent = () => {
|
|
|
26
26
|
target.closest('textarea') ||
|
|
27
27
|
target.closest('[role="button"]') ||
|
|
28
28
|
target.closest('[data-comment-controls]') ||
|
|
29
|
-
target.closest('[data-comment-pin]')
|
|
29
|
+
target.closest('[data-comment-pin]') ||
|
|
30
|
+
target.closest('[data-floating-widget]')
|
|
30
31
|
) {
|
|
31
|
-
return; // Don't create pin if clicking interactive elements
|
|
32
|
+
return; // Don't create pin if clicking interactive elements or floating widget
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// Get the overlay container dimensions (accounts for drawer being open)
|