@pixelbyte-software/pixcode 1.53.0 → 1.53.2
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/dist/assets/{index-yE2qL74i.js → index-CP2HfVKD.js} +196 -178
- package/dist/assets/{index-Cz9BCbXv.css → index-FhOsv2Yy.css} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +140 -16
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/routes/git.js +79 -21
- package/dist-server/server/routes/git.js.map +1 -1
- package/package.json +1 -1
- package/server/index.js +159 -17
- package/server/routes/git.js +89 -28
package/server/routes/git.js
CHANGED
|
@@ -380,6 +380,27 @@ function normalizeRepositoryRelativeFilePath(filePath) {
|
|
|
380
380
|
.trim();
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
function normalizeRequestedRepositoryFilePath(repositoryRootPath, filePath) {
|
|
384
|
+
const rawFilePath = String(filePath || '').replace(/\\/g, '/').trim();
|
|
385
|
+
if (!rawFilePath) {
|
|
386
|
+
return '';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (path.isAbsolute(rawFilePath)) {
|
|
390
|
+
const resolvedFilePath = path.resolve(rawFilePath);
|
|
391
|
+
const resolvedRepositoryRoot = path.resolve(repositoryRootPath);
|
|
392
|
+
const relativePath = path.relative(resolvedRepositoryRoot, resolvedFilePath);
|
|
393
|
+
|
|
394
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
395
|
+
throw new Error('Invalid file path: outside repository');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return normalizeRepositoryRelativeFilePath(relativePath);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return normalizeRepositoryRelativeFilePath(rawFilePath);
|
|
402
|
+
}
|
|
403
|
+
|
|
383
404
|
function parseStatusFilePaths(statusOutput) {
|
|
384
405
|
return statusOutput
|
|
385
406
|
.split('\n')
|
|
@@ -394,7 +415,7 @@ function parseStatusFilePaths(statusOutput) {
|
|
|
394
415
|
}
|
|
395
416
|
|
|
396
417
|
function buildFilePathCandidates(projectPath, repositoryRootPath, filePath) {
|
|
397
|
-
const normalizedFilePath =
|
|
418
|
+
const normalizedFilePath = normalizeRequestedRepositoryFilePath(repositoryRootPath, filePath);
|
|
398
419
|
const projectRelativePath = normalizeRepositoryRelativeFilePath(path.relative(repositoryRootPath, projectPath));
|
|
399
420
|
const candidates = [normalizedFilePath];
|
|
400
421
|
|
|
@@ -629,53 +650,93 @@ router.get('/file-with-diff', async (req, res) => {
|
|
|
629
650
|
repositoryRelativeFilePath,
|
|
630
651
|
} = await resolveRepositoryFilePath(projectPath, file);
|
|
631
652
|
|
|
653
|
+
const readFileFromIndex = async () => {
|
|
654
|
+
const { stdout } = await spawnAsync(
|
|
655
|
+
'git',
|
|
656
|
+
['show', `:${repositoryRelativeFilePath}`],
|
|
657
|
+
{ cwd: repositoryRootPath },
|
|
658
|
+
);
|
|
659
|
+
return stdout;
|
|
660
|
+
};
|
|
661
|
+
const readFileFromHead = async () => {
|
|
662
|
+
const { stdout } = await spawnAsync(
|
|
663
|
+
'git',
|
|
664
|
+
['show', `HEAD:${repositoryRelativeFilePath}`],
|
|
665
|
+
{ cwd: repositoryRootPath },
|
|
666
|
+
);
|
|
667
|
+
return stdout;
|
|
668
|
+
};
|
|
669
|
+
|
|
632
670
|
// Check file status
|
|
633
671
|
const { stdout: statusOutput } = await spawnAsync(
|
|
634
672
|
'git',
|
|
635
673
|
['status', '--porcelain', '--', repositoryRelativeFilePath],
|
|
636
674
|
{ cwd: repositoryRootPath },
|
|
637
675
|
);
|
|
638
|
-
const
|
|
639
|
-
const
|
|
676
|
+
const statusLine = statusOutput.split('\n').find((line) => line.trim()) || '';
|
|
677
|
+
const indexStatus = statusLine[0] || ' ';
|
|
678
|
+
const worktreeStatus = statusLine[1] || ' ';
|
|
679
|
+
const isUntracked = statusLine.startsWith('??');
|
|
680
|
+
const hasUnstagedChange = !isUntracked && worktreeStatus !== ' ';
|
|
681
|
+
const hasStagedChange = !isUntracked && indexStatus !== ' ';
|
|
682
|
+
const isDeleted = worktreeStatus === 'D' || (!hasUnstagedChange && indexStatus === 'D');
|
|
640
683
|
|
|
641
684
|
let currentContent = '';
|
|
642
685
|
let oldContent = '';
|
|
643
686
|
|
|
644
|
-
if (
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
687
|
+
if (hasUnstagedChange) {
|
|
688
|
+
try {
|
|
689
|
+
oldContent = await readFileFromIndex();
|
|
690
|
+
} catch {
|
|
691
|
+
oldContent = '';
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (worktreeStatus === 'D') {
|
|
695
|
+
currentContent = '';
|
|
696
|
+
} else {
|
|
697
|
+
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
698
|
+
const stats = await fs.stat(filePath);
|
|
699
|
+
if (stats.isDirectory()) {
|
|
700
|
+
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
701
|
+
}
|
|
702
|
+
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
703
|
+
}
|
|
704
|
+
} else if (hasStagedChange) {
|
|
705
|
+
try {
|
|
706
|
+
oldContent = await readFileFromHead();
|
|
707
|
+
} catch {
|
|
708
|
+
// A staged add has no HEAD copy; showing the whole file as added is correct.
|
|
709
|
+
oldContent = '';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (indexStatus === 'D') {
|
|
713
|
+
currentContent = '';
|
|
714
|
+
} else {
|
|
715
|
+
try {
|
|
716
|
+
currentContent = await readFileFromIndex();
|
|
717
|
+
} catch {
|
|
718
|
+
currentContent = '';
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
} else if (isUntracked) {
|
|
655
722
|
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
656
723
|
const stats = await fs.stat(filePath);
|
|
657
724
|
|
|
658
725
|
if (stats.isDirectory()) {
|
|
659
|
-
// Cannot show content for directories
|
|
660
726
|
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
661
727
|
}
|
|
662
728
|
|
|
663
729
|
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
730
|
+
} else {
|
|
731
|
+
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
732
|
+
const stats = await fs.stat(filePath);
|
|
664
733
|
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
try {
|
|
668
|
-
const { stdout: headContent } = await spawnAsync(
|
|
669
|
-
'git',
|
|
670
|
-
['show', `HEAD:${repositoryRelativeFilePath}`],
|
|
671
|
-
{ cwd: repositoryRootPath },
|
|
672
|
-
);
|
|
673
|
-
oldContent = headContent;
|
|
674
|
-
} catch (error) {
|
|
675
|
-
// File might be newly added to git (staged but not committed)
|
|
676
|
-
oldContent = '';
|
|
677
|
-
}
|
|
734
|
+
if (stats.isDirectory()) {
|
|
735
|
+
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
678
736
|
}
|
|
737
|
+
|
|
738
|
+
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
739
|
+
oldContent = currentContent;
|
|
679
740
|
}
|
|
680
741
|
|
|
681
742
|
res.json({
|