projax 1.3.10 → 1.3.11

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.
Files changed (43) hide show
  1. package/dist/api/database.d.ts +1 -0
  2. package/dist/api/database.d.ts.map +1 -1
  3. package/dist/api/database.js +19 -1
  4. package/dist/api/database.js.map +1 -1
  5. package/dist/api/package.json +1 -3
  6. package/dist/api/routes/projects.d.ts.map +1 -1
  7. package/dist/api/routes/projects.js +21 -7
  8. package/dist/api/routes/projects.js.map +1 -1
  9. package/dist/api/types.d.ts +1 -0
  10. package/dist/api/types.d.ts.map +1 -1
  11. package/dist/core/database.d.ts +1 -0
  12. package/dist/electron/core/database.d.ts +1 -0
  13. package/dist/electron/main.js +37 -40
  14. package/dist/electron/preload.d.ts +1 -0
  15. package/dist/electron/preload.js +1 -0
  16. package/dist/electron/renderer/assets/index-59AhiV_K.css +1 -0
  17. package/dist/electron/renderer/assets/index-BGV4pGGx.js +46 -0
  18. package/dist/electron/renderer/assets/index-BT_e6Msc.js +46 -0
  19. package/dist/electron/renderer/assets/index-Bu1Gyxs5.js +46 -0
  20. package/dist/electron/renderer/assets/index-C9bA-P-3.css +1 -0
  21. package/dist/electron/renderer/assets/index-CJlaCHj5.css +1 -0
  22. package/dist/electron/renderer/assets/index-CTxba8Fs.js +61 -0
  23. package/dist/electron/renderer/assets/index-C_6HLZ3g.js +46 -0
  24. package/dist/electron/renderer/assets/index-Cejn7BcN.js +46 -0
  25. package/dist/electron/renderer/assets/index-CfEtNYYJ.js +46 -0
  26. package/dist/electron/renderer/assets/index-CgMqjX1w.css +1 -0
  27. package/dist/electron/renderer/assets/index-CknIIag9.js +61 -0
  28. package/dist/electron/renderer/assets/index-Csg1hzAW.js +61 -0
  29. package/dist/electron/renderer/assets/index-CtD66H57.js +46 -0
  30. package/dist/electron/renderer/assets/index-Cz5tpCH_.css +1 -0
  31. package/dist/electron/renderer/assets/index-CzSAm4rV.js +46 -0
  32. package/dist/electron/renderer/assets/index-D7t8lZFK.js +46 -0
  33. package/dist/electron/renderer/assets/index-DAfjuYKX.js +61 -0
  34. package/dist/electron/renderer/assets/index-DPZN2_De.css +1 -0
  35. package/dist/electron/renderer/assets/index-DZfKv3qb.js +61 -0
  36. package/dist/electron/renderer/assets/index-DmLmfvuw.css +1 -0
  37. package/dist/electron/renderer/assets/index-DyhB_yB5.js +61 -0
  38. package/dist/electron/renderer/assets/index-IV4RpG6x.css +1 -0
  39. package/dist/electron/renderer/assets/index-O6z5EG_7.js +46 -0
  40. package/dist/electron/renderer/assets/index-_QUmEi7R.css +1 -0
  41. package/dist/electron/renderer/index.html +2 -2
  42. package/dist/index.js +276 -1
  43. package/package.json +15 -3
@@ -0,0 +1 @@
1
+ .project-list{padding:.75rem}.project-list-loading,.project-list-empty{padding:2rem;text-align:center;color:var(--text-secondary)}.project-list-empty .hint{font-size:12px;color:var(--text-tertiary);margin-top:.5rem}.project-item{padding:.875rem;margin-bottom:.5rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;cursor:pointer;transition:all .15s ease;position:relative}.project-item:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:transparent;transition:background .15s ease}.project-item:hover{background:var(--bg-hover);border-color:var(--border-hover);transform:translate(2px)}.project-item:hover:before{background:var(--accent-cyan)}.project-item.selected{background:var(--bg-hover);border-color:var(--accent-cyan);box-shadow:var(--shadow-sm)}.project-item.keyboard-focused{outline:2px solid var(--accent-cyan);outline-offset:-2px;background:var(--bg-hover)}.project-item.selected:before{background:var(--accent-cyan)}.project-item-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:.5rem;gap:.5rem}.project-name{font-size:13px;font-weight:600;color:var(--text-primary);margin:0;flex:1;font-family:inherit;letter-spacing:.2px}.project-remove-btn{background:transparent;border:1px solid transparent;font-size:18px;color:var(--text-tertiary);cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s ease;flex-shrink:0;line-height:1}.project-remove-btn:hover{background:#ff52521a;color:#ff5252;border-color:#ff52524d}.project-path{font-size:11px;color:var(--text-tertiary);margin:.25rem 0;word-break:break-all;font-family:SF Mono,Monaco,monospace;opacity:.8}.project-meta{display:flex;justify-content:space-between;align-items:center;margin-top:.5rem;font-size:11px;color:var(--text-tertiary);gap:.5rem}.project-tags{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.5rem}.project-tag{background:var(--bg-hover);border:1px solid var(--border-color);padding:.125rem .5rem;border-radius:3px;font-size:10px;font-weight:500;color:var(--accent-cyan);font-family:SF Mono,Monaco,monospace;text-transform:uppercase;letter-spacing:.3px}.project-scanned{flex:1;font-family:SF Mono,Monaco,monospace}.project-scan-btn{background:transparent;border:1px solid var(--border-color);font-size:12px;cursor:pointer;padding:.25rem .5rem;border-radius:3px;transition:all .15s ease;color:var(--text-secondary);font-family:inherit}.project-scan-btn:hover:not(:disabled){background:var(--bg-hover);border-color:var(--accent-green);color:var(--accent-green)}.project-scan-btn:disabled{opacity:.3;cursor:not-allowed}.running-indicator-dot{color:var(--accent-green);margin-right:.5rem;font-size:10px;animation:pulse 2s infinite}.project-item.running:before{background:var(--accent-green)}.running-count{background:var(--accent-green);color:var(--bg-primary);padding:.125rem .5rem;border-radius:10px;font-size:10px;font-weight:600;font-family:SF Mono,Monaco,monospace}.running-ports{display:flex;gap:.25rem;flex-wrap:wrap}.port-badge{background:var(--accent-blue);color:var(--bg-primary);padding:.125rem .5rem;border-radius:3px;font-size:10px;font-weight:600;font-family:SF Mono,Monaco,monospace}.project-urls-section{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px solid var(--border-color);margin-bottom:1.5rem}.urls-list{display:flex;flex-direction:column;gap:.5rem}.url-item{padding:.875rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;transition:all .15s ease;display:flex;justify-content:space-between;align-items:center;gap:1rem}.url-item:hover{background:var(--bg-hover);border-color:var(--border-hover)}.url-text{font-family:SF Mono,Monaco,monospace;font-size:12px;color:var(--accent-cyan);flex:1;word-break:break-all;cursor:pointer;text-decoration:underline}.url-text:hover{color:var(--accent-blue)}.project-details{max-width:1000px}.project-details-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1.5rem;padding-bottom:1rem;border-bottom:1px solid var(--border-color);gap:1rem}.project-details-header h2{font-size:18px;color:var(--text-primary);margin-bottom:.5rem;font-weight:600;font-family:inherit;letter-spacing:.3px}.project-name-editable{cursor:pointer;transition:color .15s ease}.project-name-editable:hover{color:var(--accent-cyan)}.project-name-edit{margin-bottom:.5rem}.project-name-input{background:var(--bg-tertiary);border:1px solid var(--accent-cyan);border-radius:4px;padding:.5rem;font-size:18px;font-weight:600;color:var(--text-primary);font-family:inherit;width:100%;max-width:400px}.project-name-input:focus{outline:none;box-shadow:0 0 0 2px #39c5cf33}.project-description-edit{margin-bottom:.5rem}.project-description-input{background:var(--bg-tertiary);border:1px solid var(--accent-cyan);border-radius:4px;padding:.5rem;font-size:13px;color:var(--text-primary);font-family:inherit;width:100%;max-width:600px;resize:vertical;min-height:60px}.project-description-input:focus{outline:none;box-shadow:0 0 0 2px #39c5cf33}.project-description{cursor:pointer;color:var(--text-secondary);font-size:13px;transition:color .15s ease;margin-bottom:.5rem}.project-description:hover{color:var(--text-primary)}.header-actions-group{display:flex;gap:.5rem;align-items:center}.project-path{color:var(--text-tertiary);font-size:11px;word-break:break-all;font-family:SF Mono,Monaco,monospace;opacity:.8}.project-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:.75rem;margin-bottom:1.5rem}.stat-card{background:var(--bg-secondary);padding:1rem;border-radius:4px;border:1px solid var(--border-color);text-align:left;transition:all .15s ease}.stat-card:hover{border-color:var(--border-hover);background:var(--bg-tertiary)}.stat-value{font-size:24px;font-weight:600;color:var(--accent-cyan);margin-bottom:.25rem;font-family:SF Mono,Monaco,monospace;line-height:1.2}.stat-label{font-size:11px;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.5px;font-weight:500}.framework-breakdown{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px solid var(--border-color);margin-bottom:1.5rem}.framework-breakdown h3{font-size:13px;color:var(--text-primary);margin-bottom:1rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit}.framework-list{display:flex;flex-wrap:wrap;gap:.5rem}.framework-item{display:flex;align-items:center;gap:.5rem;padding:.375rem .75rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:3px;transition:all .15s ease}.framework-item:hover{border-color:var(--accent-purple);background:var(--bg-hover)}.framework-name{font-weight:500;color:var(--text-primary);font-size:12px;font-family:SF Mono,Monaco,monospace}.framework-count{background:var(--accent-purple);color:var(--bg-primary);padding:.125rem .5rem;border-radius:2px;font-size:11px;font-weight:600;font-family:SF Mono,Monaco,monospace}.tests-section{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px solid var(--border-color);margin-bottom:1.5rem}.tests-section h3{font-size:13px;color:var(--text-primary);margin-bottom:1rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit}.no-tests{padding:2rem;text-align:center;color:var(--text-tertiary);font-size:12px}.tests-list{display:flex;flex-direction:column;gap:.5rem}.test-item{padding:.875rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;transition:all .15s ease}.test-item:hover{background:var(--bg-hover);border-color:var(--border-hover);transform:translate(2px)}.test-file{display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem;gap:.75rem}.test-path{font-family:SF Mono,Monaco,monospace;font-size:12px;color:var(--text-primary);flex:1;word-break:break-all}.test-framework{background:var(--accent-blue);color:var(--bg-primary);padding:.25rem .5rem;border-radius:3px;font-size:10px;font-weight:600;font-family:SF Mono,Monaco,monospace;text-transform:uppercase;letter-spacing:.3px;flex-shrink:0}.test-status{font-size:11px;color:var(--text-tertiary);font-family:SF Mono,Monaco,monospace}.jenkins-placeholder{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px dashed var(--border-color);opacity:.6}.jenkins-placeholder h3{font-size:13px;color:var(--text-secondary);margin-bottom:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit}.placeholder-text{color:var(--text-tertiary);font-style:italic;font-size:12px;line-height:1.6}.danger-zone{background:#f851490d;padding:1.25rem;border-radius:4px;border:1px solid rgba(248,81,73,.3);margin-top:1.5rem}.danger-zone h3{font-size:13px;color:#f85149;margin-bottom:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit}.danger-zone-text{color:var(--text-secondary);font-size:12px;line-height:1.6;margin-bottom:1rem}.danger-zone .btn-danger{background:transparent;border:1px solid #f85149;color:#f85149}.danger-zone .btn-danger:hover:not(:disabled){background:#f85149;color:var(--bg-primary)}.btn-small{padding:.375rem .75rem;font-size:11px}.tags-section{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px solid var(--border-color);margin-bottom:1.5rem}.tags-content{margin-top:.75rem}.tags-list{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.tag-item{background:var(--bg-tertiary);border:1px solid var(--border-color);padding:.375rem .375rem .375rem .625rem;border-radius:4px;font-size:11px;font-weight:500;color:var(--accent-cyan);font-family:SF Mono,Monaco,monospace;text-transform:uppercase;letter-spacing:.3px;display:flex;align-items:center;gap:.375rem;transition:all .15s ease}.tag-item:hover{border-color:var(--accent-cyan)}.tag-remove{background:none;border:none;color:var(--text-tertiary);cursor:pointer;padding:0 4px;font-size:16px;line-height:1;transition:color .15s ease}.tag-remove:hover{color:#f85149}.tag-add-btn{background:transparent;border:1px dashed var(--border-color);padding:.375rem .75rem;border-radius:4px;font-size:11px;font-weight:500;color:var(--text-secondary);cursor:pointer;transition:all .15s ease;text-transform:uppercase;letter-spacing:.3px}.tag-add-btn:hover{border-color:var(--accent-cyan);color:var(--accent-cyan);background:var(--bg-tertiary)}.tag-input-wrapper{position:relative;display:inline-block}.tag-input{background:var(--bg-tertiary);border:1px solid var(--accent-cyan);border-radius:4px;padding:.375rem .625rem;font-size:11px;font-family:SF Mono,Monaco,monospace;color:var(--text-primary);min-width:120px;outline:none}.tag-input:focus{box-shadow:0 0 0 2px #39c5cf33}.tag-suggestions{position:absolute;top:calc(100% + 4px);left:0;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;box-shadow:var(--shadow-md);z-index:100;min-width:100%;max-width:200px}.tag-suggestion{padding:.5rem .75rem;cursor:pointer;font-size:11px;color:var(--text-primary);font-family:SF Mono,Monaco,monospace;transition:all .15s ease}.tag-suggestion:first-child{border-radius:4px 4px 0 0}.tag-suggestion:last-child{border-radius:0 0 4px 4px}.tag-suggestion:hover{background:var(--bg-hover);color:var(--accent-cyan)}.ports-section,.scripts-section,.running-processes-section{background:var(--bg-secondary);padding:1.25rem;border-radius:4px;border:1px solid var(--border-color);margin-bottom:1.5rem}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}.section-header h3{font-size:13px;color:var(--text-primary);font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit;margin:0}.project-type-badge{background:var(--bg-tertiary);border:1px solid var(--border-color);padding:.25rem .5rem;border-radius:3px;font-size:10px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.3px;font-family:SF Mono,Monaco,monospace}.ports-list,.scripts-list,.processes-list{display:flex;flex-direction:column;gap:.5rem}.port-item{padding:.875rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;transition:all .15s ease}.port-item:hover{background:var(--bg-hover);border-color:var(--border-hover)}.port-info{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.port-number{font-family:SF Mono,Monaco,monospace;font-size:13px;font-weight:600;color:var(--accent-cyan)}.port-script{font-family:SF Mono,Monaco,monospace;font-size:11px;color:var(--text-secondary)}.port-source{font-family:SF Mono,Monaco,monospace;font-size:11px;color:var(--text-tertiary);flex:1}.script-item{padding:.875rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;transition:all .15s ease;display:flex;justify-content:space-between;align-items:center;gap:1rem}.script-item:hover{background:var(--bg-hover);border-color:var(--border-hover)}.script-info{display:flex;align-items:center;gap:.75rem;flex:1;flex-wrap:wrap}.script-name{font-family:SF Mono,Monaco,monospace;font-size:13px;font-weight:600;color:var(--text-primary);min-width:80px}.script-command{font-family:SF Mono,Monaco,monospace;font-size:11px;color:var(--text-secondary);flex:1;word-break:break-all}.script-runner{background:var(--bg-hover);border:1px solid var(--border-color);padding:.25rem .5rem;border-radius:3px;font-size:10px;font-weight:600;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.3px;font-family:SF Mono,Monaco,monospace;flex-shrink:0}.script-actions{display:flex;gap:.5rem;flex-shrink:0}.loading-state,.no-scripts{padding:2rem;text-align:center;color:var(--text-tertiary);font-size:12px}.running-indicator{color:var(--accent-green);font-size:12px;margin-right:.5rem;animation:pulse 2s infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.process-item{padding:.875rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;transition:all .15s ease;display:flex;justify-content:space-between;align-items:center;gap:1rem}.process-item:hover{background:var(--bg-hover);border-color:var(--border-hover)}.process-info{display:flex;align-items:center;gap:.75rem;flex:1;flex-wrap:wrap}.process-name{font-family:SF Mono,Monaco,monospace;font-size:13px;font-weight:600;color:var(--text-primary);min-width:100px}.process-pid{font-family:SF Mono,Monaco,monospace;font-size:11px;color:var(--text-secondary)}.process-uptime{font-family:SF Mono,Monaco,monospace;font-size:11px;color:var(--text-tertiary)}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#000000b3;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:1000}.modal-content{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;width:90%;max-width:500px;box-shadow:var(--shadow-lg);animation:modalSlideIn .2s ease}@keyframes modalSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:1.25rem;border-bottom:1px solid var(--border-color)}.modal-header h2{font-size:14px;color:var(--text-primary);margin:0;font-weight:600;text-transform:uppercase;letter-spacing:.5px;font-family:inherit}.modal-close{background:transparent;border:1px solid transparent;font-size:20px;color:var(--text-tertiary);cursor:pointer;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s ease;line-height:1}.modal-close:hover{background:var(--bg-hover);color:var(--text-primary);border-color:var(--border-color)}.modal-content form{padding:1.25rem}.form-group{margin-bottom:1.25rem}.form-group label{display:block;margin-bottom:.5rem;font-weight:500;color:var(--text-primary);font-size:12px;text-transform:uppercase;letter-spacing:.3px;font-family:inherit}.path-input-group{display:flex;gap:.5rem}.path-input-group input{flex:1;padding:.625rem;border:1px solid var(--border-color);border-radius:4px;font-size:12px;font-family:SF Mono,Monaco,monospace;background:var(--bg-tertiary);color:var(--text-primary);transition:all .15s ease}.path-input-group input::placeholder{color:var(--text-tertiary);opacity:.6}.path-input-group input:focus{outline:none;border-color:var(--accent-cyan);box-shadow:0 0 0 2px #39c5cf1a;background:var(--bg-hover)}.path-input-group input:disabled{background:var(--bg-primary);cursor:not-allowed;opacity:.5}.modal-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:1.25rem}.project-search{padding:12px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);position:sticky;top:0;z-index:10}.search-input-group{display:flex;gap:8px;position:relative}.search-input-wrapper{flex:1;position:relative;display:flex;align-items:center}.search-input{flex:1;padding:8px 36px 8px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px;outline:none;transition:border-color .2s;background:var(--bg-tertiary);color:var(--text-primary)}.sort-icon-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text-tertiary);cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s ease}.sort-icon-btn:hover{color:var(--text-primary);background:var(--bg-hover)}.sort-menu{position:absolute;top:calc(100% + 4px);right:0;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;box-shadow:var(--shadow-md);z-index:100;min-width:180px;padding:4px}.sort-menu-item{padding:8px 12px;cursor:pointer;border-radius:3px;font-size:13px;color:var(--text-primary);display:flex;justify-content:space-between;align-items:center;transition:all .15s ease}.sort-menu-item:hover{background:var(--bg-hover)}.sort-menu-item.active{background:var(--bg-hover);color:var(--accent-cyan)}.sort-menu-item .checkmark{color:var(--accent-green);font-size:12px;margin-left:8px}.search-input:focus{border-color:var(--accent-cyan)}.search-input::placeholder{color:var(--text-tertiary)}.search-filter{padding:8px 12px;border:1px solid var(--border-color);border-radius:4px;font-size:14px;background:var(--bg-tertiary);color:var(--text-primary);cursor:pointer;outline:none;transition:border-color .2s}.search-filter:focus{border-color:var(--accent-cyan)}.search-filter:hover{border-color:var(--border-hover);background:var(--bg-hover)}.settings-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.settings-modal{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;width:90%;max-width:600px;max-height:90vh;display:flex;flex-direction:column;box-shadow:var(--shadow-lg)}.settings-header{display:flex;justify-content:space-between;align-items:center;padding:1.5rem;border-bottom:1px solid var(--border-color)}.settings-header h2{font-size:18px;font-weight:600;color:var(--text-primary);margin:0}.settings-close-btn{background:none;border:none;color:var(--text-secondary);font-size:24px;cursor:pointer;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all .15s ease}.settings-close-btn:hover{background:var(--bg-tertiary);color:var(--text-primary)}.settings-content{padding:1.5rem;overflow-y:auto;flex:1}.settings-section{margin-bottom:2rem}.settings-section:last-child{margin-bottom:0}.settings-section h3{font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:1rem;text-transform:uppercase;letter-spacing:.5px}.settings-field{margin-bottom:1rem}.settings-field:last-child{margin-bottom:0}.settings-field label{display:block;font-size:12px;color:var(--text-secondary);margin-bottom:.5rem;text-transform:uppercase;letter-spacing:.3px}.settings-select,.settings-input{width:100%;padding:.75rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;transition:border-color .15s ease}.settings-select:focus,.settings-input:focus{border-color:var(--accent-cyan)}.settings-input{font-family:SF Mono,Monaco,monospace}.settings-footer{display:flex;justify-content:flex-end;gap:.75rem;padding:1.5rem;border-top:1px solid var(--border-color)}.loading-state{padding:3rem;text-align:center;color:var(--text-tertiary);font-size:13px}.app-header{-webkit-app-region:drag;-webkit-user-select:none;user-select:none;display:flex;align-items:center;gap:1rem}.app-header button,.app-header .btn,.app-header .btn-link,.app-header .header-actions{-webkit-app-region:no-drag}.status-bar{background:var(--bg-secondary);border-top:1px solid var(--border-color);padding:.5rem 1.5rem;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;font-size:11px;font-family:SF Mono,Monaco,monospace}.status-bar-content{display:flex;align-items:center;gap:1rem;width:100%;justify-content:space-between}.status-indicator{display:flex;align-items:center;gap:.5rem}.status-dot{width:6px;height:6px;border-radius:50%;display:inline-block}.status-dot.connected{background:var(--accent-green);box-shadow:0 0 4px var(--accent-green)}.status-dot.disconnected{background:#f85149;box-shadow:0 0 4px #f85149}.status-text{color:var(--text-secondary);font-size:11px;text-transform:uppercase;letter-spacing:.5px}.api-port{color:var(--text-tertiary);font-size:11px;font-family:SF Mono,Monaco,monospace}.app{display:flex;flex-direction:column;height:100vh;overflow:hidden;background:var(--bg-primary)}.app-header{background:var(--bg-secondary);border-bottom:1px solid var(--border-color);padding:.5rem 1.5rem;display:flex;align-items:center;justify-content:center;gap:1rem;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);position:relative;z-index:10;flex-shrink:0}.app-header:after{content:"";position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 0%,var(--accent-cyan) 50%,transparent 100%);opacity:.3}.app-logo{margin:0;font-size:12px;font-weight:600;letter-spacing:1.5px;color:var(--accent-cyan);position:absolute;left:50%;transform:translate(-50%)}.header-actions{display:flex;gap:1.5rem;margin-left:auto}.btn-link{padding:0;border:none;background:none;font-size:11px;font-weight:500;cursor:pointer;transition:color .15s ease;font-family:inherit;letter-spacing:.3px;text-transform:uppercase;color:var(--text-secondary);-webkit-app-region:no-drag}.btn-link:hover:not(:disabled){color:var(--text-primary)}.btn-link:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.btn-link-primary{color:var(--accent-cyan)}.btn-link-primary:hover:not(:disabled){color:var(--accent-blue)}.btn{padding:.5rem 1rem;border:1px solid var(--border-color);border-radius:4px;font-size:12px;font-weight:500;cursor:pointer;transition:all .15s ease;font-family:inherit;letter-spacing:.3px;text-transform:uppercase;background:var(--bg-tertiary);color:var(--text-primary)}.btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.btn-primary{background:var(--accent-cyan);color:var(--bg-primary);border-color:var(--accent-cyan);font-weight:600}.btn-primary:hover:not(:disabled){background:var(--accent-blue);border-color:var(--accent-blue);transform:translateY(-1px);box-shadow:var(--shadow-sm)}.btn-secondary{background:transparent;color:var(--text-secondary);border-color:var(--border-color)}.btn-secondary:hover:not(:disabled){background:var(--bg-hover);border-color:var(--border-hover);color:var(--text-primary)}.btn-danger{background:transparent;color:#f85149;border-color:#f85149}.btn-danger:hover:not(:disabled){background:#f851491a;border-color:#f85149;color:#f85149}.app-content{display:flex;flex:1;overflow:hidden;position:relative}.react-resizable-handle{position:absolute;width:4px;right:-2px;top:0;bottom:0;cursor:col-resize;background:transparent;z-index:10}.react-resizable-handle:hover{background:var(--accent-cyan);opacity:.3}.react-resizable-handle:active{background:var(--accent-cyan);opacity:.6}.sidebar{width:100%;height:100%;background:var(--bg-secondary);border-right:1px solid var(--border-color);overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column}.sidebar::-webkit-scrollbar{width:6px}.sidebar::-webkit-scrollbar-track{background:var(--bg-primary)}.sidebar::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:3px}.sidebar::-webkit-scrollbar-thumb:hover{background:var(--border-hover)}.main-content{flex:1;overflow-y:auto;padding:1.5rem;background:var(--bg-primary)}.main-content::-webkit-scrollbar{width:6px}.main-content::-webkit-scrollbar-track{background:var(--bg-primary)}.main-content::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:3px}.main-content::-webkit-scrollbar-thumb:hover{background:var(--border-hover)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-secondary);text-align:center;padding:2rem}.empty-state h2{margin-bottom:.5rem;color:var(--text-primary);font-size:16px;font-weight:500}.empty-state p{color:var(--text-tertiary);font-size:13px}.app-footer{background:var(--bg-secondary);border-top:1px solid var(--border-color);padding:.5rem 1.5rem;flex-shrink:0}*{margin:0;padding:0;box-sizing:border-box}:root{--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #1c2128;--bg-hover: #21262d;--border-color: #30363d;--border-hover: #484f58;--text-primary: #c9d1d9;--text-secondary: #8b949e;--text-tertiary: #6e7681;--accent-cyan: #39c5cf;--accent-blue: #58a6ff;--accent-green: #3fb950;--accent-purple: #bc8cff;--accent-orange: #ffa657;--shadow-sm: 0 1px 3px rgba(0, 0, 0, .3);--shadow-md: 0 4px 12px rgba(0, 0, 0, .4);--shadow-lg: 0 8px 24px rgba(0, 0, 0, .5)}body{font-family:SF Mono,Monaco,Inconsolata,Fira Code,Fira Mono,Droid Sans Mono,Source Code Pro,Menlo,Consolas,Courier New,monospace;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--bg-primary);color:var(--text-primary);font-size:13px;line-height:1.5;-webkit-user-select:none;user-select:none}input,textarea,[contenteditable=true],[contenteditable=""]{-webkit-user-select:text;user-select:text}code{font-family:SF Mono,Monaco,Inconsolata,Fira Code,Fira Mono,Droid Sans Mono,Source Code Pro,Menlo,Consolas,Courier New,monospace}#root{width:100%;height:100vh}
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' http://localhost:* ws://localhost:*;" />
7
7
  <title>projax</title>
8
- <script type="module" crossorigin src="./assets/index-D5r8Ki4V.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-DtjxFwRT.css">
8
+ <script type="module" crossorigin src="./assets/index-DAfjuYKX.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-59AhiV_K.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/dist/index.js CHANGED
@@ -249,6 +249,8 @@ program
249
249
  .description('Add a project to the dashboard')
250
250
  .argument('[path]', 'Path to the project directory')
251
251
  .option('-n, --name <name>', 'Custom name for the project (defaults to directory name)')
252
+ .option('-d, --description <description>', 'Description for the project')
253
+ .option('--tags <tags>', 'Comma-separated list of tags')
252
254
  .action(async (projectPath, options) => {
253
255
  try {
254
256
  let finalPath = projectPath;
@@ -316,8 +318,25 @@ program
316
318
  projectName = nameAnswer.name.trim();
317
319
  }
318
320
  const project = db.addProject(projectName, resolvedPath);
321
+ // Update description and tags if provided
322
+ const updates = {};
323
+ if (options?.description !== undefined) {
324
+ updates.description = options.description.trim() || null;
325
+ }
326
+ if (options?.tags) {
327
+ updates.tags = options.tags.split(',').map(t => t.trim()).filter(Boolean);
328
+ }
329
+ if (Object.keys(updates).length > 0) {
330
+ db.updateProject(project.id, updates);
331
+ }
319
332
  console.log(`✓ Added project: ${project.name} (ID: ${project.id})`);
320
333
  console.log(` Path: ${project.path}`);
334
+ if (options?.description) {
335
+ console.log(` Description: ${options.description}`);
336
+ }
337
+ if (options?.tags) {
338
+ console.log(` Tags: ${options.tags}`);
339
+ }
321
340
  // Ask if user wants to scan for tests
322
341
  const inquirer = (await Promise.resolve().then(() => __importStar(require('inquirer')))).default;
323
342
  const scanAnswer = await inquirer.prompt([
@@ -570,6 +589,262 @@ program
570
589
  process.exit(1);
571
590
  }
572
591
  });
592
+ // Description command
593
+ program
594
+ .command('desc')
595
+ .alias('description')
596
+ .description('Set or get project description')
597
+ .argument('<project>', 'Project ID or name')
598
+ .argument('[description]', 'New description (leave empty to view current)')
599
+ .action((projectIdentifier, description) => {
600
+ try {
601
+ const db = (0, core_bridge_1.getDatabaseManager)();
602
+ const projects = (0, core_bridge_1.getAllProjects)();
603
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
604
+ if (!project) {
605
+ console.error(`Error: Project not found: ${projectIdentifier}`);
606
+ process.exit(1);
607
+ }
608
+ if (description === undefined) {
609
+ // Show current description
610
+ if (project.description) {
611
+ console.log(project.description);
612
+ }
613
+ else {
614
+ console.log('No description set.');
615
+ }
616
+ }
617
+ else {
618
+ // Update description
619
+ const trimmedDesc = description.trim() || null;
620
+ db.updateProject(project.id, { description: trimmedDesc });
621
+ if (trimmedDesc) {
622
+ console.log(`✓ Updated description for "${project.name}": ${trimmedDesc}`);
623
+ }
624
+ else {
625
+ console.log(`✓ Removed description for "${project.name}"`);
626
+ }
627
+ }
628
+ }
629
+ catch (error) {
630
+ console.error('Error managing description:', error instanceof Error ? error.message : error);
631
+ process.exit(1);
632
+ }
633
+ });
634
+ // Tags command
635
+ program
636
+ .command('tags')
637
+ .description('Manage project tags')
638
+ .argument('<project>', 'Project ID or name')
639
+ .argument('[action]', 'Action: add, remove, or list (default: list)', 'list')
640
+ .argument('[tag]', 'Tag name (required for add/remove)')
641
+ .action((projectIdentifier, action = 'list', tag) => {
642
+ try {
643
+ const db = (0, core_bridge_1.getDatabaseManager)();
644
+ const projects = (0, core_bridge_1.getAllProjects)();
645
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
646
+ if (!project) {
647
+ console.error(`Error: Project not found: ${projectIdentifier}`);
648
+ process.exit(1);
649
+ }
650
+ const currentTags = project.tags || [];
651
+ if (action === 'list' || !action) {
652
+ // List tags
653
+ if (currentTags.length === 0) {
654
+ console.log('No tags set for this project.');
655
+ }
656
+ else {
657
+ console.log(`Tags for "${project.name}":`);
658
+ currentTags.forEach(t => console.log(` - ${t}`));
659
+ }
660
+ }
661
+ else if (action === 'add') {
662
+ if (!tag || !tag.trim()) {
663
+ console.error('Error: Tag name is required for add action');
664
+ process.exit(1);
665
+ }
666
+ const newTag = tag.trim();
667
+ if (currentTags.includes(newTag)) {
668
+ console.log(`Tag "${newTag}" already exists for "${project.name}"`);
669
+ }
670
+ else {
671
+ db.updateProject(project.id, { tags: [...currentTags, newTag] });
672
+ console.log(`✓ Added tag "${newTag}" to "${project.name}"`);
673
+ }
674
+ }
675
+ else if (action === 'remove') {
676
+ if (!tag || !tag.trim()) {
677
+ console.error('Error: Tag name is required for remove action');
678
+ process.exit(1);
679
+ }
680
+ const tagToRemove = tag.trim();
681
+ if (!currentTags.includes(tagToRemove)) {
682
+ console.log(`Tag "${tagToRemove}" does not exist for "${project.name}"`);
683
+ }
684
+ else {
685
+ db.updateProject(project.id, { tags: currentTags.filter(t => t !== tagToRemove) });
686
+ console.log(`✓ Removed tag "${tagToRemove}" from "${project.name}"`);
687
+ }
688
+ }
689
+ else {
690
+ console.error(`Error: Unknown action "${action}". Use: list, add, or remove`);
691
+ process.exit(1);
692
+ }
693
+ }
694
+ catch (error) {
695
+ console.error('Error managing tags:', error instanceof Error ? error.message : error);
696
+ process.exit(1);
697
+ }
698
+ });
699
+ // Open command - open project in editor
700
+ program
701
+ .command('open')
702
+ .description('Open project in editor')
703
+ .argument('<project>', 'Project ID or name')
704
+ .action(async (projectIdentifier) => {
705
+ try {
706
+ const projects = (0, core_bridge_1.getAllProjects)();
707
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
708
+ if (!project) {
709
+ console.error(`Error: Project not found: ${projectIdentifier}`);
710
+ process.exit(1);
711
+ }
712
+ // Load settings from core
713
+ const corePath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
714
+ const localCorePath = path.join(__dirname, '..', '..', '..', 'core', 'dist', 'settings');
715
+ let settings;
716
+ try {
717
+ settings = require(corePath);
718
+ }
719
+ catch {
720
+ try {
721
+ settings = require(localCorePath);
722
+ }
723
+ catch {
724
+ console.error('Error: Could not load settings');
725
+ process.exit(1);
726
+ }
727
+ }
728
+ const editorSettings = settings.getEditorSettings();
729
+ let command;
730
+ const args = [project.path];
731
+ if (editorSettings.type === 'custom' && editorSettings.customPath) {
732
+ command = editorSettings.customPath;
733
+ }
734
+ else {
735
+ switch (editorSettings.type) {
736
+ case 'vscode':
737
+ command = 'code';
738
+ break;
739
+ case 'cursor':
740
+ command = 'cursor';
741
+ break;
742
+ case 'windsurf':
743
+ command = 'windsurf';
744
+ break;
745
+ case 'zed':
746
+ command = 'zed';
747
+ break;
748
+ default:
749
+ command = 'code';
750
+ }
751
+ }
752
+ const { spawn } = require('child_process');
753
+ spawn(command, args, {
754
+ detached: true,
755
+ stdio: 'ignore',
756
+ }).unref();
757
+ console.log(`✓ Opening "${project.name}" in ${editorSettings.type || 'editor'}...`);
758
+ }
759
+ catch (error) {
760
+ console.error('Error opening project:', error instanceof Error ? error.message : error);
761
+ process.exit(1);
762
+ }
763
+ });
764
+ // Files command - open project directory
765
+ program
766
+ .command('files')
767
+ .description('Open project directory in file manager')
768
+ .argument('<project>', 'Project ID or name')
769
+ .action((projectIdentifier) => {
770
+ try {
771
+ const projects = (0, core_bridge_1.getAllProjects)();
772
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
773
+ if (!project) {
774
+ console.error(`Error: Project not found: ${projectIdentifier}`);
775
+ process.exit(1);
776
+ }
777
+ let command;
778
+ const args = [project.path];
779
+ if (os.platform() === 'darwin') {
780
+ command = 'open';
781
+ }
782
+ else if (os.platform() === 'win32') {
783
+ command = 'explorer';
784
+ }
785
+ else {
786
+ command = 'xdg-open';
787
+ }
788
+ const { spawn } = require('child_process');
789
+ spawn(command, args, {
790
+ detached: true,
791
+ stdio: 'ignore',
792
+ }).unref();
793
+ console.log(`✓ Opening "${project.name}" directory...`);
794
+ }
795
+ catch (error) {
796
+ console.error('Error opening directory:', error instanceof Error ? error.message : error);
797
+ process.exit(1);
798
+ }
799
+ });
800
+ // URLs command - list detected URLs for a project
801
+ program
802
+ .command('urls')
803
+ .description('List detected URLs for a project')
804
+ .argument('<project>', 'Project ID or name')
805
+ .action(async (projectIdentifier) => {
806
+ try {
807
+ const projects = (0, core_bridge_1.getAllProjects)();
808
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
809
+ if (!project) {
810
+ console.error(`Error: Project not found: ${projectIdentifier}`);
811
+ process.exit(1);
812
+ }
813
+ const urls = new Set();
814
+ // Get URLs from running processes
815
+ const { getRunningProcessesClean } = await Promise.resolve().then(() => __importStar(require('./script-runner')));
816
+ const runningProcesses = await getRunningProcessesClean();
817
+ const projectProcesses = runningProcesses.filter((p) => p.projectPath === project.path);
818
+ for (const process of projectProcesses) {
819
+ if (process.detectedUrls && Array.isArray(process.detectedUrls)) {
820
+ for (const url of process.detectedUrls) {
821
+ urls.add(url);
822
+ }
823
+ }
824
+ }
825
+ // Get URLs from detected ports
826
+ const db = (0, core_bridge_1.getDatabaseManager)();
827
+ const projectPorts = db.getProjectPorts(project.id);
828
+ for (const portInfo of projectPorts) {
829
+ const url = `http://localhost:${portInfo.port}`;
830
+ urls.add(url);
831
+ }
832
+ const urlArray = Array.from(urls).sort();
833
+ if (urlArray.length === 0) {
834
+ console.log(`No URLs detected for "${project.name}"`);
835
+ }
836
+ else {
837
+ console.log(`\nDetected URLs for "${project.name}":`);
838
+ urlArray.forEach((url, idx) => {
839
+ console.log(` ${idx + 1}. ${url}`);
840
+ });
841
+ }
842
+ }
843
+ catch (error) {
844
+ console.error('Error listing URLs:', error instanceof Error ? error.message : error);
845
+ process.exit(1);
846
+ }
847
+ });
573
848
  // Remove command
574
849
  program
575
850
  .command('remove')
@@ -1151,7 +1426,7 @@ program
1151
1426
  // Check if first argument is not a known command
1152
1427
  (async () => {
1153
1428
  const args = process.argv.slice(2);
1154
- const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', '--help', '-h', '--version', '-V'];
1429
+ const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', 'desc', 'description', 'tags', 'open', 'files', 'urls', '--help', '-h', '--version', '-V'];
1155
1430
  // If we have at least 1 argument and first is not a known command, treat as project identifier
1156
1431
  if (args.length >= 1 && !knownCommands.includes(args[0])) {
1157
1432
  const projectIdentifier = args[0];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "1.3.10",
4
- "description": "CLI tool for managing local development projects",
3
+ "version": "1.3.11",
4
+ "description": "Cross-platform project management dashboard for tracking local development projects. Features CLI, Terminal UI, Desktop app, REST API, and built-in tools for test detection, port management, and script execution.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "prx": "./dist/index.js"
@@ -37,7 +37,19 @@
37
37
  "project-management",
38
38
  "dashboard",
39
39
  "cli",
40
- "developer-tools"
40
+ "developer-tools",
41
+ "project-tracker",
42
+ "development-workflow",
43
+ "test-detection",
44
+ "port-management",
45
+ "script-runner",
46
+ "electron",
47
+ "terminal-ui",
48
+ "rest-api",
49
+ "nodejs",
50
+ "python",
51
+ "rust",
52
+ "go"
41
53
  ],
42
54
  "author": "",
43
55
  "license": "MIT"