portacode 0.3.19.dev4__tar.gz → 0.3.19.dev6__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.dev4 → portacode-0.3.19.dev6}/PKG-INFO +1 -1
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/_version.py +2 -2
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/project_state_handlers.py +122 -116
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/restore.sh +0 -2
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/.claude/agents/communication-manager.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/.claude/settings.local.json +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/.gitignore +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/.gitmodules +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/LICENSE +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/MANIFEST.in +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/Makefile +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/README.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/backup.sh +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/docker-compose.yaml +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/README.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/__init__.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/__main__.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/cli.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/README.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/client.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/session.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/data.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/keypair.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/service.py +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/SOURCES.txt +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/pyproject.toml +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/setup.cfg +0 -0
- {portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/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.dev6'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 19, 'dev6')
|
|
@@ -338,70 +338,23 @@ class GitManager:
|
|
|
338
338
|
return None
|
|
339
339
|
|
|
340
340
|
def _get_pygments_lexer(self, file_path: str) -> Optional[object]:
|
|
341
|
-
"""Get Pygments lexer for a file path."""
|
|
341
|
+
"""Get Pygments lexer for a file path using built-in detection."""
|
|
342
342
|
if not PYGMENTS_AVAILABLE:
|
|
343
343
|
return None
|
|
344
344
|
|
|
345
345
|
try:
|
|
346
|
-
#
|
|
346
|
+
# Use Pygments' built-in filename detection
|
|
347
347
|
return get_lexer_for_filename(file_path)
|
|
348
348
|
except ClassNotFound:
|
|
349
|
-
#
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
'.py': 'python',
|
|
353
|
-
'.js': 'javascript',
|
|
354
|
-
'.jsx': 'jsx',
|
|
355
|
-
'.ts': 'typescript',
|
|
356
|
-
'.tsx': 'tsx',
|
|
357
|
-
'.html': 'html',
|
|
358
|
-
'.htm': 'html',
|
|
359
|
-
'.css': 'css',
|
|
360
|
-
'.scss': 'scss',
|
|
361
|
-
'.sass': 'sass',
|
|
362
|
-
'.json': 'json',
|
|
363
|
-
'.xml': 'xml',
|
|
364
|
-
'.yaml': 'yaml',
|
|
365
|
-
'.yml': 'yaml',
|
|
366
|
-
'.java': 'java',
|
|
367
|
-
'.c': 'c',
|
|
368
|
-
'.cpp': 'cpp',
|
|
369
|
-
'.cc': 'cpp',
|
|
370
|
-
'.cxx': 'cpp',
|
|
371
|
-
'.h': 'c',
|
|
372
|
-
'.hpp': 'cpp',
|
|
373
|
-
'.cs': 'csharp',
|
|
374
|
-
'.php': 'php',
|
|
375
|
-
'.rb': 'ruby',
|
|
376
|
-
'.go': 'go',
|
|
377
|
-
'.rs': 'rust',
|
|
378
|
-
'.sh': 'bash',
|
|
379
|
-
'.bash': 'bash',
|
|
380
|
-
'.zsh': 'zsh',
|
|
381
|
-
'.fish': 'fish',
|
|
382
|
-
'.sql': 'sql',
|
|
383
|
-
'.md': 'markdown',
|
|
384
|
-
'.rst': 'rst'
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
lexer_name = lexer_map.get(extension)
|
|
388
|
-
if lexer_name:
|
|
389
|
-
try:
|
|
390
|
-
return get_lexer_by_name(lexer_name)
|
|
391
|
-
except ClassNotFound:
|
|
392
|
-
pass
|
|
393
|
-
|
|
394
|
-
# Final fallback to text
|
|
395
|
-
try:
|
|
396
|
-
return get_lexer_by_name('text')
|
|
397
|
-
except ClassNotFound:
|
|
398
|
-
return None
|
|
349
|
+
# If no lexer found, return None (will fall back to plain text)
|
|
350
|
+
logger.debug("No Pygments lexer found for file: %s", file_path)
|
|
351
|
+
return None
|
|
399
352
|
except Exception as e:
|
|
400
353
|
logger.debug("Error getting Pygments lexer: %s", e)
|
|
401
354
|
return None
|
|
402
355
|
|
|
403
|
-
def _generate_html_diff(self, original_content: str, modified_content: str, file_path: str) -> Optional[str]:
|
|
404
|
-
"""Generate unified HTML diff with intra-line highlighting
|
|
356
|
+
def _generate_html_diff(self, original_content: str, modified_content: str, file_path: str) -> Optional[Dict[str, str]]:
|
|
357
|
+
"""Generate unified HTML diff with intra-line highlighting. Returns both minimal and full context versions."""
|
|
405
358
|
if not PYGMENTS_AVAILABLE:
|
|
406
359
|
logger.debug("Pygments not available for HTML diff generation")
|
|
407
360
|
return None
|
|
@@ -413,85 +366,138 @@ class GitManager:
|
|
|
413
366
|
original_lines = original_content.splitlines(keepends=True)
|
|
414
367
|
modified_lines = modified_content.splitlines(keepends=True)
|
|
415
368
|
|
|
416
|
-
# Generate
|
|
417
|
-
|
|
369
|
+
# Generate both minimal (3 lines context) and full context diffs
|
|
370
|
+
minimal_diff_lines = list(difflib.unified_diff(
|
|
418
371
|
original_lines,
|
|
419
372
|
modified_lines,
|
|
420
373
|
fromfile='a/' + os.path.basename(file_path),
|
|
421
374
|
tofile='b/' + os.path.basename(file_path),
|
|
422
|
-
lineterm=''
|
|
375
|
+
lineterm='',
|
|
376
|
+
n=3 # 3 lines of context (default)
|
|
423
377
|
))
|
|
424
378
|
|
|
425
|
-
|
|
426
|
-
|
|
379
|
+
full_diff_lines = list(difflib.unified_diff(
|
|
380
|
+
original_lines,
|
|
381
|
+
modified_lines,
|
|
382
|
+
fromfile='a/' + os.path.basename(file_path),
|
|
383
|
+
tofile='b/' + os.path.basename(file_path),
|
|
384
|
+
lineterm='',
|
|
385
|
+
n=len(original_lines) + len(modified_lines) # Show all lines
|
|
386
|
+
))
|
|
427
387
|
|
|
428
|
-
#
|
|
429
|
-
|
|
388
|
+
# Parse both diffs
|
|
389
|
+
minimal_parsed = self._parse_unified_diff_with_intraline(minimal_diff_lines, original_lines, modified_lines)
|
|
390
|
+
full_parsed = self._parse_unified_diff_with_intraline(full_diff_lines, original_lines, modified_lines)
|
|
430
391
|
|
|
431
|
-
#
|
|
432
|
-
|
|
433
|
-
|
|
392
|
+
# Generate HTML for both versions
|
|
393
|
+
minimal_html = self._generate_diff_html(minimal_parsed, file_path, 'minimal')
|
|
394
|
+
full_html = self._generate_diff_html(full_parsed, file_path, 'full')
|
|
434
395
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
396
|
+
return {
|
|
397
|
+
'minimal': minimal_html,
|
|
398
|
+
'full': full_html
|
|
399
|
+
}
|
|
438
400
|
|
|
439
|
-
|
|
440
|
-
|
|
401
|
+
except Exception as e:
|
|
402
|
+
logger.error("Error generating HTML diff: %s", e)
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
def _generate_diff_html(self, parsed_diff: List[Dict], file_path: str, view_mode: str) -> str:
|
|
406
|
+
"""Generate HTML for a parsed diff."""
|
|
407
|
+
# Get Pygments lexer for syntax highlighting
|
|
408
|
+
lexer = self._get_pygments_lexer(file_path)
|
|
409
|
+
|
|
410
|
+
# Build HTML
|
|
411
|
+
html_parts = []
|
|
412
|
+
html_parts.append(f'<div class="unified-diff-container" data-view-mode="{view_mode}">')
|
|
413
|
+
|
|
414
|
+
# Add stats header with toggle
|
|
415
|
+
line_additions = sum(1 for line in parsed_diff if line['type'] == 'add')
|
|
416
|
+
line_deletions = sum(1 for line in parsed_diff if line['type'] == 'delete')
|
|
417
|
+
|
|
418
|
+
html_parts.append(f'''
|
|
419
|
+
<div class="diff-stats">
|
|
420
|
+
<div class="diff-stats-left">
|
|
441
421
|
<span class="additions">+{line_additions}</span>
|
|
442
422
|
<span class="deletions">-{line_deletions}</span>
|
|
443
423
|
<span class="file-path">{os.path.basename(file_path)}</span>
|
|
444
424
|
</div>
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
425
|
+
<div class="diff-stats-right">
|
|
426
|
+
<button class="diff-toggle-btn" data-current-mode="{view_mode}">
|
|
427
|
+
<i class="fas fa-eye"></i>
|
|
428
|
+
<span class="toggle-text">{''}</span>
|
|
429
|
+
</button>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
''')
|
|
433
|
+
|
|
434
|
+
# Generate unified diff view
|
|
435
|
+
html_parts.append('<div class="diff-content">')
|
|
436
|
+
html_parts.append('<table class="diff-table">')
|
|
437
|
+
|
|
438
|
+
for line_info in parsed_diff:
|
|
439
|
+
if line_info['type'] == 'header':
|
|
440
|
+
continue # Skip all diff headers including --- and +++ lines
|
|
460
441
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
442
|
+
line_type = line_info['type']
|
|
443
|
+
old_line_num = line_info.get('old_line_num', '')
|
|
444
|
+
new_line_num = line_info.get('new_line_num', '')
|
|
445
|
+
content = line_info['content']
|
|
446
|
+
intraline_html = line_info.get('intraline_html', '')
|
|
447
|
+
|
|
448
|
+
# Use intra-line highlighted content if available
|
|
449
|
+
if intraline_html:
|
|
450
|
+
final_content = intraline_html
|
|
451
|
+
else:
|
|
452
|
+
# Apply syntax highlighting to the content
|
|
453
|
+
if content and content[0] in '+-':
|
|
454
|
+
# For added/deleted lines, highlight the content without the prefix
|
|
455
|
+
prefix = content[0]
|
|
456
|
+
clean_content = content[1:]
|
|
457
|
+
|
|
458
|
+
if lexer and clean_content.strip():
|
|
459
|
+
try:
|
|
460
|
+
# Apply syntax highlighting
|
|
461
|
+
highlighted = highlight(clean_content, lexer, HtmlFormatter(nowrap=True, noclasses=False))
|
|
462
|
+
final_content = prefix + highlighted
|
|
463
|
+
except Exception as e:
|
|
464
|
+
logger.debug("Error applying syntax highlighting: %s", e)
|
|
465
|
+
final_content = self._escape_html(content)
|
|
466
|
+
else:
|
|
467
|
+
final_content = self._escape_html(content)
|
|
468
|
+
elif content and content[0] == ' ':
|
|
469
|
+
# For context lines, highlight without the space prefix
|
|
470
|
+
clean_content = content[1:]
|
|
471
|
+
|
|
472
|
+
if lexer and clean_content.strip():
|
|
473
|
+
try:
|
|
474
|
+
# Apply syntax highlighting
|
|
475
|
+
highlighted = highlight(clean_content, lexer, HtmlFormatter(nowrap=True, noclasses=False))
|
|
476
|
+
final_content = ' ' + highlighted
|
|
477
|
+
except Exception as e:
|
|
478
|
+
logger.debug("Error applying syntax highlighting: %s", e)
|
|
479
|
+
final_content = self._escape_html(content)
|
|
480
|
+
else:
|
|
471
481
|
final_content = self._escape_html(content)
|
|
472
482
|
else:
|
|
473
483
|
final_content = self._escape_html(content)
|
|
474
|
-
|
|
475
|
-
# CSS classes for different line types
|
|
476
|
-
row_class = f'diff-line diff-{line_type}'
|
|
477
|
-
|
|
478
|
-
html_parts.append(f'''
|
|
479
|
-
<tr class="{row_class}">
|
|
480
|
-
<td class="line-num old-line-num">{old_line_num}</td>
|
|
481
|
-
<td class="line-num new-line-num">{new_line_num}</td>
|
|
482
|
-
<td class="line-content">{final_content}</td>
|
|
483
|
-
</tr>
|
|
484
|
-
''')
|
|
485
|
-
|
|
486
|
-
html_parts.append('</table>')
|
|
487
|
-
html_parts.append('</div>')
|
|
488
|
-
html_parts.append('</div>')
|
|
489
484
|
|
|
490
|
-
|
|
485
|
+
# CSS classes for different line types
|
|
486
|
+
row_class = f'diff-line diff-{line_type}'
|
|
491
487
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
488
|
+
html_parts.append(f'''
|
|
489
|
+
<tr class="{row_class}">
|
|
490
|
+
<td class="line-num old-line-num">{old_line_num}</td>
|
|
491
|
+
<td class="line-num new-line-num">{new_line_num}</td>
|
|
492
|
+
<td class="line-content">{final_content}</td>
|
|
493
|
+
</tr>
|
|
494
|
+
''')
|
|
495
|
+
|
|
496
|
+
html_parts.append('</table>')
|
|
497
|
+
html_parts.append('</div>')
|
|
498
|
+
html_parts.append('</div>')
|
|
499
|
+
|
|
500
|
+
return ''.join(html_parts)
|
|
495
501
|
|
|
496
502
|
def _parse_unified_diff_with_intraline(self, diff_lines, original_lines, modified_lines):
|
|
497
503
|
"""Parse unified diff and add intra-line character highlighting."""
|
|
@@ -1556,8 +1562,8 @@ class ProjectStateManager:
|
|
|
1556
1562
|
# Compute diff details for the client
|
|
1557
1563
|
diff_details = git_manager._compute_diff_details(original_content, modified_content)
|
|
1558
1564
|
|
|
1559
|
-
# Generate HTML diff with syntax highlighting
|
|
1560
|
-
|
|
1565
|
+
# Generate HTML diff with syntax highlighting (both minimal and full context)
|
|
1566
|
+
html_diff_versions = git_manager._generate_html_diff(original_content, modified_content, file_path)
|
|
1561
1567
|
|
|
1562
1568
|
# Create a descriptive title for the diff
|
|
1563
1569
|
title_parts = []
|
|
@@ -1585,7 +1591,7 @@ class ProjectStateManager:
|
|
|
1585
1591
|
'from_hash': from_hash,
|
|
1586
1592
|
'to_hash': to_hash,
|
|
1587
1593
|
'diff_timeline': True,
|
|
1588
|
-
'
|
|
1594
|
+
'html_diff_versions': html_diff_versions
|
|
1589
1595
|
})
|
|
1590
1596
|
|
|
1591
1597
|
project_state.open_tabs[tab_key] = diff_tab
|
|
@@ -8,8 +8,6 @@ BACKUP_DIR="${1:-$PWD/../backups}"
|
|
|
8
8
|
VOLUME_NAME="portacode_pgdata"
|
|
9
9
|
SERVICE="db"
|
|
10
10
|
|
|
11
|
-
SDFSD
|
|
12
|
-
|
|
13
11
|
# ─── PICK A BACKUP ────────────────────────────────────────────────────────
|
|
14
12
|
mapfile -t BACKUPS < <(
|
|
15
13
|
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.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/portacode/connection/handlers/tab_factory.py
RENAMED
|
File without changes
|
{portacode-0.3.19.dev4 → portacode-0.3.19.dev6}/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
|