portacode 0.3.19.dev2__tar.gz → 0.3.19.dev3__tar.gz
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.
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/PKG-INFO +1 -1
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/_version.py +2 -2
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/project_state_handlers.py +164 -71
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/restore.sh +2 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/.claude/agents/communication-manager.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/.claude/settings.local.json +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/.gitignore +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/.gitmodules +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/LICENSE +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/MANIFEST.in +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/Makefile +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/README.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/backup.sh +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/docker-compose.yaml +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/README.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/__init__.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/__main__.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/cli.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/README.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/client.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/session.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/data.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/keypair.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/service.py +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/SOURCES.txt +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/pyproject.toml +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/setup.cfg +0 -0
- {portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/setup.py +0 -0
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.3.19.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 3, 19, '
|
|
20
|
+
__version__ = version = '0.3.19.dev3'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 19, 'dev3')
|
|
@@ -401,35 +401,45 @@ class GitManager:
|
|
|
401
401
|
return None
|
|
402
402
|
|
|
403
403
|
def _generate_html_diff(self, original_content: str, modified_content: str, file_path: str) -> Optional[str]:
|
|
404
|
-
"""Generate unified HTML diff with
|
|
405
|
-
if not
|
|
406
|
-
logger.debug("
|
|
404
|
+
"""Generate unified HTML diff with intra-line highlighting using GitPython and diff-match-patch."""
|
|
405
|
+
if not PYGMENTS_AVAILABLE:
|
|
406
|
+
logger.debug("Pygments not available for HTML diff generation")
|
|
407
407
|
return None
|
|
408
408
|
|
|
409
409
|
try:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
410
|
+
import difflib
|
|
411
|
+
|
|
412
|
+
# Get line-based diff using Python's difflib (similar to git diff)
|
|
413
|
+
original_lines = original_content.splitlines(keepends=True)
|
|
414
|
+
modified_lines = modified_content.splitlines(keepends=True)
|
|
415
|
+
|
|
416
|
+
# Generate unified diff
|
|
417
|
+
diff_lines = list(difflib.unified_diff(
|
|
418
|
+
original_lines,
|
|
419
|
+
modified_lines,
|
|
420
|
+
fromfile='a/' + os.path.basename(file_path),
|
|
421
|
+
tofile='b/' + os.path.basename(file_path),
|
|
422
|
+
lineterm=''
|
|
423
|
+
))
|
|
424
|
+
|
|
425
|
+
# Parse the unified diff and add intra-line highlighting
|
|
426
|
+
parsed_diff = self._parse_unified_diff_with_intraline(diff_lines, original_lines, modified_lines)
|
|
414
427
|
|
|
415
428
|
# Get Pygments lexer for syntax highlighting
|
|
416
429
|
lexer = self._get_pygments_lexer(file_path)
|
|
417
430
|
|
|
418
|
-
# Convert diffs to line-based unified diff
|
|
419
|
-
unified_diff_lines = self._create_unified_diff_lines(diffs, original_content, modified_content)
|
|
420
|
-
|
|
421
431
|
# Build HTML
|
|
422
432
|
html_parts = []
|
|
423
433
|
html_parts.append('<div class="unified-diff-container">')
|
|
424
434
|
|
|
425
435
|
# Add stats header
|
|
426
|
-
|
|
427
|
-
|
|
436
|
+
line_additions = sum(1 for line in parsed_diff if line['type'] == 'add')
|
|
437
|
+
line_deletions = sum(1 for line in parsed_diff if line['type'] == 'delete')
|
|
428
438
|
|
|
429
439
|
html_parts.append(f'''
|
|
430
440
|
<div class="diff-stats">
|
|
431
|
-
<span class="additions">+{
|
|
432
|
-
<span class="deletions">-{
|
|
441
|
+
<span class="additions">+{line_additions}</span>
|
|
442
|
+
<span class="deletions">-{line_deletions}</span>
|
|
433
443
|
<span class="file-path">{os.path.basename(file_path)}</span>
|
|
434
444
|
</div>
|
|
435
445
|
''')
|
|
@@ -438,26 +448,29 @@ class GitManager:
|
|
|
438
448
|
html_parts.append('<div class="diff-content">')
|
|
439
449
|
html_parts.append('<table class="diff-table">')
|
|
440
450
|
|
|
441
|
-
for line_info in
|
|
442
|
-
|
|
451
|
+
for line_info in parsed_diff:
|
|
452
|
+
if line_info['type'] == 'header':
|
|
453
|
+
continue # Skip diff headers
|
|
454
|
+
|
|
455
|
+
line_type = line_info['type']
|
|
443
456
|
old_line_num = line_info.get('old_line_num', '')
|
|
444
457
|
new_line_num = line_info.get('new_line_num', '')
|
|
445
458
|
content = line_info['content']
|
|
459
|
+
intraline_html = line_info.get('intraline_html', '')
|
|
446
460
|
|
|
447
|
-
#
|
|
448
|
-
if
|
|
461
|
+
# Use intra-line highlighted content if available, otherwise apply syntax highlighting
|
|
462
|
+
if intraline_html:
|
|
463
|
+
final_content = intraline_html
|
|
464
|
+
elif lexer and content.strip():
|
|
449
465
|
try:
|
|
450
|
-
#
|
|
466
|
+
# Apply syntax highlighting to the content (without the +/- prefix)
|
|
451
467
|
clean_content = content[1:] if content and content[0] in '+-' else content
|
|
452
468
|
highlighted = highlight(clean_content, lexer, HtmlFormatter(nowrap=True, noclasses=False))
|
|
453
|
-
|
|
454
|
-
if content and content[0] in '+-':
|
|
455
|
-
highlighted = content[0] + highlighted[len(clean_content):]
|
|
456
|
-
content = highlighted
|
|
469
|
+
final_content = content[0] + highlighted if content and content[0] in '+-' else highlighted
|
|
457
470
|
except Exception:
|
|
458
|
-
|
|
471
|
+
final_content = self._escape_html(content)
|
|
459
472
|
else:
|
|
460
|
-
|
|
473
|
+
final_content = self._escape_html(content)
|
|
461
474
|
|
|
462
475
|
# CSS classes for different line types
|
|
463
476
|
row_class = f'diff-line diff-{line_type}'
|
|
@@ -466,7 +479,7 @@ class GitManager:
|
|
|
466
479
|
<tr class="{row_class}">
|
|
467
480
|
<td class="line-num old-line-num">{old_line_num}</td>
|
|
468
481
|
<td class="line-num new-line-num">{new_line_num}</td>
|
|
469
|
-
<td class="line-content">{
|
|
482
|
+
<td class="line-content">{final_content}</td>
|
|
470
483
|
</tr>
|
|
471
484
|
''')
|
|
472
485
|
|
|
@@ -480,53 +493,133 @@ class GitManager:
|
|
|
480
493
|
logger.error("Error generating HTML diff: %s", e)
|
|
481
494
|
return None
|
|
482
495
|
|
|
483
|
-
def
|
|
484
|
-
"""
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
'type': 'context',
|
|
502
|
-
'old_line_num': old_line_num,
|
|
503
|
-
'new_line_num': new_line_num,
|
|
504
|
-
'content': ' ' + line
|
|
505
|
-
})
|
|
506
|
-
old_line_num += 1
|
|
507
|
-
new_line_num += 1
|
|
496
|
+
def _parse_unified_diff_with_intraline(self, diff_lines, original_lines, modified_lines):
|
|
497
|
+
"""Parse unified diff and add intra-line character highlighting."""
|
|
498
|
+
parsed = []
|
|
499
|
+
old_line_num = 0
|
|
500
|
+
new_line_num = 0
|
|
501
|
+
|
|
502
|
+
pending_deletes = []
|
|
503
|
+
pending_adds = []
|
|
504
|
+
|
|
505
|
+
def flush_pending():
|
|
506
|
+
"""Process pending delete/add pairs for intra-line highlighting."""
|
|
507
|
+
if pending_deletes and pending_adds:
|
|
508
|
+
# Apply intra-line highlighting to delete/add pairs
|
|
509
|
+
for i, (del_line, add_line) in enumerate(zip(pending_deletes, pending_adds)):
|
|
510
|
+
del_content = del_line['content'][1:] # Remove '-' prefix
|
|
511
|
+
add_content = add_line['content'][1:] # Remove '+' prefix
|
|
512
|
+
|
|
513
|
+
del_highlighted, add_highlighted = self._generate_intraline_diff(del_content, add_content)
|
|
508
514
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
'type': 'delete',
|
|
513
|
-
'old_line_num': old_line_num,
|
|
514
|
-
'new_line_num': '',
|
|
515
|
-
'content': '-' + line
|
|
516
|
-
})
|
|
517
|
-
old_line_num += 1
|
|
515
|
+
# Update the parsed lines with intra-line highlighting
|
|
516
|
+
del_line['intraline_html'] = '-' + del_highlighted
|
|
517
|
+
add_line['intraline_html'] = '+' + add_highlighted
|
|
518
518
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
519
|
+
parsed.append(del_line)
|
|
520
|
+
parsed.append(add_line)
|
|
521
|
+
|
|
522
|
+
# Handle remaining unmatched deletes/adds
|
|
523
|
+
for del_line in pending_deletes[len(pending_adds):]:
|
|
524
|
+
parsed.append(del_line)
|
|
525
|
+
for add_line in pending_adds[len(pending_deletes):]:
|
|
526
|
+
parsed.append(add_line)
|
|
527
|
+
else:
|
|
528
|
+
# No pairs to highlight, just add them as-is
|
|
529
|
+
parsed.extend(pending_deletes)
|
|
530
|
+
parsed.extend(pending_adds)
|
|
531
|
+
|
|
532
|
+
pending_deletes.clear()
|
|
533
|
+
pending_adds.clear()
|
|
534
|
+
|
|
535
|
+
for line in diff_lines:
|
|
536
|
+
if line.startswith('@@'):
|
|
537
|
+
# Flush any pending changes before hunk header
|
|
538
|
+
flush_pending()
|
|
539
|
+
|
|
540
|
+
# Parse hunk header to get line numbers
|
|
541
|
+
import re
|
|
542
|
+
match = re.match(r'@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@', line)
|
|
543
|
+
if match:
|
|
544
|
+
old_line_num = int(match.group(1)) - 1
|
|
545
|
+
new_line_num = int(match.group(2)) - 1
|
|
546
|
+
|
|
547
|
+
parsed.append({
|
|
548
|
+
'type': 'header',
|
|
549
|
+
'content': line,
|
|
550
|
+
'old_line_num': '',
|
|
551
|
+
'new_line_num': ''
|
|
552
|
+
})
|
|
553
|
+
elif line.startswith('-'):
|
|
554
|
+
pending_deletes.append({
|
|
555
|
+
'type': 'delete',
|
|
556
|
+
'old_line_num': old_line_num + 1,
|
|
557
|
+
'new_line_num': '',
|
|
558
|
+
'content': line
|
|
559
|
+
})
|
|
560
|
+
old_line_num += 1
|
|
561
|
+
elif line.startswith('+'):
|
|
562
|
+
pending_adds.append({
|
|
563
|
+
'type': 'add',
|
|
564
|
+
'old_line_num': '',
|
|
565
|
+
'new_line_num': new_line_num + 1,
|
|
566
|
+
'content': line
|
|
567
|
+
})
|
|
568
|
+
new_line_num += 1
|
|
569
|
+
elif line.startswith(' '):
|
|
570
|
+
# Flush pending changes before context line
|
|
571
|
+
flush_pending()
|
|
572
|
+
|
|
573
|
+
old_line_num += 1
|
|
574
|
+
new_line_num += 1
|
|
575
|
+
parsed.append({
|
|
576
|
+
'type': 'context',
|
|
577
|
+
'old_line_num': old_line_num,
|
|
578
|
+
'new_line_num': new_line_num,
|
|
579
|
+
'content': line
|
|
580
|
+
})
|
|
581
|
+
elif line.startswith('---') or line.startswith('+++'):
|
|
582
|
+
parsed.append({
|
|
583
|
+
'type': 'header',
|
|
584
|
+
'content': line,
|
|
585
|
+
'old_line_num': '',
|
|
586
|
+
'new_line_num': ''
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
# Flush any remaining pending changes
|
|
590
|
+
flush_pending()
|
|
591
|
+
|
|
592
|
+
return parsed
|
|
593
|
+
|
|
594
|
+
def _generate_intraline_diff(self, old_text: str, new_text: str) -> tuple[str, str]:
|
|
595
|
+
"""Generate intra-line character-level diff highlighting."""
|
|
596
|
+
if not DIFF_MATCH_PATCH_AVAILABLE:
|
|
597
|
+
return self._escape_html(old_text), self._escape_html(new_text)
|
|
598
|
+
|
|
599
|
+
try:
|
|
600
|
+
dmp = diff_match_patch()
|
|
601
|
+
diffs = dmp.diff_main(old_text, new_text)
|
|
602
|
+
dmp.diff_cleanupSemantic(diffs)
|
|
603
|
+
|
|
604
|
+
old_parts = []
|
|
605
|
+
new_parts = []
|
|
606
|
+
|
|
607
|
+
for op, text in diffs:
|
|
608
|
+
escaped_text = self._escape_html(text)
|
|
609
|
+
|
|
610
|
+
if op == 0: # EQUAL
|
|
611
|
+
old_parts.append(escaped_text)
|
|
612
|
+
new_parts.append(escaped_text)
|
|
613
|
+
elif op == -1: # DELETE
|
|
614
|
+
old_parts.append(f'<span class="intraline-delete">{escaped_text}</span>')
|
|
615
|
+
elif op == 1: # INSERT
|
|
616
|
+
new_parts.append(f'<span class="intraline-add">{escaped_text}</span>')
|
|
617
|
+
|
|
618
|
+
return ''.join(old_parts), ''.join(new_parts)
|
|
619
|
+
|
|
620
|
+
except Exception as e:
|
|
621
|
+
logger.debug("Error generating intra-line diff: %s", e)
|
|
622
|
+
return self._escape_html(old_text), self._escape_html(new_text)
|
|
530
623
|
|
|
531
624
|
def _escape_html(self, text: str) -> str:
|
|
532
625
|
"""Escape HTML special characters."""
|
|
@@ -8,6 +8,8 @@ BACKUP_DIR="${1:-$PWD/../backups}"
|
|
|
8
8
|
VOLUME_NAME="portacode_pgdata"
|
|
9
9
|
SERVICE="db"
|
|
10
10
|
|
|
11
|
+
SDFSD
|
|
12
|
+
|
|
11
13
|
# ─── PICK A BACKUP ────────────────────────────────────────────────────────
|
|
12
14
|
mapfile -t BACKUPS < <(
|
|
13
15
|
find "$BACKUP_DIR" -maxdepth 1 -type f -name "pgdata-*.tar.gz" \
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/tab_factory.py
RENAMED
|
File without changes
|
{portacode-0.3.19.dev2 → portacode-0.3.19.dev3}/portacode/connection/handlers/terminal_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|