hale-commenting-system 3.4.0 ā 3.4.3
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 +1 -1
- package/scripts/integrate.js +45 -30
- package/src/app/commenting-system/components/CommentOverlay.tsx +3 -0
- package/src/app/commenting-system/components/JiraTab.tsx +7 -1
- package/src/app/commenting-system/contexts/CommentContext.tsx +6 -2
- package/src/app/commenting-system/contexts/GitHubAuthContext.tsx +6 -1
- package/src/app/commenting-system/services/githubAdapter.ts +29 -25
- package/src/app/commenting-system/utils/env.ts +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hale-commenting-system",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.3",
|
|
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
|
"homepage": "https://www.npmjs.com/package/hale-commenting-system",
|
|
6
6
|
"license": "MIT",
|
package/scripts/integrate.js
CHANGED
|
@@ -1266,12 +1266,13 @@ async function main() {
|
|
|
1266
1266
|
console.log(`\nā
Detected repository: ${owner}/${repo}\n`);
|
|
1267
1267
|
}
|
|
1268
1268
|
} else if (projectSetup === 'cloned') {
|
|
1269
|
-
console.log('\nš Since you cloned the repo, you
|
|
1270
|
-
console.log('
|
|
1269
|
+
console.log('\nš Since you cloned the repo, you can create your own GitHub repository to store comments.\n');
|
|
1270
|
+
console.log('Note: This is optional! You can test the system locally first and add GitHub integration later.\n');
|
|
1271
|
+
console.log('Steps to create a GitHub repository (optional):');
|
|
1271
1272
|
console.log('1. Create a new repository on GitHub');
|
|
1272
1273
|
console.log('2. Add it as a remote: git remote add origin <your-repo-url>');
|
|
1273
1274
|
console.log('3. Push your code: git push -u origin main\n');
|
|
1274
|
-
|
|
1275
|
+
|
|
1275
1276
|
const hasCreated = await prompt([
|
|
1276
1277
|
{
|
|
1277
1278
|
type: 'confirm',
|
|
@@ -1282,34 +1283,36 @@ async function main() {
|
|
|
1282
1283
|
]);
|
|
1283
1284
|
|
|
1284
1285
|
if (!hasCreated.created) {
|
|
1285
|
-
console.log('\
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1286
|
+
console.log('\nāļø No problem! You can set up the GitHub repository later.');
|
|
1287
|
+
console.log(' The system will still work locally for testing.\n');
|
|
1288
|
+
// Set placeholder values that can be updated later
|
|
1289
|
+
owner = 'YOUR_GITHUB_USERNAME';
|
|
1290
|
+
repo = 'YOUR_REPO_NAME';
|
|
1291
|
+
} else {
|
|
1292
|
+
// Ask for owner/repo
|
|
1293
|
+
const repoAnswers = await prompt([
|
|
1294
|
+
{
|
|
1295
|
+
type: 'input',
|
|
1296
|
+
name: 'owner',
|
|
1297
|
+
message: 'What is your GitHub username or organization name?',
|
|
1298
|
+
validate: (input) => {
|
|
1299
|
+
if (!input.trim()) return 'Owner is required';
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
type: 'input',
|
|
1305
|
+
name: 'repo',
|
|
1306
|
+
message: 'What is the name of your GitHub repository?',
|
|
1307
|
+
validate: (input) => {
|
|
1308
|
+
if (!input.trim()) return 'Repository name is required';
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1308
1311
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1312
|
+
]);
|
|
1313
|
+
owner = repoAnswers.owner;
|
|
1314
|
+
repo = repoAnswers.repo;
|
|
1315
|
+
}
|
|
1313
1316
|
} else if (projectSetup === 'unknown') {
|
|
1314
1317
|
// Try to detect from git
|
|
1315
1318
|
if (gitInfo && gitInfo.owner && gitInfo.repo) {
|
|
@@ -1761,6 +1764,18 @@ async function main() {
|
|
|
1761
1764
|
console.log(' (If it\'s already running, restart it to load the new configuration)');
|
|
1762
1765
|
console.log('2. The commenting system will be available in your app!\n');
|
|
1763
1766
|
|
|
1767
|
+
// Check if placeholders were used
|
|
1768
|
+
const hasPlaceholders = owner === 'YOUR_GITHUB_USERNAME' || repo === 'YOUR_REPO_NAME';
|
|
1769
|
+
|
|
1770
|
+
if (hasPlaceholders) {
|
|
1771
|
+
console.log('ā ļø Important: Placeholder values were used for GitHub repository.');
|
|
1772
|
+
console.log(' The UI will work for testing, but comments won\'t sync to GitHub until you:');
|
|
1773
|
+
console.log(' 1. Create a GitHub repository');
|
|
1774
|
+
console.log(' 2. Update VITE_GITHUB_OWNER and VITE_GITHUB_REPO in .env');
|
|
1775
|
+
console.log(' 3. Set up GitHub OAuth (optional - for authentication)');
|
|
1776
|
+
console.log(' 4. Restart your dev server\n');
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1764
1779
|
if (!githubConfig || !jiraConfig) {
|
|
1765
1780
|
console.log('š To add integrations later:');
|
|
1766
1781
|
if (!githubConfig) {
|
|
@@ -72,10 +72,13 @@ export const CommentOverlay: React.FunctionComponent = () => {
|
|
|
72
72
|
position: 'absolute',
|
|
73
73
|
top: 0,
|
|
74
74
|
left: 0,
|
|
75
|
+
right: 0,
|
|
76
|
+
bottom: 0,
|
|
75
77
|
width: '100%',
|
|
76
78
|
height: '100%',
|
|
77
79
|
pointerEvents: 'none',
|
|
78
80
|
zIndex: 999,
|
|
81
|
+
overflow: 'visible', // Ensure pins can be visible even if slightly outside
|
|
79
82
|
}}
|
|
80
83
|
>
|
|
81
84
|
{currentThreads.map((thread) => (
|
|
@@ -530,7 +530,13 @@ export const JiraTab: React.FunctionComponent = () => {
|
|
|
530
530
|
const scopeLabel =
|
|
531
531
|
source === 'page' ? 'This page' : source === 'section' ? `Section (${sectionRoute}/*)` : null;
|
|
532
532
|
const key = record.jiraKey || '';
|
|
533
|
-
|
|
533
|
+
let jiraBaseUrl: string | undefined;
|
|
534
|
+
try {
|
|
535
|
+
jiraBaseUrl = typeof process !== 'undefined' && process.env ? process.env.VITE_JIRA_BASE_URL : undefined;
|
|
536
|
+
} catch (e) {
|
|
537
|
+
jiraBaseUrl = undefined;
|
|
538
|
+
}
|
|
539
|
+
const url = issue?.url || (jiraBaseUrl ? `${jiraBaseUrl}/browse/${key}` : '');
|
|
534
540
|
|
|
535
541
|
const parsedSections = parseJiraTemplateSections(issue?.description || '');
|
|
536
542
|
const byTitle = new Map(parsedSections.map((s) => [s.title, s.body]));
|
|
@@ -463,14 +463,18 @@ export const CommentProvider: React.FunctionComponent<{ children: React.ReactNod
|
|
|
463
463
|
};
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
+
// Track which issue numbers were returned from GitHub
|
|
467
|
+
const githubIssueNumbers = new Set(ghThreads.map(gt => gt.issueNumber).filter((n): n is number => !!n));
|
|
468
|
+
|
|
466
469
|
// Keep local threads on this route/version that:
|
|
467
470
|
// 1. Don't have an issueNumber yet, OR
|
|
468
|
-
// 2. Are actively syncing (prevents race condition where issue was created but GitHub API hasn't returned it yet)
|
|
471
|
+
// 2. Are actively syncing (prevents race condition where issue was created but GitHub API hasn't returned it yet), OR
|
|
472
|
+
// 3. Have an issueNumber but GitHub didn't return it (preserve existing threads even if GitHub API fails or issue is hidden)
|
|
469
473
|
const localUnlinked = prev.filter(
|
|
470
474
|
(t) =>
|
|
471
475
|
t.route === route &&
|
|
472
476
|
(t.version ?? '1') === (version ?? '1') &&
|
|
473
|
-
(!t.issueNumber || t.syncStatus === 'syncing'),
|
|
477
|
+
(!t.issueNumber || t.syncStatus === 'syncing' || !githubIssueNumbers.has(t.issueNumber)),
|
|
474
478
|
);
|
|
475
479
|
|
|
476
480
|
// Remove duplicates: if a thread is both in localUnlinked and merged, prefer the merged version
|
|
@@ -40,7 +40,12 @@ export const GitHubAuthProvider: React.FunctionComponent<{ children: React.React
|
|
|
40
40
|
}, []);
|
|
41
41
|
|
|
42
42
|
const login = () => {
|
|
43
|
-
|
|
43
|
+
let clientId: string | undefined;
|
|
44
|
+
try {
|
|
45
|
+
clientId = typeof process !== 'undefined' && process.env ? process.env.VITE_GITHUB_CLIENT_ID : undefined;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
clientId = undefined;
|
|
48
|
+
}
|
|
44
49
|
if (!clientId) {
|
|
45
50
|
// eslint-disable-next-line no-alert
|
|
46
51
|
alert('GitHub login is not configured (missing VITE_GITHUB_CLIENT_ID).');
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getEnv } from '../utils/env';
|
|
2
|
+
|
|
1
3
|
export interface GitHubUser {
|
|
2
4
|
login: string;
|
|
3
5
|
avatar: string;
|
|
@@ -31,14 +33,16 @@ export const getStoredUser = (): GitHubUser | null => {
|
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export const isGitHubConfigured = (): boolean => {
|
|
34
|
-
|
|
36
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
37
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
38
|
+
return Boolean(getStoredToken() && owner && repo);
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
export const diagnoseGitHubSetup = () => {
|
|
38
42
|
const token = getStoredToken();
|
|
39
43
|
const user = getStoredUser();
|
|
40
|
-
const owner =
|
|
41
|
-
const repo =
|
|
44
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
45
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
42
46
|
|
|
43
47
|
console.log('š GitHub Configuration Diagnostic:');
|
|
44
48
|
console.log(' Token:', token ? `Present (${token.substring(0, 10)}...)` : 'Missing ā');
|
|
@@ -100,8 +104,8 @@ async function githubProxyRequest(method: string, endpoint: string, data?: any):
|
|
|
100
104
|
4. Token has expired or been revoked
|
|
101
105
|
|
|
102
106
|
Current config:
|
|
103
|
-
- Owner: ${
|
|
104
|
-
- Repo: ${
|
|
107
|
+
- Owner: ${getEnv('VITE_GITHUB_OWNER')}
|
|
108
|
+
- Repo: ${getEnv('VITE_GITHUB_REPO')}
|
|
105
109
|
- Endpoint: ${endpoint}
|
|
106
110
|
`);
|
|
107
111
|
}
|
|
@@ -153,8 +157,8 @@ export const githubAdapter = {
|
|
|
153
157
|
}): Promise<GitHubResult<{ number: number; html_url: string }>> {
|
|
154
158
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
155
159
|
|
|
156
|
-
const owner =
|
|
157
|
-
const repo =
|
|
160
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
161
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
158
162
|
|
|
159
163
|
try {
|
|
160
164
|
const metadata = [
|
|
@@ -193,8 +197,8 @@ export const githubAdapter = {
|
|
|
193
197
|
|
|
194
198
|
async createComment(issueNumber: number, body: string): Promise<GitHubResult> {
|
|
195
199
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
196
|
-
const owner =
|
|
197
|
-
const repo =
|
|
200
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
201
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
198
202
|
|
|
199
203
|
try {
|
|
200
204
|
const data = await githubProxyRequest('POST', `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, { body });
|
|
@@ -210,8 +214,8 @@ export const githubAdapter = {
|
|
|
210
214
|
|
|
211
215
|
async fetchIssuesForRouteAndVersion(route: string, version?: string): Promise<GitHubResult<any[]>> {
|
|
212
216
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
213
|
-
const owner =
|
|
214
|
-
const repo =
|
|
217
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
218
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
215
219
|
try {
|
|
216
220
|
const data = await githubProxyRequest(
|
|
217
221
|
'GET',
|
|
@@ -247,8 +251,8 @@ export const githubAdapter = {
|
|
|
247
251
|
|
|
248
252
|
async fetchIssueComments(issueNumber: number): Promise<GitHubResult<any[]>> {
|
|
249
253
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
250
|
-
const owner =
|
|
251
|
-
const repo =
|
|
254
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
255
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
252
256
|
try {
|
|
253
257
|
const data = await githubProxyRequest(
|
|
254
258
|
'GET',
|
|
@@ -262,8 +266,8 @@ export const githubAdapter = {
|
|
|
262
266
|
|
|
263
267
|
async updateComment(commentId: number, body: string): Promise<GitHubResult> {
|
|
264
268
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
265
|
-
const owner =
|
|
266
|
-
const repo =
|
|
269
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
270
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
267
271
|
try {
|
|
268
272
|
const data = await githubProxyRequest('PATCH', `/repos/${owner}/${repo}/issues/comments/${commentId}`, { body });
|
|
269
273
|
return { success: true, data };
|
|
@@ -274,8 +278,8 @@ export const githubAdapter = {
|
|
|
274
278
|
|
|
275
279
|
async deleteComment(commentId: number): Promise<GitHubResult> {
|
|
276
280
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
277
|
-
const owner =
|
|
278
|
-
const repo =
|
|
281
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
282
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
279
283
|
try {
|
|
280
284
|
await githubProxyRequest('DELETE', `/repos/${owner}/${repo}/issues/comments/${commentId}`);
|
|
281
285
|
return { success: true, data: {} };
|
|
@@ -286,8 +290,8 @@ export const githubAdapter = {
|
|
|
286
290
|
|
|
287
291
|
async closeIssue(issueNumber: number): Promise<GitHubResult> {
|
|
288
292
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
289
|
-
const owner =
|
|
290
|
-
const repo =
|
|
293
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
294
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
291
295
|
try {
|
|
292
296
|
const data = await githubProxyRequest('PATCH', `/repos/${owner}/${repo}/issues/${issueNumber}`, { state: 'closed' });
|
|
293
297
|
return { success: true, data };
|
|
@@ -298,8 +302,8 @@ export const githubAdapter = {
|
|
|
298
302
|
|
|
299
303
|
async reopenIssue(issueNumber: number): Promise<GitHubResult> {
|
|
300
304
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
301
|
-
const owner =
|
|
302
|
-
const repo =
|
|
305
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
306
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
303
307
|
try {
|
|
304
308
|
const data = await githubProxyRequest('PATCH', `/repos/${owner}/${repo}/issues/${issueNumber}`, { state: 'open' });
|
|
305
309
|
return { success: true, data };
|
|
@@ -310,8 +314,8 @@ export const githubAdapter = {
|
|
|
310
314
|
|
|
311
315
|
async getRepoFile(path: string): Promise<GitHubResult<{ text: string; sha: string } | null>> {
|
|
312
316
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
313
|
-
const owner =
|
|
314
|
-
const repo =
|
|
317
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
318
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
315
319
|
try {
|
|
316
320
|
const data = await githubProxyRequest('GET', `/repos/${owner}/${repo}/contents/${encodePath(path)}`);
|
|
317
321
|
const content = typeof data?.content === 'string' ? data.content.replace(/\n/g, '') : '';
|
|
@@ -335,8 +339,8 @@ export const githubAdapter = {
|
|
|
335
339
|
sha?: string;
|
|
336
340
|
}): Promise<GitHubResult<{ sha: string }>> {
|
|
337
341
|
if (!isGitHubConfigured()) return { success: false, error: 'Please sign in with GitHub' };
|
|
338
|
-
const owner =
|
|
339
|
-
const repo =
|
|
342
|
+
const owner = getEnv('VITE_GITHUB_OWNER');
|
|
343
|
+
const repo = getEnv('VITE_GITHUB_REPO');
|
|
340
344
|
try {
|
|
341
345
|
const payload: any = {
|
|
342
346
|
message: params.message,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safely access environment variables
|
|
3
|
+
* Returns undefined if process or env vars are not available
|
|
4
|
+
*/
|
|
5
|
+
export const getEnv = (key: string): string | undefined => {
|
|
6
|
+
// Check if running in an environment with process.env (webpack with DefinePlugin or similar)
|
|
7
|
+
try {
|
|
8
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
9
|
+
return process.env[key];
|
|
10
|
+
}
|
|
11
|
+
} catch (e) {
|
|
12
|
+
// process might be defined but accessing it throws an error
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
};
|