@nogataka/smart-edit 1.0.2 → 1.0.3
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/README.md +201 -65
- package/dist/smart-edit/cli.js +79 -2
- package/dist/smart-edit/dashboard.js +29 -0
- package/dist/smart-edit/instance-registry.d.ts +43 -0
- package/dist/smart-edit/instance-registry.js +269 -0
- package/dist/smart-edit/resources/dashboard/dashboard.js +9 -9
- package/dist/smart-edit/resources/dashboard/index.css +1 -1
- package/dist/smart-edit/standalone-dashboard.d.ts +32 -0
- package/dist/smart-edit/standalone-dashboard.js +223 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg-primary: #f2f2f2;--bg-secondary: #ffffff;--text-primary: #1a1a1a;--text-secondary: #3d3d3d;--text-muted: #777;--border-color: #cfcfcf;--btn-primary: #2b2b2b;--btn-hover: #1e1e1e;--btn-disabled: #9c9c9c;--btn-text: #f5f5f5;--shadow: 0 2px 4px rgba(0, 0, 0, .12);--tool-highlight: #e5e5e5;--tool-highlight-text: #1a1a1a;--log-debug: #888;--log-info: #1a1a1a;--log-warning: #454545;--log-error: #111111;--stats-header: #ebebeb;--sidebar-width: 240px;--sidebar-width-collapsed: 64px;--topbar-height: 56px;--content-padding: 24px;--card-bg: var(--bg-secondary);--card-border-radius: 12px;--card-shadow: 0 2px 8px rgba(0, 0, 0, .08);--card-shadow-hover: 0 4px 16px rgba(0, 0, 0, .12);--card-padding: 20px;--spacing-xs: 4px;--spacing-sm: 8px;--spacing-md: 16px;--spacing-lg: 24px;--spacing-xl: 32px;--font-size-xs: 11px;--font-size-sm: 13px;--font-size-base: 14px;--font-size-lg: 16px;--font-size-xl: 20px;--font-size-2xl: 24px;--status-success: #22c55e;--status-warning: #f59e0b;--status-error: #ef4444;--status-info: #3b82f6;--input-bg: var(--bg-primary);--input-border: var(--border-color);--input-focus-border: #3b82f6;--input-focus-ring: rgba(59, 130, 246, .3);--nav-item-hover: rgba(0, 0, 0, .04);--nav-item-active: rgba(59, 130, 246, .1);--nav-item-active-border: #3b82f6;--transition-fast: .15s ease;--transition-normal: .2s ease;--transition-slow: .3s ease}[data-theme=dark]{--bg-primary: #121212;--bg-secondary: #1e1e1e;--text-primary: #f1f1f1;--text-secondary: #d0d0d0;--text-muted: #9a9a9a;--border-color: #2c2c2c;--btn-primary: #ededed;--btn-hover: #d6d6d6;--btn-disabled: #4a4a4a;--btn-text: #111111;--shadow: 0 2px 4px rgba(0, 0, 0, .4);--tool-highlight: #2a2a2a;--tool-highlight-text: #f1f1f1;--log-debug: #a0a0a0;--log-info: #f1f1f1;--log-warning: #c7c7c7;--log-error: #ffffff;--stats-header: #262626;--card-bg: var(--bg-secondary);--card-shadow: 0 2px 8px rgba(0, 0, 0, .3);--card-shadow-hover: 0 4px 16px rgba(0, 0, 0, .4);--input-bg: #1a1a1a;--nav-item-hover: rgba(255, 255, 255, .04);--nav-item-active: rgba(59, 130, 246, .15)}*{box-sizing:border-box}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Helvetica,Arial,"Apple Color Emoji","Segoe UI Emoji";margin:0;padding:0;background-color:var(--bg-primary);color:var(--text-primary);transition:background-color .3s ease,color .3s ease;overflow:hidden;height:100vh}#root{height:100%}.header{text-align:center;margin-bottom:20px;font-size:1.5rem;font-weight:700}.log-container{background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;height:100%;min-height:400px;overflow-y:auto;overflow-x:auto;padding:var(--spacing-md);white-space:pre-wrap;font-size:12px;line-height:1.5;color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;transition:background-color .3s ease,border-color .3s ease,color .3s ease}.log-card{height:calc(100vh - var(--topbar-height) - var(--content-padding) * 2 - var(--spacing-md))}.controls{display:flex;align-items:center;gap:var(--spacing-sm);flex-wrap:wrap}.btn{background-color:var(--btn-primary);color:var(--btn-text);border:none;padding:8px 16px;border-radius:4px;cursor:pointer;font-size:14px;transition:background-color .3s ease}.btn:hover{background-color:var(--btn-hover)}.btn:disabled{background-color:var(--btn-disabled);color:var(--text-muted);cursor:not-allowed}.theme-toggle{display:flex;align-items:center;justify-content:center;background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:50%;width:36px;height:36px;padding:0;cursor:pointer;transition:background-color .3s ease,border-color .3s ease}.theme-toggle:hover{background-color:var(--border-color)}.theme-toggle .icon{font-size:18px;line-height:1}.language-toggle{display:flex;align-items:center;gap:4px;background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:18px;padding:6px 10px;cursor:pointer;transition:background-color .3s ease,border-color .3s ease}.language-toggle:hover{background-color:var(--border-color)}.language-toggle .icon{display:flex;align-items:center;color:var(--text-primary)}.language-toggle .language-code{font-size:12px;font-weight:600;color:var(--text-primary)}.log-debug{color:var(--log-debug)}.log-info{color:var(--log-info)}.log-warning{color:var(--log-warning);font-weight:600}.log-error{color:var(--log-error);font-weight:700}.log-default{color:var(--log-info)}.tool-name{background-color:var(--tool-highlight);color:var(--tool-highlight-text);font-weight:700;padding:0 2px;border-radius:2px}.loading{text-align:center;color:var(--text-muted);font-style:italic}.error-message{color:var(--log-error);text-align:center;margin:10px 0}.charts-container{display:flex;flex-wrap:wrap;gap:15px;justify-content:space-between;max-width:1400px;margin:0 auto}.chart-group{flex:1;min-width:280px;max-width:320px;text-align:center}.chart-wide{flex:0 0 100%;min-width:100%;margin-top:10px}.chart-group h3{margin:0 0 10px;color:var(--text-secondary)}.stats-summary{margin:0 auto;border-collapse:collapse;background:var(--bg-secondary);border-radius:5px;overflow:hidden;box-shadow:var(--shadow);transition:background-color .3s ease,box-shadow .3s ease}.stats-summary th,.stats-summary td{padding:10px 20px;text-align:left;border-bottom:1px solid var(--border-color);color:var(--text-primary);transition:border-color .3s ease,color .3s ease}.stats-summary th{background-color:var(--stats-header);font-weight:700;transition:background-color .3s ease}.stats-summary tr:last-child td{border-bottom:none}@media(max-width:768px){.charts-container{flex-direction:column}.chart-group,.chart-wide{min-width:auto;max-width:none}.controls{flex-direction:column;gap:5px}}.main-layout{display:flex;height:100vh;overflow:hidden}.sidebar{width:var(--sidebar-width);height:100%;background-color:var(--bg-secondary);border-right:1px solid var(--border-color);display:flex;flex-direction:column;transition:width var(--transition-normal);flex-shrink:0;overflow:hidden}.sidebar.collapsed{width:var(--sidebar-width-collapsed)}.sidebar-header{height:var(--topbar-height);display:flex;align-items:center;justify-content:space-between;padding:0 var(--spacing-md);border-bottom:1px solid var(--border-color);flex-shrink:0}.sidebar-logo{display:flex;align-items:center;gap:var(--spacing-sm);font-weight:600;font-size:var(--font-size-lg);color:var(--text-primary);white-space:nowrap;overflow:hidden}.sidebar-logo-icon{width:28px;height:28px;flex-shrink:0}.sidebar.collapsed .sidebar-logo-text{display:none}.sidebar-toggle{background:none;border:none;cursor:pointer;padding:var(--spacing-xs);border-radius:4px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:background-color var(--transition-fast)}.sidebar-toggle:hover{background-color:var(--nav-item-hover)}.sidebar-nav{flex:1;padding:var(--spacing-md) var(--spacing-sm);overflow-y:auto}.nav-item{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);border-radius:8px;cursor:pointer;color:var(--text-secondary);transition:all var(--transition-fast);margin-bottom:var(--spacing-xs);border:none;background:none;width:100%;text-align:left;font-size:var(--font-size-base)}.nav-item:hover{background-color:var(--nav-item-hover);color:var(--text-primary)}.nav-item.active{background-color:var(--nav-item-active);color:var(--nav-item-active-border);font-weight:500}.nav-item-icon{width:20px;height:20px;flex-shrink:0;display:flex;align-items:center;justify-content:center}.nav-item-label{white-space:nowrap;overflow:hidden}.sidebar.collapsed .nav-item-label{display:none}.sidebar.collapsed .nav-item{justify-content:center;padding:var(--spacing-sm)}.sidebar-footer{padding:var(--spacing-md) var(--spacing-sm);border-top:1px solid var(--border-color)}.shutdown-btn{color:var(--status-error)!important}.shutdown-btn:hover{background-color:#ef44441a!important;color:var(--status-error)!important}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}.topbar{height:var(--topbar-height);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-color);display:flex;align-items:center;justify-content:space-between;padding:0 var(--content-padding);flex-shrink:0}.topbar-left{display:flex;align-items:center;gap:var(--spacing-md)}.topbar-title{font-size:var(--font-size-lg);font-weight:600;color:var(--text-primary)}.topbar-subtitle{font-size:var(--font-size-sm);color:var(--text-muted)}.topbar-right{display:flex;align-items:center;gap:var(--spacing-md)}.connection-indicator{display:flex;align-items:center;gap:var(--spacing-xs);font-size:var(--font-size-sm);color:var(--text-muted)}.connection-dot{width:8px;height:8px;border-radius:50%;background-color:var(--status-error)}.connection-dot.streaming{background-color:var(--status-success);animation:pulse 2s infinite}.connection-dot.polling{background-color:var(--status-warning)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.content-area{flex:1;overflow-y:auto;padding:var(--content-padding);background-color:var(--bg-primary)}.card{background-color:var(--card-bg);border-radius:var(--card-border-radius);box-shadow:var(--card-shadow);padding:var(--card-padding);transition:box-shadow var(--transition-fast)}.card:hover{box-shadow:var(--card-shadow-hover)}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-md)}.card-title{font-size:var(--font-size-lg);font-weight:600;color:var(--text-primary);margin:0}.card-subtitle{font-size:var(--font-size-sm);color:var(--text-muted);margin:var(--spacing-xs) 0 0 0}.card-actions{display:flex;gap:var(--spacing-sm)}.card-body{color:var(--text-primary)}@media(max-width:768px){.sidebar{position:fixed;left:0;top:0;z-index:100;transform:translate(-100%);transition:transform var(--transition-normal)}.sidebar.open{transform:translate(0)}.sidebar-backdrop{position:fixed;inset:0;background-color:#00000080;z-index:99;opacity:0;visibility:hidden;transition:opacity var(--transition-normal)}.sidebar-backdrop.visible{opacity:1;visibility:visible}.topbar{padding:0 var(--spacing-md)}.content-area{padding:var(--spacing-md)}.mobile-menu-btn{display:flex;background:none;border:none;cursor:pointer;padding:var(--spacing-xs);color:var(--text-primary)}}@media(min-width:769px){.mobile-menu-btn,.sidebar-backdrop{display:none}}.log-panel{display:flex;flex-direction:column;gap:var(--spacing-md);height:100%}.log-panel-header{display:flex;flex-direction:column;gap:var(--spacing-md);margin-bottom:var(--spacing-md)}.log-panel-controls{display:flex;align-items:center;gap:var(--spacing-sm)}.log-panel-content{flex:1;min-height:0}@media(max-width:768px){.log-panel-header{gap:var(--spacing-sm)}}.stats-panel{display:flex;flex-direction:column;gap:var(--spacing-lg)}.stats-panel-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--spacing-md)}.stats-panel-actions{display:flex;gap:var(--spacing-sm)}.stats-estimator{font-size:var(--font-size-sm);color:var(--text-muted)}.stats-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted)}.stats-empty p{margin:0}.stats-empty-hint{font-size:var(--font-size-sm);margin-top:var(--spacing-sm)!important}.metrics-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md)}.metric-card{background-color:var(--card-bg);border-radius:var(--card-border-radius);box-shadow:var(--card-shadow);padding:var(--card-padding);transition:box-shadow var(--transition-fast)}.metric-card:hover{box-shadow:var(--card-shadow-hover)}.metric-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-sm)}.metric-card-title{font-size:var(--font-size-sm);color:var(--text-muted);font-weight:500}.metric-card-icon{color:var(--text-muted)}.metric-card-value{font-size:var(--font-size-2xl);font-weight:700;color:var(--text-primary);line-height:1.2}.metric-card-footer{display:flex;align-items:center;justify-content:space-between;margin-top:var(--spacing-sm)}.metric-card-subtitle{font-size:var(--font-size-xs);color:var(--text-muted)}.metric-card-trend{font-size:var(--font-size-xs);font-weight:600;padding:2px 6px;border-radius:4px}.metric-card-trend.up{color:var(--status-success);background-color:#22c55e1a}.metric-card-trend.down{color:var(--status-error);background-color:#ef44441a}.metric-card-trend.neutral{color:var(--text-muted);background-color:var(--nav-item-hover)}.live-counter{font-variant-numeric:tabular-nums}.stats-charts-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:var(--spacing-lg)}.stats-charts-row{width:100%}.pie-charts-row{display:flex;gap:var(--spacing-lg);justify-content:center;flex-wrap:wrap}.pie-chart-item{flex:1;min-width:250px;max-width:350px}.chart-container{position:relative;width:100%}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:200px;color:var(--text-muted);font-style:italic;text-align:center;gap:var(--spacing-xs)}.chart-empty-hint{font-size:.85em;opacity:.7}.activity-chart-wrapper{display:flex;flex-direction:column;gap:var(--spacing-sm)}.chart-actions{display:flex;justify-content:flex-end;padding-top:var(--spacing-sm)}.btn-sm{padding:4px 8px;font-size:.85em}@media(max-width:768px){.metrics-grid{grid-template-columns:repeat(2,1fr)}.stats-charts-grid{grid-template-columns:1fr}.pie-charts-row{flex-direction:column;align-items:center}.pie-chart-item{max-width:none;width:100%}}.session-panel{display:flex;flex-direction:column;gap:var(--spacing-lg)}.session-panel-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:var(--spacing-lg);flex-wrap:wrap;gap:var(--spacing-md)}.session-panel-title{font-size:var(--font-size-lg);font-weight:600;color:var(--text-primary);margin:0}.session-panel-subtitle{font-size:var(--font-size-sm);color:var(--text-muted);margin:var(--spacing-xs) 0 0 0}.session-panel-actions{display:flex;gap:var(--spacing-sm)}.session-list{display:flex;flex-direction:column;gap:var(--spacing-md)}.session-list-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted)}.session-list-empty p{margin:0}.session-list-empty-hint{font-size:var(--font-size-sm);margin-top:var(--spacing-sm)!important}.session-item{background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:var(--spacing-md);transition:border-color var(--transition-fast)}.session-item:hover{border-color:var(--text-muted)}.session-item-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:var(--spacing-sm)}.session-item-info{display:flex;flex-direction:column;gap:2px}.session-item-date{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary)}.session-item-time{font-size:var(--font-size-sm);color:var(--text-muted)}.session-item-duration{font-size:var(--font-size-sm);color:var(--text-secondary);background-color:var(--nav-item-hover);padding:2px 8px;border-radius:4px}.session-item-project{font-size:var(--font-size-sm);color:var(--text-secondary);margin-bottom:var(--spacing-sm)}.session-item-stats{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-sm)}.session-stat{display:flex;flex-direction:column;gap:2px}.session-stat-label{font-size:var(--font-size-xs);color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px}.session-stat-value{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}.session-item-actions{display:flex;gap:var(--spacing-sm);justify-content:flex-end}.session-action-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;font-size:var(--font-size-sm);color:var(--text-secondary);background:none;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;transition:all var(--transition-fast)}.session-action-btn:hover{color:var(--text-primary);border-color:var(--text-muted);background-color:var(--nav-item-hover)}.btn-danger{background-color:transparent;color:var(--status-error);border:1px solid var(--status-error)}.btn-danger:hover{background-color:var(--status-error);color:#fff}.export-btn{display:flex;align-items:center}@media(max-width:768px){.session-panel-header{flex-direction:column;align-items:stretch}.session-panel-actions{justify-content:flex-end}.session-item-stats{flex-wrap:wrap;gap:var(--spacing-md)}}.dashboard-overview{display:flex;flex-direction:column;gap:var(--spacing-lg)}.welcome-section{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-md)}.welcome-content{flex:1}.welcome-title{font-size:var(--font-size-xl);font-weight:600;color:var(--text-primary);margin:0 0 var(--spacing-xs) 0}.welcome-description{font-size:var(--font-size-base);color:var(--text-secondary);margin:0}.connection-badge{flex-shrink:0}.connection-status{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:20px;font-size:var(--font-size-sm);font-weight:500}.connection-status:before{content:"";width:8px;height:8px;border-radius:50%}.connection-status.streaming{background-color:#22c55e1a;color:var(--status-success)}.connection-status.streaming:before{background-color:var(--status-success);animation:pulse 2s infinite}.connection-status.polling{background-color:#f59e0b1a;color:var(--status-warning)}.connection-status.polling:before{background-color:var(--status-warning)}.connection-status.disconnected{background-color:#ef44441a;color:var(--status-error)}.connection-status.disconnected:before{background-color:var(--status-error)}.quick-nav-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:var(--spacing-md)}.quick-nav-card{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;cursor:pointer;transition:all var(--transition-fast);text-align:left;width:100%}.quick-nav-card:hover{border-color:var(--input-focus-border);background-color:var(--nav-item-active)}.quick-nav-icon{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:8px;background-color:var(--nav-item-active);color:var(--input-focus-border);flex-shrink:0}.quick-nav-content{flex:1;min-width:0}.quick-nav-title{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary);margin:0}.quick-nav-description{font-size:var(--font-size-sm);color:var(--text-muted);margin:4px 0 0}.quick-nav-arrow{color:var(--text-muted);flex-shrink:0;transition:transform var(--transition-fast)}.quick-nav-card:hover .quick-nav-arrow{transform:translate(4px);color:var(--input-focus-border)}@media(max-width:768px){.welcome-section{flex-direction:column}.quick-nav-grid{grid-template-columns:1fr}}.search-input-wrapper{position:relative;display:flex;align-items:center}.search-input-icon{position:absolute;left:12px;color:var(--text-muted);pointer-events:none;display:flex;align-items:center}.search-input{width:100%;padding:8px 36px;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-primary);font-size:var(--font-size-sm);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.search-input:focus{outline:none;border-color:var(--input-focus-border);box-shadow:0 0 0 3px var(--input-focus-ring)}.search-input::placeholder{color:var(--text-muted)}.search-input-clear{position:absolute;right:8px;display:flex;align-items:center;justify-content:center;padding:4px;border:none;background:none;color:var(--text-muted);cursor:pointer;border-radius:4px;transition:color var(--transition-fast),background-color var(--transition-fast)}.search-input-clear:hover{color:var(--text-primary);background-color:var(--nav-item-hover)}.dropdown{position:relative;display:inline-block;min-width:140px}.dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-primary);font-size:var(--font-size-sm);cursor:pointer;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.dropdown-trigger:hover{border-color:var(--text-muted)}.dropdown-trigger:focus{outline:none;border-color:var(--input-focus-border);box-shadow:0 0 0 3px var(--input-focus-ring)}.dropdown-text{flex:1;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-arrow{display:flex;align-items:center;margin-left:8px;color:var(--text-muted);transition:transform var(--transition-fast)}.dropdown-arrow.open{transform:rotate(180deg)}.dropdown-menu{position:absolute;top:100%;left:0;right:0;margin-top:4px;padding:4px;border:1px solid var(--border-color);border-radius:6px;background-color:var(--bg-secondary);box-shadow:var(--card-shadow);z-index:100;max-height:240px;overflow-y:auto}.dropdown-clear{display:block;width:100%;padding:6px 8px;border:none;background:none;color:var(--status-info);font-size:var(--font-size-xs);text-align:left;cursor:pointer;border-bottom:1px solid var(--border-color);margin-bottom:4px}.dropdown-clear:hover{text-decoration:underline}.dropdown-options{display:flex;flex-direction:column;gap:2px}.dropdown-option{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;transition:background-color var(--transition-fast)}.dropdown-option:hover{background-color:var(--nav-item-hover)}.dropdown-option input[type=checkbox],.dropdown-option input[type=radio]{margin:0;accent-color:var(--input-focus-border)}.dropdown-option-label{font-size:var(--font-size-sm);color:var(--text-primary)}.log-filter-bar{display:flex;align-items:center;gap:var(--spacing-md);flex-wrap:wrap}.log-filter-search{flex:1;min-width:200px;max-width:300px}.log-filter-dropdowns{display:flex;align-items:center;gap:var(--spacing-sm)}.log-filter-clear{padding:8px 12px;font-size:var(--font-size-sm)}.icon-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast)}.icon-btn:hover{border-color:var(--text-muted);color:var(--text-primary);background-color:var(--nav-item-hover)}.icon-btn:disabled{opacity:.5;cursor:not-allowed}.reload-btn.spinning svg{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.filter-stats{display:flex;align-items:center;gap:var(--spacing-sm);font-size:var(--font-size-sm);color:var(--text-muted)}.filter-stats-count{font-weight:600;color:var(--text-primary)}@media(max-width:768px){.log-filter-bar{flex-direction:column;align-items:stretch}.log-filter-search{max-width:none}.log-filter-dropdowns{flex-wrap:wrap}.dropdown{flex:1;min-width:120px}}
|
|
1
|
+
:root{--bg-primary: #f2f2f2;--bg-secondary: #ffffff;--text-primary: #1a1a1a;--text-secondary: #3d3d3d;--text-muted: #777;--border-color: #cfcfcf;--btn-primary: #2b2b2b;--btn-hover: #1e1e1e;--btn-disabled: #9c9c9c;--btn-text: #f5f5f5;--shadow: 0 2px 4px rgba(0, 0, 0, .12);--tool-highlight: #e5e5e5;--tool-highlight-text: #1a1a1a;--log-debug: #888;--log-info: #1a1a1a;--log-warning: #454545;--log-error: #111111;--stats-header: #ebebeb;--sidebar-width: 240px;--sidebar-width-collapsed: 64px;--topbar-height: 56px;--content-padding: 24px;--card-bg: var(--bg-secondary);--card-border-radius: 12px;--card-shadow: 0 2px 8px rgba(0, 0, 0, .08);--card-shadow-hover: 0 4px 16px rgba(0, 0, 0, .12);--card-padding: 20px;--spacing-xs: 4px;--spacing-sm: 8px;--spacing-md: 16px;--spacing-lg: 24px;--spacing-xl: 32px;--font-size-xs: 11px;--font-size-sm: 13px;--font-size-base: 14px;--font-size-lg: 16px;--font-size-xl: 20px;--font-size-2xl: 24px;--status-success: #22c55e;--status-warning: #f59e0b;--status-error: #ef4444;--status-info: #3b82f6;--input-bg: var(--bg-primary);--input-border: var(--border-color);--input-focus-border: #3b82f6;--input-focus-ring: rgba(59, 130, 246, .3);--nav-item-hover: rgba(0, 0, 0, .04);--nav-item-active: rgba(59, 130, 246, .1);--nav-item-active-border: #3b82f6;--transition-fast: .15s ease;--transition-normal: .2s ease;--transition-slow: .3s ease}[data-theme=dark]{--bg-primary: #121212;--bg-secondary: #1e1e1e;--text-primary: #f1f1f1;--text-secondary: #d0d0d0;--text-muted: #9a9a9a;--border-color: #2c2c2c;--btn-primary: #ededed;--btn-hover: #d6d6d6;--btn-disabled: #4a4a4a;--btn-text: #111111;--shadow: 0 2px 4px rgba(0, 0, 0, .4);--tool-highlight: #2a2a2a;--tool-highlight-text: #f1f1f1;--log-debug: #a0a0a0;--log-info: #f1f1f1;--log-warning: #c7c7c7;--log-error: #ffffff;--stats-header: #262626;--card-bg: var(--bg-secondary);--card-shadow: 0 2px 8px rgba(0, 0, 0, .3);--card-shadow-hover: 0 4px 16px rgba(0, 0, 0, .4);--input-bg: #1a1a1a;--nav-item-hover: rgba(255, 255, 255, .04);--nav-item-active: rgba(59, 130, 246, .15)}*{box-sizing:border-box}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Helvetica,Arial,"Apple Color Emoji","Segoe UI Emoji";margin:0;padding:0;background-color:var(--bg-primary);color:var(--text-primary);transition:background-color .3s ease,color .3s ease;overflow:hidden;height:100vh}#root{height:100%}.header{text-align:center;margin-bottom:20px;font-size:1.5rem;font-weight:700}.log-container{background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;height:100%;min-height:400px;overflow-y:auto;overflow-x:auto;padding:var(--spacing-md);white-space:pre-wrap;font-size:12px;line-height:1.5;color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;transition:background-color .3s ease,border-color .3s ease,color .3s ease}.log-card{height:calc(100vh - var(--topbar-height) - var(--content-padding) * 2 - var(--spacing-md))}.controls{display:flex;align-items:center;gap:var(--spacing-sm);flex-wrap:wrap}.btn{background-color:var(--btn-primary);color:var(--btn-text);border:none;padding:8px 16px;border-radius:4px;cursor:pointer;font-size:14px;transition:background-color .3s ease}.btn:hover{background-color:var(--btn-hover)}.btn:disabled{background-color:var(--btn-disabled);color:var(--text-muted);cursor:not-allowed}.theme-toggle{display:flex;align-items:center;justify-content:center;background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:50%;width:36px;height:36px;padding:0;cursor:pointer;transition:background-color .3s ease,border-color .3s ease}.theme-toggle:hover{background-color:var(--border-color)}.theme-toggle .icon{font-size:18px;line-height:1}.language-toggle{display:flex;align-items:center;gap:4px;background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:18px;padding:6px 10px;cursor:pointer;transition:background-color .3s ease,border-color .3s ease}.language-toggle:hover{background-color:var(--border-color)}.language-toggle .icon{display:flex;align-items:center;color:var(--text-primary)}.language-toggle .language-code{font-size:12px;font-weight:600;color:var(--text-primary)}.log-debug{color:var(--log-debug)}.log-info{color:var(--log-info)}.log-warning{color:var(--log-warning);font-weight:600}.log-error{color:var(--log-error);font-weight:700}.log-default{color:var(--log-info)}.tool-name{background-color:var(--tool-highlight);color:var(--tool-highlight-text);font-weight:700;padding:0 2px;border-radius:2px}.loading{text-align:center;color:var(--text-muted);font-style:italic}.error-message{color:var(--log-error);text-align:center;margin:10px 0}.charts-container{display:flex;flex-wrap:wrap;gap:15px;justify-content:space-between;max-width:1400px;margin:0 auto}.chart-group{flex:1;min-width:280px;max-width:320px;text-align:center}.chart-wide{flex:0 0 100%;min-width:100%;margin-top:10px}.chart-group h3{margin:0 0 10px;color:var(--text-secondary)}.stats-summary{margin:0 auto;border-collapse:collapse;background:var(--bg-secondary);border-radius:5px;overflow:hidden;box-shadow:var(--shadow);transition:background-color .3s ease,box-shadow .3s ease}.stats-summary th,.stats-summary td{padding:10px 20px;text-align:left;border-bottom:1px solid var(--border-color);color:var(--text-primary);transition:border-color .3s ease,color .3s ease}.stats-summary th{background-color:var(--stats-header);font-weight:700;transition:background-color .3s ease}.stats-summary tr:last-child td{border-bottom:none}@media(max-width:768px){.charts-container{flex-direction:column}.chart-group,.chart-wide{min-width:auto;max-width:none}.controls{flex-direction:column;gap:5px}}.main-layout{display:flex;height:100vh;overflow:hidden}.sidebar{width:var(--sidebar-width);height:100%;background-color:var(--bg-secondary);border-right:1px solid var(--border-color);display:flex;flex-direction:column;transition:width var(--transition-normal);flex-shrink:0;overflow:hidden}.sidebar.collapsed{width:var(--sidebar-width-collapsed)}.sidebar-header{height:var(--topbar-height);display:flex;align-items:center;justify-content:space-between;padding:0 var(--spacing-md);border-bottom:1px solid var(--border-color);flex-shrink:0}.sidebar-logo{display:flex;align-items:center;gap:var(--spacing-sm);font-weight:600;font-size:var(--font-size-lg);color:var(--text-primary);white-space:nowrap;overflow:hidden}.sidebar-logo-icon{width:28px;height:28px;flex-shrink:0}.sidebar.collapsed .sidebar-logo-text{display:none}.sidebar-toggle{background:none;border:none;cursor:pointer;padding:var(--spacing-xs);border-radius:4px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:background-color var(--transition-fast)}.sidebar-toggle:hover{background-color:var(--nav-item-hover)}.sidebar-content{flex:1;padding:var(--spacing-md);overflow-y:auto}.sidebar-section{margin-bottom:var(--spacing-lg)}.sidebar-section-title{font-size:var(--font-size-xs);font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin:0 0 var(--spacing-sm) 0}.project-info{display:flex;flex-direction:column;gap:var(--spacing-sm)}.project-name{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-primary);font-weight:600;font-size:var(--font-size-base)}.project-name-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-path{font-size:var(--font-size-xs);color:var(--text-muted);word-break:break-all;line-height:1.4}.project-empty{color:var(--text-muted);font-size:var(--font-size-sm);font-style:italic}.instance-list{display:flex;flex-direction:column;gap:var(--spacing-xs)}.instance-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border-radius:6px;cursor:pointer;border:none;background:none;color:var(--text-secondary);font-size:var(--font-size-sm);text-align:left;transition:all var(--transition-fast)}.instance-item:hover{background-color:var(--nav-item-hover);color:var(--text-primary)}.instance-item.active{background-color:var(--nav-item-active);color:var(--text-primary)}.instance-indicator{width:8px;height:8px;border-radius:50%;background-color:var(--text-muted);flex-shrink:0}.instance-indicator.active{background-color:var(--status-success)}.instance-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-footer{padding:var(--spacing-md);border-top:1px solid var(--border-color)}.sidebar-action-btn{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border-radius:6px;cursor:pointer;border:none;background:none;font-size:var(--font-size-sm);transition:all var(--transition-fast)}.sidebar-action-btn.shutdown-btn{color:var(--status-error)}.sidebar-action-btn.shutdown-btn:hover{background-color:#ef44441a}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}.topbar{height:var(--topbar-height);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-color);display:flex;align-items:center;justify-content:space-between;padding:0 var(--content-padding);flex-shrink:0}.topbar-nav{display:flex;align-items:center;gap:var(--spacing-xs)}.topbar-nav-item{display:flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs) var(--spacing-md);border-radius:6px;cursor:pointer;color:var(--text-secondary);transition:all var(--transition-fast);border:none;background:none;font-size:var(--font-size-sm);font-weight:500}.topbar-nav-item:hover{background-color:var(--nav-item-hover);color:var(--text-primary)}.topbar-nav-item.active{background-color:var(--nav-item-active);color:var(--nav-item-active-border)}.topbar-nav-icon{display:flex;align-items:center;justify-content:center}.topbar-nav-label{white-space:nowrap}.topbar-right{display:flex;align-items:center;gap:var(--spacing-md)}.connection-indicator{display:flex;align-items:center;gap:var(--spacing-xs);font-size:var(--font-size-sm);color:var(--text-muted)}.connection-dot{width:8px;height:8px;border-radius:50%;background-color:var(--status-error)}.connection-dot.streaming{background-color:var(--status-success);animation:pulse 2s infinite}.connection-dot.polling{background-color:var(--status-warning)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.content-area{flex:1;overflow-y:auto;padding:var(--content-padding);background-color:var(--bg-primary)}.card{background-color:var(--card-bg);border-radius:var(--card-border-radius);box-shadow:var(--card-shadow);padding:var(--card-padding);transition:box-shadow var(--transition-fast)}.card:hover{box-shadow:var(--card-shadow-hover)}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-md)}.card-title{font-size:var(--font-size-lg);font-weight:600;color:var(--text-primary);margin:0}.card-subtitle{font-size:var(--font-size-sm);color:var(--text-muted);margin:var(--spacing-xs) 0 0 0}.card-actions{display:flex;gap:var(--spacing-sm)}.card-body{color:var(--text-primary)}@media(max-width:768px){.sidebar{position:fixed;left:0;top:0;z-index:100;transform:translate(-100%);transition:transform var(--transition-normal)}.sidebar.open{transform:translate(0)}.sidebar-backdrop{position:fixed;inset:0;background-color:#00000080;z-index:99;opacity:0;visibility:hidden;transition:opacity var(--transition-normal)}.sidebar-backdrop.visible{opacity:1;visibility:visible}.topbar{padding:0 var(--spacing-md)}.content-area{padding:var(--spacing-md)}.mobile-menu-btn{display:flex;background:none;border:none;cursor:pointer;padding:var(--spacing-xs);color:var(--text-primary)}}@media(min-width:769px){.mobile-menu-btn,.sidebar-backdrop{display:none}}.log-panel{display:flex;flex-direction:column;gap:var(--spacing-md);height:100%}.log-panel-header{display:flex;flex-direction:column;gap:var(--spacing-md);margin-bottom:var(--spacing-md)}.log-panel-controls{display:flex;align-items:center;gap:var(--spacing-sm)}.log-panel-content{flex:1;min-height:0}@media(max-width:768px){.log-panel-header{gap:var(--spacing-sm)}}.stats-panel{display:flex;flex-direction:column;gap:var(--spacing-lg)}.stats-panel-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--spacing-md)}.stats-panel-actions{display:flex;gap:var(--spacing-sm)}.stats-estimator{font-size:var(--font-size-sm);color:var(--text-muted)}.stats-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted)}.stats-empty p{margin:0}.stats-empty-hint{font-size:var(--font-size-sm);margin-top:var(--spacing-sm)!important}.metrics-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md)}.metric-card{background-color:var(--card-bg);border-radius:var(--card-border-radius);box-shadow:var(--card-shadow);padding:var(--card-padding);transition:box-shadow var(--transition-fast)}.metric-card:hover{box-shadow:var(--card-shadow-hover)}.metric-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-sm)}.metric-card-title{font-size:var(--font-size-sm);color:var(--text-muted);font-weight:500}.metric-card-icon{color:var(--text-muted)}.metric-card-value{font-size:var(--font-size-2xl);font-weight:700;color:var(--text-primary);line-height:1.2}.metric-card-footer{display:flex;align-items:center;justify-content:space-between;margin-top:var(--spacing-sm)}.metric-card-subtitle{font-size:var(--font-size-xs);color:var(--text-muted)}.metric-card-trend{font-size:var(--font-size-xs);font-weight:600;padding:2px 6px;border-radius:4px}.metric-card-trend.up{color:var(--status-success);background-color:#22c55e1a}.metric-card-trend.down{color:var(--status-error);background-color:#ef44441a}.metric-card-trend.neutral{color:var(--text-muted);background-color:var(--nav-item-hover)}.live-counter{font-variant-numeric:tabular-nums}.stats-charts-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:var(--spacing-lg)}.stats-charts-row{width:100%}.pie-charts-row{display:flex;gap:var(--spacing-lg);justify-content:center;flex-wrap:wrap}.pie-chart-item{flex:1;min-width:250px;max-width:350px}.chart-container{position:relative;width:100%}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:200px;color:var(--text-muted);font-style:italic;text-align:center;gap:var(--spacing-xs)}.chart-empty-hint{font-size:.85em;opacity:.7}.activity-chart-wrapper{display:flex;flex-direction:column;gap:var(--spacing-sm)}.chart-actions{display:flex;justify-content:flex-end;padding-top:var(--spacing-sm)}.btn-sm{padding:4px 8px;font-size:.85em}@media(max-width:768px){.metrics-grid{grid-template-columns:repeat(2,1fr)}.stats-charts-grid{grid-template-columns:1fr}.pie-charts-row{flex-direction:column;align-items:center}.pie-chart-item{max-width:none;width:100%}}.session-panel{display:flex;flex-direction:column;gap:var(--spacing-lg)}.session-panel-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:var(--spacing-lg);flex-wrap:wrap;gap:var(--spacing-md)}.session-panel-title{font-size:var(--font-size-lg);font-weight:600;color:var(--text-primary);margin:0}.session-panel-subtitle{font-size:var(--font-size-sm);color:var(--text-muted);margin:var(--spacing-xs) 0 0 0}.session-panel-actions{display:flex;gap:var(--spacing-sm)}.session-list{display:flex;flex-direction:column;gap:var(--spacing-md)}.session-list-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted)}.session-list-empty p{margin:0}.session-list-empty-hint{font-size:var(--font-size-sm);margin-top:var(--spacing-sm)!important}.session-item{background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:var(--spacing-md);transition:border-color var(--transition-fast)}.session-item:hover{border-color:var(--text-muted)}.session-item-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:var(--spacing-sm)}.session-item-info{display:flex;flex-direction:column;gap:2px}.session-item-date{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary)}.session-item-time{font-size:var(--font-size-sm);color:var(--text-muted)}.session-item-duration{font-size:var(--font-size-sm);color:var(--text-secondary);background-color:var(--nav-item-hover);padding:2px 8px;border-radius:4px}.session-item-project{font-size:var(--font-size-sm);color:var(--text-secondary);margin-bottom:var(--spacing-sm)}.session-item-stats{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-sm)}.session-stat{display:flex;flex-direction:column;gap:2px}.session-stat-label{font-size:var(--font-size-xs);color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px}.session-stat-value{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}.session-item-actions{display:flex;gap:var(--spacing-sm);justify-content:flex-end}.session-action-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;font-size:var(--font-size-sm);color:var(--text-secondary);background:none;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;transition:all var(--transition-fast)}.session-action-btn:hover{color:var(--text-primary);border-color:var(--text-muted);background-color:var(--nav-item-hover)}.btn-danger{background-color:transparent;color:var(--status-error);border:1px solid var(--status-error)}.btn-danger:hover{background-color:var(--status-error);color:#fff}.export-btn{display:flex;align-items:center}@media(max-width:768px){.session-panel-header{flex-direction:column;align-items:stretch}.session-panel-actions{justify-content:flex-end}.session-item-stats{flex-wrap:wrap;gap:var(--spacing-md)}}.dashboard-overview{display:flex;flex-direction:column;gap:var(--spacing-lg)}.welcome-section{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-md)}.welcome-content{flex:1}.welcome-title{font-size:var(--font-size-xl);font-weight:600;color:var(--text-primary);margin:0 0 var(--spacing-xs) 0}.welcome-description{font-size:var(--font-size-base);color:var(--text-secondary);margin:0}.connection-badge{flex-shrink:0}.connection-status{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:20px;font-size:var(--font-size-sm);font-weight:500}.connection-status:before{content:"";width:8px;height:8px;border-radius:50%}.connection-status.streaming{background-color:#22c55e1a;color:var(--status-success)}.connection-status.streaming:before{background-color:var(--status-success);animation:pulse 2s infinite}.connection-status.polling{background-color:#f59e0b1a;color:var(--status-warning)}.connection-status.polling:before{background-color:var(--status-warning)}.connection-status.disconnected{background-color:#ef44441a;color:var(--status-error)}.connection-status.disconnected:before{background-color:var(--status-error)}.quick-nav-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:var(--spacing-md)}.quick-nav-card{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;cursor:pointer;transition:all var(--transition-fast);text-align:left;width:100%}.quick-nav-card:hover{border-color:var(--input-focus-border);background-color:var(--nav-item-active)}.quick-nav-icon{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:8px;background-color:var(--nav-item-active);color:var(--input-focus-border);flex-shrink:0}.quick-nav-content{flex:1;min-width:0}.quick-nav-title{font-size:var(--font-size-base);font-weight:600;color:var(--text-primary);margin:0}.quick-nav-description{font-size:var(--font-size-sm);color:var(--text-muted);margin:4px 0 0}.quick-nav-arrow{color:var(--text-muted);flex-shrink:0;transition:transform var(--transition-fast)}.quick-nav-card:hover .quick-nav-arrow{transform:translate(4px);color:var(--input-focus-border)}@media(max-width:768px){.welcome-section{flex-direction:column}.quick-nav-grid{grid-template-columns:1fr}}.search-input-wrapper{position:relative;display:flex;align-items:center}.search-input-icon{position:absolute;left:12px;color:var(--text-muted);pointer-events:none;display:flex;align-items:center}.search-input{width:100%;padding:8px 36px;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-primary);font-size:var(--font-size-sm);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.search-input:focus{outline:none;border-color:var(--input-focus-border);box-shadow:0 0 0 3px var(--input-focus-ring)}.search-input::placeholder{color:var(--text-muted)}.search-input-clear{position:absolute;right:8px;display:flex;align-items:center;justify-content:center;padding:4px;border:none;background:none;color:var(--text-muted);cursor:pointer;border-radius:4px;transition:color var(--transition-fast),background-color var(--transition-fast)}.search-input-clear:hover{color:var(--text-primary);background-color:var(--nav-item-hover)}.dropdown{position:relative;display:inline-block;min-width:140px}.dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-primary);font-size:var(--font-size-sm);cursor:pointer;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.dropdown-trigger:hover{border-color:var(--text-muted)}.dropdown-trigger:focus{outline:none;border-color:var(--input-focus-border);box-shadow:0 0 0 3px var(--input-focus-ring)}.dropdown-text{flex:1;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-arrow{display:flex;align-items:center;margin-left:8px;color:var(--text-muted);transition:transform var(--transition-fast)}.dropdown-arrow.open{transform:rotate(180deg)}.dropdown-menu{position:absolute;top:100%;left:0;right:0;margin-top:4px;padding:4px;border:1px solid var(--border-color);border-radius:6px;background-color:var(--bg-secondary);box-shadow:var(--card-shadow);z-index:100;max-height:240px;overflow-y:auto}.dropdown-clear{display:block;width:100%;padding:6px 8px;border:none;background:none;color:var(--status-info);font-size:var(--font-size-xs);text-align:left;cursor:pointer;border-bottom:1px solid var(--border-color);margin-bottom:4px}.dropdown-clear:hover{text-decoration:underline}.dropdown-options{display:flex;flex-direction:column;gap:2px}.dropdown-option{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;transition:background-color var(--transition-fast)}.dropdown-option:hover{background-color:var(--nav-item-hover)}.dropdown-option input[type=checkbox],.dropdown-option input[type=radio]{margin:0;accent-color:var(--input-focus-border)}.dropdown-option-label{font-size:var(--font-size-sm);color:var(--text-primary)}.log-filter-bar{display:flex;align-items:center;gap:var(--spacing-md);flex-wrap:wrap}.log-filter-search{flex:1;min-width:200px;max-width:300px}.log-filter-dropdowns{display:flex;align-items:center;gap:var(--spacing-sm)}.log-filter-clear{padding:8px 12px;font-size:var(--font-size-sm)}.icon-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid var(--input-border);border-radius:6px;background-color:var(--input-bg);color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast)}.icon-btn:hover{border-color:var(--text-muted);color:var(--text-primary);background-color:var(--nav-item-hover)}.icon-btn:disabled{opacity:.5;cursor:not-allowed}.reload-btn.spinning svg{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.filter-stats{display:flex;align-items:center;gap:var(--spacing-sm);font-size:var(--font-size-sm);color:var(--text-muted)}.filter-stats-count{font-weight:600;color:var(--text-primary)}@media(max-width:768px){.log-filter-bar{flex-direction:column;align-items:stretch}.log-filter-search{max-width:none}.log-filter-dropdowns{flex-wrap:wrap}.dropdown{flex:1;min-width:120px}}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Dashboard Server for Multi-Instance Mode
|
|
3
|
+
*
|
|
4
|
+
* This server runs independently from MCP servers and provides:
|
|
5
|
+
* - Static file serving for the dashboard UI
|
|
6
|
+
* - API endpoints to list registered instances
|
|
7
|
+
* - Proxy capabilities to forward requests to individual MCP instances
|
|
8
|
+
*/
|
|
9
|
+
export interface StandaloneDashboardOptions {
|
|
10
|
+
port?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class StandaloneDashboardServer {
|
|
13
|
+
private server;
|
|
14
|
+
private listeningPort;
|
|
15
|
+
private readonly requestedPort;
|
|
16
|
+
constructor(options?: StandaloneDashboardOptions);
|
|
17
|
+
start(): Promise<number>;
|
|
18
|
+
stop(): void;
|
|
19
|
+
getPort(): number | null;
|
|
20
|
+
private startServer;
|
|
21
|
+
private listenOnPort;
|
|
22
|
+
private handleRequest;
|
|
23
|
+
private handleGetInstances;
|
|
24
|
+
private serveDashboardAsset;
|
|
25
|
+
private respondNotFound;
|
|
26
|
+
private respondMethodNotAllowed;
|
|
27
|
+
private sendJson;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Start a standalone dashboard server and wait for shutdown signal.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runStandaloneDashboard(options?: StandaloneDashboardOptions): Promise<void>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Dashboard Server for Multi-Instance Mode
|
|
3
|
+
*
|
|
4
|
+
* This server runs independently from MCP servers and provides:
|
|
5
|
+
* - Static file serving for the dashboard UI
|
|
6
|
+
* - API endpoints to list registered instances
|
|
7
|
+
* - Proxy capabilities to forward requests to individual MCP instances
|
|
8
|
+
*/
|
|
9
|
+
import { Buffer } from 'node:buffer';
|
|
10
|
+
import { createServer } from 'node:http';
|
|
11
|
+
import { parse as parseUrl } from 'node:url';
|
|
12
|
+
import { promises as fs } from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import process from 'node:process';
|
|
15
|
+
import { createSmartEditLogger } from './util/logging.js';
|
|
16
|
+
import { SMART_EDIT_DASHBOARD_DIR } from './constants.js';
|
|
17
|
+
import { getInstances, DEFAULT_DASHBOARD_PORT } from './instance-registry.js';
|
|
18
|
+
const { logger } = createSmartEditLogger({ name: 'smart-edit.standalone-dashboard', emitToConsole: true, level: 'info' });
|
|
19
|
+
const DASHBOARD_HOST = '127.0.0.1';
|
|
20
|
+
const CONTENT_TYPE_BY_EXTENSION = {
|
|
21
|
+
'.html': 'text/html; charset=utf-8',
|
|
22
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
23
|
+
'.css': 'text/css; charset=utf-8',
|
|
24
|
+
'.png': 'image/png',
|
|
25
|
+
'.json': 'application/json; charset=utf-8',
|
|
26
|
+
'.ico': 'image/x-icon'
|
|
27
|
+
};
|
|
28
|
+
export class StandaloneDashboardServer {
|
|
29
|
+
server = null;
|
|
30
|
+
listeningPort = null;
|
|
31
|
+
requestedPort;
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.requestedPort = options.port ?? DEFAULT_DASHBOARD_PORT;
|
|
34
|
+
}
|
|
35
|
+
async start() {
|
|
36
|
+
if (this.server && this.listeningPort !== null) {
|
|
37
|
+
return this.listeningPort;
|
|
38
|
+
}
|
|
39
|
+
const { server, port } = await this.startServer();
|
|
40
|
+
this.server = server;
|
|
41
|
+
this.listeningPort = port;
|
|
42
|
+
logger.info(`Smart-Edit standalone dashboard listening on http://${DASHBOARD_HOST}:${port}/dashboard/`);
|
|
43
|
+
return port;
|
|
44
|
+
}
|
|
45
|
+
stop() {
|
|
46
|
+
if (this.server) {
|
|
47
|
+
this.server.close((error) => {
|
|
48
|
+
if (error) {
|
|
49
|
+
logger.warn('Failed to stop standalone dashboard server cleanly.', error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
this.server = null;
|
|
53
|
+
this.listeningPort = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getPort() {
|
|
57
|
+
return this.listeningPort;
|
|
58
|
+
}
|
|
59
|
+
async startServer() {
|
|
60
|
+
let candidatePort = this.requestedPort;
|
|
61
|
+
let lastError = null;
|
|
62
|
+
while (candidatePort <= 65535) {
|
|
63
|
+
const server = createServer((req, res) => {
|
|
64
|
+
void this.handleRequest(req, res);
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
const port = await this.listenOnPort(server, candidatePort);
|
|
68
|
+
return { server, port };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
lastError = error;
|
|
72
|
+
try {
|
|
73
|
+
server.close();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// ignore close errors
|
|
77
|
+
}
|
|
78
|
+
if (lastError && (lastError.code === 'EADDRINUSE' || lastError.code === 'EACCES')) {
|
|
79
|
+
candidatePort += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
logger.error('Failed to start standalone dashboard server.', lastError ?? undefined);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw new Error(lastError?.message ?? 'Unable to start standalone dashboard server; no available ports.');
|
|
87
|
+
}
|
|
88
|
+
listenOnPort(server, port) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const onError = (error) => {
|
|
91
|
+
cleanup();
|
|
92
|
+
reject(error);
|
|
93
|
+
};
|
|
94
|
+
const onListening = () => {
|
|
95
|
+
const address = server.address();
|
|
96
|
+
cleanup();
|
|
97
|
+
if (address && typeof address === 'object') {
|
|
98
|
+
resolve(address.port);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
resolve(port);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const cleanup = () => {
|
|
105
|
+
server.off('error', onError);
|
|
106
|
+
server.off('listening', onListening);
|
|
107
|
+
};
|
|
108
|
+
server.once('error', onError);
|
|
109
|
+
server.once('listening', onListening);
|
|
110
|
+
server.listen({ port, host: DASHBOARD_HOST, exclusive: true });
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async handleRequest(req, res) {
|
|
114
|
+
const method = req.method?.toUpperCase() ?? 'GET';
|
|
115
|
+
const url = parseUrl(req.url ?? '/', true);
|
|
116
|
+
const pathname = url.pathname ?? '/';
|
|
117
|
+
// Add CORS headers for cross-origin requests from dashboard UI to MCP instances
|
|
118
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
119
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS');
|
|
120
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
121
|
+
if (method === 'OPTIONS') {
|
|
122
|
+
res.statusCode = 204;
|
|
123
|
+
res.end();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
// Redirect root to dashboard
|
|
128
|
+
if (pathname === '/' || pathname === '') {
|
|
129
|
+
res.statusCode = 302;
|
|
130
|
+
res.setHeader('Location', '/dashboard/');
|
|
131
|
+
res.end();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (pathname.startsWith('/dashboard')) {
|
|
135
|
+
if (method !== 'GET') {
|
|
136
|
+
this.respondMethodNotAllowed(res);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
await this.serveDashboardAsset(pathname, res);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// API endpoints for multi-instance mode
|
|
143
|
+
if (pathname === '/api/instances') {
|
|
144
|
+
if (method !== 'GET') {
|
|
145
|
+
this.respondMethodNotAllowed(res);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.handleGetInstances(res);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.respondNotFound(res);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
logger.error('Standalone dashboard request failed.', error instanceof Error ? error : undefined);
|
|
155
|
+
this.sendJson(res, 500, { error: 'Internal server error' });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
handleGetInstances(res) {
|
|
159
|
+
const instances = getInstances();
|
|
160
|
+
this.sendJson(res, 200, { instances });
|
|
161
|
+
}
|
|
162
|
+
async serveDashboardAsset(pathname, res) {
|
|
163
|
+
let relativePath = pathname.replace(/^\/dashboard\/?/, '');
|
|
164
|
+
// Handle directory requests
|
|
165
|
+
if (relativePath === '' || relativePath.endsWith('/')) {
|
|
166
|
+
relativePath = relativePath + 'index.html';
|
|
167
|
+
}
|
|
168
|
+
const resolvedPath = path.resolve(SMART_EDIT_DASHBOARD_DIR, decodeURIComponent(relativePath));
|
|
169
|
+
if (!resolvedPath.startsWith(path.resolve(SMART_EDIT_DASHBOARD_DIR))) {
|
|
170
|
+
this.respondNotFound(res);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const file = await fs.readFile(resolvedPath);
|
|
175
|
+
const ext = path.extname(resolvedPath).toLowerCase();
|
|
176
|
+
const contentType = CONTENT_TYPE_BY_EXTENSION[ext] ?? 'application/octet-stream';
|
|
177
|
+
res.statusCode = 200;
|
|
178
|
+
res.setHeader('Content-Type', contentType);
|
|
179
|
+
res.setHeader('Content-Length', file.length);
|
|
180
|
+
res.end(file);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error.code === 'ENOENT') {
|
|
184
|
+
this.respondNotFound(res);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
respondNotFound(res) {
|
|
191
|
+
res.statusCode = 404;
|
|
192
|
+
res.end('Not found');
|
|
193
|
+
}
|
|
194
|
+
respondMethodNotAllowed(res) {
|
|
195
|
+
res.statusCode = 405;
|
|
196
|
+
res.end('Method not allowed');
|
|
197
|
+
}
|
|
198
|
+
sendJson(res, statusCode, payload) {
|
|
199
|
+
const body = JSON.stringify(payload);
|
|
200
|
+
res.statusCode = statusCode;
|
|
201
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
202
|
+
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf-8'));
|
|
203
|
+
res.end(body);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Start a standalone dashboard server and wait for shutdown signal.
|
|
208
|
+
*/
|
|
209
|
+
export async function runStandaloneDashboard(options = {}) {
|
|
210
|
+
const server = new StandaloneDashboardServer(options);
|
|
211
|
+
const port = await server.start();
|
|
212
|
+
console.log(`Smart-Edit Dashboard is running at: http://127.0.0.1:${port}/dashboard/`);
|
|
213
|
+
console.log('Press Ctrl+C to stop.');
|
|
214
|
+
await new Promise((resolve) => {
|
|
215
|
+
const shutdown = () => {
|
|
216
|
+
console.log('\nShutting down dashboard...');
|
|
217
|
+
server.stop();
|
|
218
|
+
resolve();
|
|
219
|
+
};
|
|
220
|
+
process.once('SIGINT', shutdown);
|
|
221
|
+
process.once('SIGTERM', shutdown);
|
|
222
|
+
});
|
|
223
|
+
}
|