aegis-framework 0.3.0 → 0.3.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/aegis/cli/cli.py +210 -146
- package/aegis/core/window.py +30 -18
- package/package.json +2 -2
package/aegis/cli/cli.py
CHANGED
|
@@ -463,18 +463,189 @@ aegis build
|
|
|
463
463
|
└── icon.png # App icon (256x256 recommended)
|
|
464
464
|
```
|
|
465
465
|
|
|
466
|
-
|
|
466
|
+
---
|
|
467
467
|
|
|
468
|
-
|
|
468
|
+
# ⚡ Aegis Utility API
|
|
469
|
+
|
|
470
|
+
Aegis includes a powerful utility API that simplifies common JavaScript patterns!
|
|
471
|
+
|
|
472
|
+
## Event Shortcuts
|
|
473
|
+
|
|
474
|
+
```javascript
|
|
475
|
+
// Click event (works on single or multiple elements!)
|
|
476
|
+
Aegis.click('#btn', () => alert('Clicked!'));
|
|
477
|
+
Aegis.click('.card', (e) => console.log(e.target));
|
|
478
|
+
|
|
479
|
+
// More event shortcuts
|
|
480
|
+
Aegis.submit('#form', handler);
|
|
481
|
+
Aegis.change('#select', handler);
|
|
482
|
+
Aegis.input('#search', handler);
|
|
483
|
+
Aegis.keypress('#input', handler);
|
|
484
|
+
|
|
485
|
+
// Hover with enter/leave
|
|
486
|
+
Aegis.hover('#card', onEnter, onLeave);
|
|
487
|
+
|
|
488
|
+
// Universal event binding
|
|
489
|
+
Aegis.on('.field', 'focus blur', handler); // Multiple events!
|
|
490
|
+
Aegis.once('#btn', 'click', handler); // Fire once only
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Element Selection & Manipulation
|
|
494
|
+
|
|
495
|
+
```javascript
|
|
496
|
+
// Select elements
|
|
497
|
+
const el = Aegis.get('#my-id');
|
|
498
|
+
const all = Aegis.getAll('.cards');
|
|
499
|
+
|
|
500
|
+
// Create elements
|
|
501
|
+
Aegis.create('div', {{
|
|
502
|
+
id: 'my-div',
|
|
503
|
+
class: 'card active',
|
|
504
|
+
html: '<h1>Hello!</h1>',
|
|
505
|
+
style: {{ color: 'red' }},
|
|
506
|
+
on: {{ click: handler }},
|
|
507
|
+
parent: '#container'
|
|
508
|
+
}});
|
|
509
|
+
|
|
510
|
+
// Classes
|
|
511
|
+
Aegis.addClass('#el', 'active');
|
|
512
|
+
Aegis.removeClass('#el', 'hidden');
|
|
513
|
+
Aegis.toggleClass('#el', 'visible');
|
|
514
|
+
|
|
515
|
+
// Styles & Visibility
|
|
516
|
+
Aegis.css('#el', {{ color: 'blue', fontSize: '18px' }});
|
|
517
|
+
Aegis.hide('#modal');
|
|
518
|
+
Aegis.show('#modal');
|
|
519
|
+
Aegis.fadeIn('#el', 300);
|
|
520
|
+
Aegis.fadeOut('#el', 300);
|
|
521
|
+
|
|
522
|
+
// Content
|
|
523
|
+
Aegis.html('#container', '<p>New content</p>');
|
|
524
|
+
Aegis.text('#title', 'Hello World');
|
|
525
|
+
Aegis.val('#input', 'value');
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Forms
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
// Serialize form to object
|
|
532
|
+
const data = Aegis.form.serialize('#my-form');
|
|
533
|
+
// {{ name: 'John', email: 'john@email.com' }}
|
|
534
|
+
|
|
535
|
+
// Fill form with data
|
|
536
|
+
Aegis.form.fill('#form', {{ name: 'Jane', email: 'jane@email.com' }});
|
|
537
|
+
|
|
538
|
+
// Validate
|
|
539
|
+
const result = Aegis.form.validate('#form', {{
|
|
540
|
+
email: {{ required: true, email: true }},
|
|
541
|
+
password: {{ required: true, minLength: 8 }}
|
|
542
|
+
}});
|
|
543
|
+
if (!result.valid) console.log(result.errors);
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## HTTP Requests
|
|
547
|
+
|
|
548
|
+
```javascript
|
|
549
|
+
const users = await Aegis.http.get('/api/users');
|
|
550
|
+
await Aegis.http.post('/api/users', {{ name: 'John' }});
|
|
551
|
+
await Aegis.http.put('/api/users/1', {{ name: 'Jane' }});
|
|
552
|
+
await Aegis.http.delete('/api/users/1');
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
## String Utilities
|
|
556
|
+
|
|
557
|
+
```javascript
|
|
558
|
+
Aegis.string.capitalize('hello'); // 'Hello'
|
|
559
|
+
Aegis.string.slugify('Hello World!'); // 'hello-world'
|
|
560
|
+
Aegis.string.camelCase('hello-world'); // 'helloWorld'
|
|
561
|
+
Aegis.string.truncate('Long text', 5); // 'Long ...'
|
|
562
|
+
Aegis.string.template('Hi {{{{name}}}}!', {{ name: 'John' }}); // 'Hi John!'
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Array Utilities
|
|
566
|
+
|
|
567
|
+
```javascript
|
|
568
|
+
Aegis.array.unique([1, 2, 2, 3]); // [1, 2, 3]
|
|
569
|
+
Aegis.array.shuffle([1, 2, 3]); // Random order
|
|
570
|
+
Aegis.array.groupBy(users, 'role'); // Group by key
|
|
571
|
+
Aegis.array.sortBy(users, 'name'); // Sort by key
|
|
572
|
+
Aegis.array.chunk([1,2,3,4,5], 2); // [[1,2], [3,4], [5]]
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Object Utilities
|
|
576
|
+
|
|
577
|
+
```javascript
|
|
578
|
+
Aegis.object.clone(obj); // Deep clone
|
|
579
|
+
Aegis.object.merge(obj1, obj2); // Merge
|
|
580
|
+
Aegis.object.pick(obj, ['name']); // Pick keys
|
|
581
|
+
Aegis.object.get(user, 'address.city'); // Deep get
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
## Date Utilities
|
|
585
|
+
|
|
586
|
+
```javascript
|
|
587
|
+
Aegis.date.format(date, 'YYYY-MM-DD'); // '2024-12-28'
|
|
588
|
+
Aegis.date.ago(date); // '5 minutes ago'
|
|
589
|
+
Aegis.date.isToday(date); // true/false
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
## Number Utilities
|
|
593
|
+
|
|
594
|
+
```javascript
|
|
595
|
+
Aegis.number.format(1234567); // '1.234.567'
|
|
596
|
+
Aegis.number.currency(1234.56); // 'R$ 1.234,56'
|
|
597
|
+
Aegis.number.bytes(1536000); // '1.46 MB'
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
## Toast Notifications
|
|
601
|
+
|
|
602
|
+
```javascript
|
|
603
|
+
Aegis.toast('Hello!');
|
|
604
|
+
Aegis.toast('Success!', {{ type: 'success' }});
|
|
605
|
+
Aegis.toast('Error!', {{ type: 'error', duration: 5000 }});
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Storage
|
|
609
|
+
|
|
610
|
+
```javascript
|
|
611
|
+
Aegis.storage.set('user', {{ name: 'John' }}); // Auto JSON
|
|
612
|
+
Aegis.storage.get('user'); // Auto parse
|
|
613
|
+
Aegis.storage.remove('user');
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
## Validation
|
|
617
|
+
|
|
618
|
+
```javascript
|
|
619
|
+
Aegis.is.email('test@email.com'); // true
|
|
620
|
+
Aegis.is.url('https://...'); // true
|
|
621
|
+
Aegis.is.empty([]); // true
|
|
622
|
+
Aegis.is.mobile(); // true if mobile
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
## More Utilities
|
|
626
|
+
|
|
627
|
+
```javascript
|
|
628
|
+
await Aegis.clipboard.copy('text'); // Copy to clipboard
|
|
629
|
+
Aegis.debounce(fn, 300); // Debounce
|
|
630
|
+
Aegis.throttle(fn, 100); // Throttle
|
|
631
|
+
await Aegis.delay(1000); // Wait 1 second
|
|
632
|
+
Aegis.uid('card'); // 'card-123456789-abc'
|
|
633
|
+
Aegis.random.uuid(); // Full UUID
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
# 🔌 Backend API (Python Bridge)
|
|
639
|
+
|
|
640
|
+
## File Operations
|
|
469
641
|
|
|
470
642
|
```javascript
|
|
471
643
|
// Read directory contents
|
|
472
644
|
const dir = await Aegis.read({{ path: '/home/user' }});
|
|
473
|
-
console.log(dir.entries);
|
|
645
|
+
console.log(dir.entries);
|
|
474
646
|
|
|
475
647
|
// Read file content
|
|
476
648
|
const file = await Aegis.read({{ path: '/home/user', file: 'data.txt' }});
|
|
477
|
-
console.log(file.content);
|
|
478
649
|
|
|
479
650
|
// Write file
|
|
480
651
|
await Aegis.write({{
|
|
@@ -483,166 +654,76 @@ await Aegis.write({{
|
|
|
483
654
|
content: 'Hello, Aegis!'
|
|
484
655
|
}});
|
|
485
656
|
|
|
486
|
-
// Check if
|
|
657
|
+
// Check if exists
|
|
487
658
|
const info = await Aegis.exists({{ path: '/home/user/file.txt' }});
|
|
488
|
-
if (info.exists && info.isFile) {{
|
|
489
|
-
console.log('File exists!');
|
|
490
|
-
}}
|
|
491
659
|
|
|
492
660
|
// Create directory
|
|
493
661
|
await Aegis.mkdir({{ path: '/home/user/new-folder' }});
|
|
494
662
|
|
|
495
|
-
// Delete
|
|
663
|
+
// Delete
|
|
496
664
|
await Aegis.remove({{ path: '/home/user/old-file.txt' }});
|
|
497
|
-
await Aegis.remove({{ path: '/home/user/old-folder', recursive: true }});
|
|
498
|
-
|
|
499
|
-
// Copy file or directory
|
|
500
|
-
await Aegis.copy({{
|
|
501
|
-
src: '/home/user/file.txt',
|
|
502
|
-
dest: '/home/user/backup/file.txt'
|
|
503
|
-
}});
|
|
504
665
|
|
|
505
|
-
//
|
|
506
|
-
await Aegis.
|
|
507
|
-
|
|
508
|
-
dest: '/home/user/new-name.txt'
|
|
509
|
-
}});
|
|
666
|
+
// Copy & Move
|
|
667
|
+
await Aegis.copy({{ src: '/from', dest: '/to' }});
|
|
668
|
+
await Aegis.move({{ src: '/from', dest: '/to' }});
|
|
510
669
|
```
|
|
511
670
|
|
|
512
|
-
|
|
671
|
+
## Execute Commands
|
|
513
672
|
|
|
514
673
|
```javascript
|
|
515
|
-
//
|
|
674
|
+
// Shell command
|
|
516
675
|
const result = await Aegis.run({{ sh: 'ls -la' }});
|
|
517
|
-
console.log(result.output);
|
|
518
|
-
console.log(result.exitCode);
|
|
519
|
-
|
|
520
|
-
// Run Python code
|
|
521
|
-
const pyResult = await Aegis.run({{ py: 'print(2 + 2)' }});
|
|
522
|
-
console.log(pyResult.output); // "4"
|
|
523
676
|
|
|
524
|
-
//
|
|
677
|
+
// Async with streaming (UI doesn't freeze!)
|
|
525
678
|
await Aegis.runAsync(
|
|
526
|
-
{{ sh: '
|
|
527
|
-
(progress) =>
|
|
528
|
-
console.log(progress.line); // Each line as it comes
|
|
529
|
-
}}
|
|
679
|
+
{{ sh: 'long-command' }},
|
|
680
|
+
(progress) => console.log(progress.line)
|
|
530
681
|
);
|
|
531
682
|
```
|
|
532
683
|
|
|
533
|
-
|
|
684
|
+
## Dialogs
|
|
534
685
|
|
|
535
686
|
```javascript
|
|
536
|
-
|
|
537
|
-
await Aegis.dialog.
|
|
538
|
-
|
|
539
|
-
title: 'Success',
|
|
540
|
-
message: 'Operation completed!'
|
|
541
|
-
}});
|
|
542
|
-
|
|
543
|
-
// Confirmation dialog
|
|
544
|
-
const confirm = await Aegis.dialog.message({{
|
|
545
|
-
type: 'question',
|
|
546
|
-
title: 'Confirm',
|
|
547
|
-
message: 'Are you sure?',
|
|
548
|
-
buttons: 'yesno'
|
|
549
|
-
}});
|
|
550
|
-
if (confirm.response) {{
|
|
551
|
-
// User clicked Yes
|
|
552
|
-
}}
|
|
553
|
-
|
|
554
|
-
// Open file dialog
|
|
555
|
-
const file = await Aegis.dialog.open({{
|
|
556
|
-
title: 'Select a file',
|
|
557
|
-
filters: [{{ name: 'Images', extensions: ['png', 'jpg', 'gif'] }}]
|
|
558
|
-
}});
|
|
559
|
-
console.log(file.path);
|
|
560
|
-
|
|
561
|
-
// Save file dialog
|
|
562
|
-
const savePath = await Aegis.dialog.save({{
|
|
563
|
-
title: 'Save as',
|
|
564
|
-
defaultName: 'document.txt'
|
|
565
|
-
}});
|
|
687
|
+
await Aegis.dialog.message({{ type: 'info', title: 'Hi', message: 'Hello!' }});
|
|
688
|
+
const file = await Aegis.dialog.open({{ title: 'Select file' }});
|
|
689
|
+
const path = await Aegis.dialog.save({{ title: 'Save as' }});
|
|
566
690
|
```
|
|
567
691
|
|
|
568
|
-
|
|
692
|
+
## Download with Progress
|
|
569
693
|
|
|
570
694
|
```javascript
|
|
571
|
-
// Download file with progress bar
|
|
572
695
|
await Aegis.download(
|
|
573
|
-
{{
|
|
574
|
-
|
|
575
|
-
dest: '/home/user/downloads/file.zip'
|
|
576
|
-
}},
|
|
577
|
-
(progress) => {{
|
|
578
|
-
const percent = progress.percent.toFixed(1);
|
|
579
|
-
progressBar.style.width = percent + '%';
|
|
580
|
-
statusText.textContent = `${{progress.downloaded}} / ${{progress.total}} bytes`;
|
|
581
|
-
}}
|
|
696
|
+
{{ url: 'https://example.com/file.zip', dest: '/path/file.zip' }},
|
|
697
|
+
(progress) => console.log(progress.percent + '%')
|
|
582
698
|
);
|
|
583
699
|
```
|
|
584
700
|
|
|
585
|
-
|
|
701
|
+
## App Control
|
|
586
702
|
|
|
587
703
|
```javascript
|
|
588
|
-
// Window controls
|
|
589
704
|
Aegis.app.minimize();
|
|
590
705
|
Aegis.app.maximize();
|
|
591
706
|
Aegis.app.quit();
|
|
592
|
-
|
|
593
|
-
// Get system paths (localized for your language!)
|
|
594
707
|
const home = await Aegis.app.getPath({{ name: 'home' }});
|
|
595
|
-
const docs = await Aegis.app.getPath({{ name: 'documents' }}); // Returns "Documentos" on pt-BR
|
|
596
|
-
const downloads = await Aegis.app.getPath({{ name: 'downloads' }});
|
|
597
|
-
// Also: desktop, music, pictures, videos
|
|
598
708
|
```
|
|
599
709
|
|
|
600
|
-
|
|
710
|
+
## Window Control (Frameless)
|
|
601
711
|
|
|
602
712
|
```javascript
|
|
603
|
-
// Make element draggable for window movement
|
|
604
713
|
Aegis.window.moveBar('#titlebar', {{ exclude: '.btn-close' }});
|
|
605
|
-
|
|
606
|
-
// Setup resize handles
|
|
607
|
-
Aegis.window.resizeHandles({{
|
|
608
|
-
'.resize-n': 'n',
|
|
609
|
-
'.resize-s': 's',
|
|
610
|
-
'.resize-se': 'se',
|
|
611
|
-
// Options: n, s, e, w, ne, nw, se, sw
|
|
612
|
-
}});
|
|
613
|
-
|
|
614
|
-
// Get/set window size
|
|
615
|
-
const size = await Aegis.window.getSize();
|
|
616
|
-
await Aegis.window.setSize({{ width: 1024, height: 768 }});
|
|
617
|
-
|
|
618
|
-
// Get/set window position
|
|
619
|
-
const pos = await Aegis.window.getPosition();
|
|
620
|
-
await Aegis.window.setPosition({{ x: 100, y: 100 }});
|
|
714
|
+
Aegis.window.resizeHandles({{ '.resize-se': 'se' }});
|
|
621
715
|
```
|
|
622
716
|
|
|
623
|
-
|
|
717
|
+
---
|
|
624
718
|
|
|
625
|
-
|
|
719
|
+
## 🔒 Security (preload.js)
|
|
626
720
|
|
|
627
721
|
```javascript
|
|
628
|
-
// src/preload.js
|
|
629
722
|
Aegis.expose([
|
|
630
|
-
'read',
|
|
631
|
-
'
|
|
632
|
-
'run', // Command execution
|
|
633
|
-
'dialog', // Native dialogs
|
|
634
|
-
'app', // App control
|
|
635
|
-
'window', // Window control
|
|
636
|
-
'download', // Download with progress
|
|
637
|
-
'exists', // File existence
|
|
638
|
-
'mkdir', // Create directories
|
|
639
|
-
'remove', // Delete files
|
|
640
|
-
'copy', // Copy files
|
|
641
|
-
'move' // Move/rename files
|
|
723
|
+
'read', 'write', 'run', 'dialog', 'app', 'window',
|
|
724
|
+
'download', 'exists', 'mkdir', 'remove', 'copy', 'move'
|
|
642
725
|
]);
|
|
643
|
-
|
|
644
|
-
// For maximum security, only expose what you need!
|
|
645
|
-
// If you omit 'run', the app cannot execute shell commands
|
|
726
|
+
// Omit 'run' to prevent shell command execution!
|
|
646
727
|
```
|
|
647
728
|
|
|
648
729
|
## ⚙️ Configuration (aegis.config.json)
|
|
@@ -650,53 +731,36 @@ Aegis.expose([
|
|
|
650
731
|
```json
|
|
651
732
|
{{
|
|
652
733
|
"name": "{project_name}",
|
|
653
|
-
"title": "My
|
|
654
|
-
"version": "1.0.0",
|
|
655
|
-
"main": "src/index.html",
|
|
656
|
-
"preload": "src/preload.js",
|
|
734
|
+
"title": "My App",
|
|
657
735
|
"width": 1200,
|
|
658
736
|
"height": 800,
|
|
659
|
-
"resizable": true,
|
|
660
737
|
"frame": true,
|
|
661
|
-
"
|
|
738
|
+
"resizable": true
|
|
662
739
|
}}
|
|
663
740
|
```
|
|
664
741
|
|
|
665
|
-
|
|
666
|
-
|--------|-------------|
|
|
667
|
-
| `frame` | Set to `false` for frameless window (custom titlebar) |
|
|
668
|
-
| `resizable` | Allow window resizing |
|
|
669
|
-
| `width/height` | Initial window size |
|
|
670
|
-
| `devTools` | Enable right-click → Inspect Element |
|
|
671
|
-
|
|
672
|
-
## 📦 Building AppImage
|
|
742
|
+
## 📦 Build AppImage
|
|
673
743
|
|
|
674
744
|
```bash
|
|
675
745
|
aegis build
|
|
676
746
|
```
|
|
677
747
|
|
|
678
|
-
|
|
748
|
+
Creates a portable ~200KB AppImage in `dist/`.
|
|
679
749
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
## 🆚 Why Aegis over Electron?
|
|
750
|
+
## 🆚 Aegis vs Electron
|
|
683
751
|
|
|
684
752
|
| Aspect | Electron | Aegis |
|
|
685
753
|
|--------|----------|-------|
|
|
686
754
|
| App Size | ~150 MB | **~200 KB** |
|
|
687
|
-
|
|
|
688
|
-
| Renderer | Chromium (bundled) | WebKit2GTK (system) |
|
|
689
|
-
| RAM Usage | High (~100MB+) | Low (~30MB) |
|
|
755
|
+
| RAM Usage | ~100 MB | ~30 MB |
|
|
690
756
|
| Platform | Cross-platform | Linux |
|
|
691
757
|
|
|
692
|
-
## 📚
|
|
693
|
-
|
|
694
|
-
- [Aegis GitHub](https://github.com/Diegopam/aegis-framework)
|
|
695
|
-
- [npm Package](https://www.npmjs.com/package/aegis-framework)
|
|
758
|
+
## 📚 More Info
|
|
696
759
|
|
|
697
|
-
|
|
760
|
+
- [GitHub](https://github.com/Diegopam/aegis-framework)
|
|
761
|
+
- [npm](https://www.npmjs.com/package/aegis-framework)
|
|
698
762
|
|
|
699
|
-
MIT
|
|
763
|
+
MIT License
|
|
700
764
|
'''
|
|
701
765
|
with open(project_dir / 'README.md', 'w') as f:
|
|
702
766
|
f.write(readme_content)
|
package/aegis/core/window.py
CHANGED
|
@@ -118,7 +118,8 @@ class AegisWindow(Gtk.Window):
|
|
|
118
118
|
print(f"[Aegis] Action: {action}, Payload: {payload}")
|
|
119
119
|
|
|
120
120
|
# Check if this is an async action
|
|
121
|
-
|
|
121
|
+
# 'run' is now async by default to prevent UI freezing
|
|
122
|
+
async_actions = {'run', 'run.async', 'download', 'copy.async'}
|
|
122
123
|
|
|
123
124
|
if action in async_actions:
|
|
124
125
|
# Handle in background thread - response sent via callback
|
|
@@ -241,35 +242,45 @@ class AegisWindow(Gtk.Window):
|
|
|
241
242
|
|
|
242
243
|
return {'success': True, 'path': full_path}
|
|
243
244
|
|
|
244
|
-
def _handle_run(self, payload):
|
|
245
|
-
"""Execute Python or shell commands"""
|
|
245
|
+
def _handle_run(self, payload, callback_id=None):
|
|
246
|
+
"""Execute Python or shell commands (Thread-Safe)"""
|
|
246
247
|
import subprocess
|
|
247
248
|
|
|
249
|
+
result = {'error': 'No command specified'}
|
|
250
|
+
|
|
248
251
|
if 'py' in payload:
|
|
249
252
|
# Execute Python code
|
|
250
253
|
try:
|
|
251
|
-
|
|
252
|
-
|
|
254
|
+
res = eval(payload['py'])
|
|
255
|
+
result = {'output': str(res), 'exitCode': 0}
|
|
253
256
|
except:
|
|
254
257
|
exec_globals = {}
|
|
255
258
|
exec(payload['py'], exec_globals)
|
|
256
|
-
|
|
259
|
+
result = {'output': '', 'exitCode': 0}
|
|
257
260
|
|
|
258
261
|
elif 'sh' in payload:
|
|
259
262
|
# Execute shell command
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
263
|
+
try:
|
|
264
|
+
# Ensure we wait here in the thread, not blocking UI
|
|
265
|
+
proc_res = subprocess.run(
|
|
266
|
+
payload['sh'],
|
|
267
|
+
shell=True,
|
|
268
|
+
capture_output=True,
|
|
269
|
+
text=True
|
|
270
|
+
)
|
|
271
|
+
result = {
|
|
272
|
+
'output': proc_res.stdout,
|
|
273
|
+
'error': proc_res.stderr,
|
|
274
|
+
'exitCode': proc_res.returncode
|
|
275
|
+
}
|
|
276
|
+
except Exception as e:
|
|
277
|
+
result = {'error': str(e), 'exitCode': -1}
|
|
271
278
|
|
|
272
|
-
|
|
279
|
+
# If running async (callback_id present), send response to main thread
|
|
280
|
+
if callback_id:
|
|
281
|
+
GLib.idle_add(self._send_response, callback_id, result)
|
|
282
|
+
else:
|
|
283
|
+
return result
|
|
273
284
|
|
|
274
285
|
def _handle_exists(self, payload):
|
|
275
286
|
"""Check if path exists"""
|
|
@@ -610,6 +621,7 @@ class AegisWindow(Gtk.Window):
|
|
|
610
621
|
def _process_async_action(self, action, payload, callback_id):
|
|
611
622
|
"""Process async actions in background threads"""
|
|
612
623
|
handlers = {
|
|
624
|
+
'run': self._handle_run, # Now supported in background thread
|
|
613
625
|
'run.async': self._handle_run_async,
|
|
614
626
|
'download': self._handle_download,
|
|
615
627
|
'copy.async': self._handle_copy_async,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aegis-framework",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Lightweight AppImage framework using WebKit2GTK and Python - An alternative to Electron that creates ~200KB apps instead of 150MB!",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"appimage",
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=14"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|