hale-commenting-system 2.2.91 → 2.2.94

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hale-commenting-system",
3
- "version": "2.2.91",
3
+ "version": "2.2.94",
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",
@@ -1170,115 +1170,253 @@ function modifyRoutesTsx(filePath) {
1170
1170
  }
1171
1171
 
1172
1172
  function modifyAppLayoutTsx(filePath) {
1173
- const content = fs.readFileSync(filePath, 'utf8');
1174
-
1175
- // Check if already integrated
1176
- if (content.includes('CommentPanel') && content.includes('CommentOverlay')) {
1177
- console.log(' ⚠️ Already integrated (CommentPanel/CommentOverlay found)');
1173
+ let content = fs.readFileSync(filePath, 'utf8');
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)');
1178
1179
  return false;
1179
1180
  }
1180
1181
 
1181
1182
  try {
1182
- const ast = parser(content, {
1183
- sourceType: 'module',
1184
- plugins: ['typescript', 'jsx', 'decorators-legacy', 'classProperties']
1185
- });
1186
-
1187
- let hasCommentImport = false;
1188
- let pageElement = null;
1189
-
1190
- // Check imports and find Page element
1191
- traverse(ast, {
1192
- ImportDeclaration(path) {
1193
- const source = path.node.source.value;
1194
- if (source.includes('commenting-system') || source.includes('@app/commenting-system') || source.includes('hale-commenting-system')) {
1195
- hasCommentImport = true;
1196
- // Check if components are imported
1197
- const specifiers = path.node.specifiers || [];
1198
- const hasCommentPanel = specifiers.some(s =>
1199
- s.imported && (s.imported.name === 'CommentPanel' || s.local.name === 'CommentPanel')
1200
- );
1201
- const hasCommentOverlay = specifiers.some(s =>
1202
- s.imported && (s.imported.name === 'CommentOverlay' || s.local.name === 'CommentOverlay')
1183
+ // Step 1: Add imports using string manipulation (more reliable for complex imports)
1184
+
1185
+ // Check and add PatternFly imports
1186
+ const patternflyImportRegex = /from\s+['"]@patternfly\/react-core['"]/;
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'`
1203
1200
  );
1201
+ }
1202
+ }
1203
+ }
1204
1204
 
1205
- if (!hasCommentPanel) {
1206
- specifiers.push(types.importSpecifier(types.identifier('CommentPanel'), types.identifier('CommentPanel')));
1207
- }
1208
- if (!hasCommentOverlay) {
1209
- specifiers.push(types.importSpecifier(types.identifier('CommentOverlay'), types.identifier('CommentOverlay')));
1210
- }
1211
- path.node.specifiers = specifiers;
1205
+ // Check and add PatternFly icons imports
1206
+ const iconsImportRegex = /from\s+['"]@patternfly\/react-icons['"]/;
1207
+ const iconsMatch = content.match(iconsImportRegex);
1208
+
1209
+ if (iconsMatch) {
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';
1212
1217
  }
1213
- },
1214
- JSXElement(path) {
1215
- if (path.node.openingElement.name.name === 'Page') {
1216
- pageElement = path;
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'`
1225
+ );
1217
1226
  }
1218
1227
  }
1219
- });
1228
+ }
1220
1229
 
1221
- // Add imports if missing
1222
- if (!hasCommentImport) {
1223
- // Find last import declaration
1224
- let lastImportIndex = -1;
1225
- for (let i = ast.program.body.length - 1; i >= 0; i--) {
1226
- if (ast.program.body[i].type === 'ImportDeclaration') {
1227
- lastImportIndex = i;
1228
- break;
1229
- }
1230
+ // Add commenting system imports
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
+ );
1230
1258
  }
1231
- const importIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
1232
-
1233
- const componentImports = types.importDeclaration(
1234
- [
1235
- types.importSpecifier(types.identifier('CommentPanel'), types.identifier('CommentPanel')),
1236
- types.importSpecifier(types.identifier('CommentOverlay'), types.identifier('CommentOverlay'))
1237
- ],
1238
- types.stringLiteral('hale-commenting-system')
1239
- );
1240
-
1241
- ast.program.body.splice(importIndex, 0, componentImports);
1242
1259
  }
1243
1260
 
1244
- // Wrap Page children with CommentPanel and CommentOverlay
1245
- if (pageElement) {
1246
- const pageChildren = pageElement.node.children;
1247
-
1248
- // Check if already wrapped
1249
- if (pageChildren.length > 0 &&
1250
- pageChildren[0].type === 'JSXElement' &&
1251
- pageChildren[0].openingElement.name.name === 'CommentPanel') {
1252
- console.log(' ⚠️ Already integrated (CommentPanel found in JSX)');
1253
- return false;
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;
1280
+ }
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);
1254
1287
  }
1288
+ }
1255
1289
 
1256
- // Create CommentOverlay
1257
- const commentOverlay = types.jsxElement(
1258
- types.jsxOpeningElement(types.jsxIdentifier('CommentOverlay'), [], true),
1259
- null,
1260
- []
1290
+ // Step 3: Add the special renderNavGroup logic
1291
+ // Find the renderNavGroup function
1292
+ const renderNavGroupMatch = content.match(/const\s+renderNavGroup\s*=\s*\([^)]+\)\s*=>\s*\{?/);
1293
+ if (renderNavGroupMatch && !content.includes("group.label === 'Comments'")) {
1294
+ const funcStart = content.indexOf(renderNavGroupMatch[0]);
1295
+ const funcStartBrace = content.indexOf('{', funcStart) || content.indexOf('(', funcStart);
1296
+
1297
+ // Insert the special Comments handling at the start of the function
1298
+ const specialHandling = `
1299
+ // Special handling for Comments group
1300
+ if (group.label === 'Comments') {
1301
+ return (
1302
+ <NavExpandable
1303
+ key={\`\${group.label}-\${groupIndex}\`}
1304
+ id={\`\${group.label}-\${groupIndex}\`}
1305
+ title="Hale Commenting System"
1306
+ isActive={group.routes.some((route) => route.path === location.pathname)}
1307
+ >
1308
+ <NavItem
1309
+ onClick={(e) => {
1310
+ e.stopPropagation();
1311
+ setFloatingWidgetMode(!floatingWidgetMode);
1312
+ if (!floatingWidgetMode) {
1313
+ setDrawerPinnedOpen(false);
1314
+ }
1315
+ }}
1316
+ style={{ cursor: 'pointer' }}
1317
+ >
1318
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
1319
+ <ExternalLinkAltIcon />
1320
+ <span>{floatingWidgetMode ? 'Close widget' : 'Pop out'}</span>
1321
+ </div>
1322
+ </NavItem>
1323
+ <NavItem>
1324
+ <div
1325
+ data-comment-controls
1326
+ style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
1327
+ >
1328
+ <span>Enable Comments</span>
1329
+ <Switch
1330
+ id="comments-enabled-switch"
1331
+ isChecked={commentsEnabled}
1332
+ onChange={(_event, checked) => {
1333
+ setCommentsEnabled(checked);
1334
+ if (checked) {
1335
+ setDrawerPinnedOpen(true);
1336
+ }
1337
+ }}
1338
+ aria-label="Enable or disable comments"
1339
+ />
1340
+ </div>
1341
+ </NavItem>
1342
+ <NavItem>
1343
+ <div
1344
+ data-comment-controls
1345
+ style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
1346
+ >
1347
+ <span>Page info drawer</span>
1348
+ <Switch
1349
+ id="page-info-drawer-switch"
1350
+ isChecked={drawerPinnedOpen}
1351
+ onChange={(_event, checked) => setDrawerPinnedOpen(checked)}
1352
+ aria-label="Pin page info drawer open"
1353
+ />
1354
+ </div>
1355
+ </NavItem>
1356
+ <NavItem>
1357
+ <div
1358
+ data-comment-controls
1359
+ style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
1360
+ >
1361
+ {isAuthenticated ? (
1362
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
1363
+ <span style={{ display: 'inline-flex', alignItems: 'center', gap: '6px' }}>
1364
+ <GithubIcon />
1365
+ {user?.login ? \`@\${user.login}\` : 'Signed in'}
1366
+ </span>
1367
+ <Button variant="link" isInline onClick={logout}>
1368
+ Sign out
1369
+ </Button>
1370
+ </div>
1371
+ ) : (
1372
+ <Button variant="link" isInline icon={<GithubIcon />} onClick={login}>
1373
+ Sign in with GitHub
1374
+ </Button>
1375
+ )}
1376
+ </div>
1377
+ </NavItem>
1378
+ {group.routes.map((route, idx) => route.label && renderNavItem(route, idx))}
1379
+ </NavExpandable>
1261
1380
  );
1381
+ }
1262
1382
 
1263
- // Create CommentPanel wrapping children and CommentOverlay
1264
- const commentPanel = types.jsxElement(
1265
- types.jsxOpeningElement(types.jsxIdentifier('CommentPanel'), []),
1266
- types.jsxClosingElement(types.jsxIdentifier('CommentPanel')),
1267
- [commentOverlay, ...pageChildren]
1268
- );
1383
+ // Default handling for other groups
1384
+ `;
1269
1385
 
1270
- pageElement.node.children = [commentPanel];
1386
+ content = content.slice(0, funcStartBrace + 1) + specialHandling + content.slice(funcStartBrace + 1);
1271
1387
  }
1272
1388
 
1273
- const output = generate(ast, {
1274
- retainLines: false,
1275
- compact: false
1276
- }, content);
1389
+ // Step 4: Wrap Page children if not already done
1390
+ if (!content.includes('<CommentPanel>')) {
1391
+ // Find the return statement with Page
1392
+ const pageMatch = content.match(/<Page[^>]*>/);
1393
+ if (pageMatch) {
1394
+ const pageStart = content.indexOf(pageMatch[0]);
1395
+ const pageEnd = content.indexOf('</Page>', pageStart);
1396
+
1397
+ if (pageEnd > pageStart) {
1398
+ // Extract children between Page tags
1399
+ const pageOpenTagEnd = content.indexOf('>', pageStart) + 1;
1400
+ const children = content.slice(pageOpenTagEnd, pageEnd);
1401
+
1402
+ // Wrap with CommentPanel and add CommentOverlay
1403
+ const wrappedChildren = `
1404
+ <CommentPanel>
1405
+ <CommentOverlay />
1406
+ ${children.trim()}
1407
+ </CommentPanel>
1408
+ `;
1409
+
1410
+ content = content.slice(0, pageOpenTagEnd) + wrappedChildren + content.slice(pageEnd);
1411
+ }
1412
+ }
1413
+ }
1277
1414
 
1278
- fs.writeFileSync(filePath, output.code);
1415
+ fs.writeFileSync(filePath, content);
1279
1416
  return true;
1280
1417
  } catch (error) {
1281
1418
  console.error(` ❌ Error modifying ${filePath}:`, error.message);
1419
+ console.error(error.stack);
1282
1420
  return false;
1283
1421
  }
1284
1422
  }
@@ -6,6 +6,12 @@ export { GitHubAuthProvider, useGitHubAuth } from './contexts/GitHubAuthContext'
6
6
  export { CommentOverlay } from './components/CommentOverlay';
7
7
  export { CommentPin } from './components/CommentPin';
8
8
  export { CommentPanel } from './components/CommentPanel';
9
+ export { DetailsTab } from './components/DetailsTab';
10
+ export { JiraTab } from './components/JiraTab';
11
+ export { FloatingWidget } from './components/FloatingWidget';
12
+
13
+ // Services
14
+ export { githubAdapter, isGitHubConfigured } from './services/githubAdapter';
9
15
 
10
16
  // Types
11
17
  export type { Comment, Thread, SyncStatus, ThreadStatus } from './types';