agentgui 1.0.67 → 1.0.69
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/.prd +92 -0
- package/.prd-browser +607 -0
- package/CLAUDE.md +1559 -125
- package/browser-test-harness.js +371 -0
- package/browser-test.js +409 -0
- package/execute-tests.js +164 -0
- package/lib/claude-runner.js +41 -12
- package/lib/database-service.ts +252 -0
- package/lib/sync-service.ts +275 -0
- package/lib/types.ts +168 -0
- package/package.json +1 -1
- package/readme.md +586 -0
- package/run-e2e-test.sh +88 -0
- package/server.js +274 -8
- package/static/index.html +487 -180
- package/static/js/client.js +558 -0
- package/static/js/event-filter.js +311 -0
- package/static/js/event-processor.js +454 -0
- package/static/js/streaming-renderer.js +813 -0
- package/static/js/syntax-highlighter.js +271 -0
- package/static/js/ui-components.js +433 -0
- package/static/js/websocket-manager.js +482 -0
- package/static/templates/INDEX.html +465 -0
- package/static/templates/README.md +190 -0
- package/static/templates/agent-capabilities.html +56 -0
- package/static/templates/agent-metadata-panel.html +44 -0
- package/static/templates/agent-status-badge.html +30 -0
- package/static/templates/code-annotation-panel.html +155 -0
- package/static/templates/code-suggestion-panel.html +184 -0
- package/static/templates/command-header.html +77 -0
- package/static/templates/command-output-scrollable.html +118 -0
- package/static/templates/elapsed-time.html +54 -0
- package/static/templates/error-alert.html +106 -0
- package/static/templates/error-history-timeline.html +160 -0
- package/static/templates/error-recovery-options.html +109 -0
- package/static/templates/error-stack-trace.html +95 -0
- package/static/templates/error-summary.html +80 -0
- package/static/templates/event-counter.html +48 -0
- package/static/templates/execution-actions.html +97 -0
- package/static/templates/execution-progress-bar.html +80 -0
- package/static/templates/execution-stepper.html +120 -0
- package/static/templates/file-breadcrumb.html +118 -0
- package/static/templates/file-diff-viewer.html +121 -0
- package/static/templates/file-metadata.html +133 -0
- package/static/templates/file-read-panel.html +66 -0
- package/static/templates/file-write-panel.html +120 -0
- package/static/templates/git-branch-remote.html +107 -0
- package/static/templates/git-diff-list.html +101 -0
- package/static/templates/git-log-visualization.html +153 -0
- package/static/templates/git-status-panel.html +115 -0
- package/static/templates/quality-metrics-display.html +170 -0
- package/static/templates/terminal-output-panel.html +87 -0
- package/static/templates/test-results-display.html +144 -0
- package/test-browser.js +457 -0
- package/test-runner.js +182 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!-- Agent Capabilities Badges Panel -->
|
|
2
|
+
<div class="capabilities-panel card" role="region" aria-label="Agent capabilities">
|
|
3
|
+
<div class="card-header border-b pb-2 mb-3">
|
|
4
|
+
<h4 class="font-semibold text-sm">Capabilities</h4>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div class="flex flex-wrap gap-2">
|
|
8
|
+
<!-- File Read -->
|
|
9
|
+
<div class="badge flex items-center gap-1" title="Can read files from disk">
|
|
10
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
11
|
+
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m0 2v14h14V5H5z"/>
|
|
12
|
+
</svg>
|
|
13
|
+
<span aria-label="File read capability">Read</span>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<!-- File Write -->
|
|
17
|
+
<div class="badge flex items-center gap-1" title="Can write files to disk">
|
|
18
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
19
|
+
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25z"/>
|
|
20
|
+
</svg>
|
|
21
|
+
<span aria-label="File write capability">Write</span>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Git -->
|
|
25
|
+
<div class="badge flex items-center gap-1" title="Can perform git operations">
|
|
26
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
27
|
+
<path d="M2.5 7.5a2 2 0 1 1 4 0 2 2 0 0 1-4 0m16 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0m-8 9a2 2 0 1 1 4 0 2 2 0 0 1-4 0"/>
|
|
28
|
+
</svg>
|
|
29
|
+
<span aria-label="Git operations capability">Git</span>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Command Execution -->
|
|
33
|
+
<div class="badge flex items-center gap-1" title="Can execute system commands">
|
|
34
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
35
|
+
<path d="M13 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-8h-2v8H5V5h8V3z"/>
|
|
36
|
+
</svg>
|
|
37
|
+
<span aria-label="Command execution capability">Exec</span>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Code Analysis -->
|
|
41
|
+
<div class="badge flex items-center gap-1" title="Can analyze source code">
|
|
42
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
43
|
+
<path d="M9.4 16.6L4.8 12l4.6-4.6L6.6 6l-6 6 6 6 1.8-1.4zm5.2 0l4.6-4.6-4.6-4.6 1.8-1.8 6 6-6 6-1.8-1.4z"/>
|
|
44
|
+
</svg>
|
|
45
|
+
<span aria-label="Code analysis capability">Code</span>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<!-- Streaming -->
|
|
49
|
+
<div class="badge flex items-center gap-1" title="Supports real-time streaming output">
|
|
50
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
51
|
+
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 9.5c0 .83-.67 1.5-1.5 1.5S11 13.33 11 12.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5z"/>
|
|
52
|
+
</svg>
|
|
53
|
+
<span aria-label="Streaming capability">Stream</span>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<!-- Agent Metadata Panel with Status Badge -->
|
|
2
|
+
<div class="agent-metadata-card card" role="region" aria-label="Agent Status and Metadata">
|
|
3
|
+
<div class="card-header flex items-center justify-between gap-3">
|
|
4
|
+
<div class="flex items-center gap-3">
|
|
5
|
+
<div class="w-12 h-12 rounded-full bg-primary text-white flex items-center justify-center font-bold text-lg" aria-hidden="true">
|
|
6
|
+
{{ agentIcon }}
|
|
7
|
+
</div>
|
|
8
|
+
<div>
|
|
9
|
+
<h3 class="font-semibold text-base">{{ agentName }}</h3>
|
|
10
|
+
<p class="text-sm text-secondary">ID: {{ agentId }}</p>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="badge" data-status="{{ status }}" role="status" aria-live="polite">
|
|
14
|
+
<span class="badge-dot"></span>
|
|
15
|
+
{{ statusLabel }}
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<details class="mt-4 border-t pt-4" open>
|
|
20
|
+
<summary class="cursor-pointer font-medium hover:text-primary">Metadata & Capabilities</summary>
|
|
21
|
+
<div class="mt-3 space-y-3">
|
|
22
|
+
<div class="grid grid-cols-2 gap-3 text-sm">
|
|
23
|
+
<div>
|
|
24
|
+
<span class="text-secondary">Version:</span>
|
|
25
|
+
<p class="font-mono">{{ agentVersion }}</p>
|
|
26
|
+
</div>
|
|
27
|
+
<div>
|
|
28
|
+
<span class="text-secondary">Uptime:</span>
|
|
29
|
+
<p class="font-mono">{{ agentUptime }}</p>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div>
|
|
34
|
+
<span class="text-secondary text-sm">Capabilities:</span>
|
|
35
|
+
<div class="flex flex-wrap gap-2 mt-2">
|
|
36
|
+
<span class="badge badge-sm" aria-label="File read capability">Read</span>
|
|
37
|
+
<span class="badge badge-sm" aria-label="File write capability">Write</span>
|
|
38
|
+
<span class="badge badge-sm" aria-label="Git operations capability">Git</span>
|
|
39
|
+
<span class="badge badge-sm" aria-label="Command execution capability">Exec</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</details>
|
|
44
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- Agent Status Badge Component -->
|
|
2
|
+
<div class="badge badge-lg" data-status="{{ status }}" role="status" aria-label="Agent status: {{ statusLabel }}">
|
|
3
|
+
<span class="badge-dot animate-pulse"></span>
|
|
4
|
+
<span class="badge-text">{{ statusLabel }}</span>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<!-- Status variants (for reference):
|
|
8
|
+
- running: green dot + "Running"
|
|
9
|
+
- idle: gray dot + "Idle"
|
|
10
|
+
- error: red dot + "Error"
|
|
11
|
+
- offline: orange dot + "Offline"
|
|
12
|
+
- loading: blue dot + "Loading..."
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
<style scoped>
|
|
16
|
+
.badge[data-status="running"] { --badge-color: #10b981; }
|
|
17
|
+
.badge[data-status="idle"] { --badge-color: #6b7280; }
|
|
18
|
+
.badge[data-status="error"] { --badge-color: #ef4444; }
|
|
19
|
+
.badge[data-status="offline"] { --badge-color: #f97316; }
|
|
20
|
+
.badge[data-status="loading"] { --badge-color: #3b82f6; }
|
|
21
|
+
|
|
22
|
+
.badge-dot {
|
|
23
|
+
display: inline-block;
|
|
24
|
+
width: 8px;
|
|
25
|
+
height: 8px;
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
background-color: var(--badge-color, currentColor);
|
|
28
|
+
margin-right: 8px;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<!-- Code Annotation Display with Inline Comments -->
|
|
2
|
+
<div class="code-annotation-panel card" role="region" aria-label="Code annotations">
|
|
3
|
+
<div class="card-header border-b pb-3 mb-3">
|
|
4
|
+
<h4 class="font-semibold">{{ annotationCount }} Annotations</h4>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<!-- Annotated Code -->
|
|
8
|
+
<div class="annotated-code" role="region" aria-label="Code with inline annotations">
|
|
9
|
+
{{ for line in codeLines }}
|
|
10
|
+
<div class="code-line" data-line-number="{{ line.number }}">
|
|
11
|
+
<span class="line-number" aria-hidden="true">{{ line.number }}</span>
|
|
12
|
+
<code class="line-content">{{ line.content }}</code>
|
|
13
|
+
|
|
14
|
+
<!-- Annotation Indicator -->
|
|
15
|
+
{{ if line.annotation }}
|
|
16
|
+
<button class="annotation-indicator" data-annotation-id="{{ line.annotation.id }}" aria-label="Show annotation">
|
|
17
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
|
18
|
+
<path d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
|
|
19
|
+
</svg>
|
|
20
|
+
</button>
|
|
21
|
+
{{ end }}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Annotation Details -->
|
|
25
|
+
{{ if line.annotation }}
|
|
26
|
+
<div class="annotation-detail" data-annotation-id="{{ line.annotation.id }}">
|
|
27
|
+
<div class="annotation-content">
|
|
28
|
+
<div class="annotation-header">
|
|
29
|
+
<span class="annotation-type badge">{{ line.annotation.type }}</span>
|
|
30
|
+
<span class="annotation-author text-xs text-secondary">{{ line.annotation.author }}</span>
|
|
31
|
+
</div>
|
|
32
|
+
<p class="annotation-text text-sm mt-1">{{ line.annotation.comment }}</p>
|
|
33
|
+
<div class="annotation-actions flex gap-1 mt-2">
|
|
34
|
+
<button class="btn-micro" data-action="resolve">Resolve</button>
|
|
35
|
+
<button class="btn-micro" data-action="reply">Reply</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
{{ end }}
|
|
40
|
+
{{ end }}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<style scoped>
|
|
45
|
+
.code-annotation-panel {
|
|
46
|
+
padding: 16px;
|
|
47
|
+
max-height: 600px;
|
|
48
|
+
overflow-y: auto;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.annotated-code {
|
|
52
|
+
background-color: var(--color-bg-code);
|
|
53
|
+
border: 1px solid var(--color-border);
|
|
54
|
+
border-radius: 4px;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.code-line {
|
|
59
|
+
display: flex;
|
|
60
|
+
gap: 12px;
|
|
61
|
+
padding: 4px 8px;
|
|
62
|
+
border-bottom: 1px solid var(--color-border);
|
|
63
|
+
position: relative;
|
|
64
|
+
align-items: center;
|
|
65
|
+
background-color: var(--color-bg-primary);
|
|
66
|
+
transition: background-color 0.2s;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.code-line:hover {
|
|
70
|
+
background-color: var(--color-bg-hover);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.code-line[data-has-annotation] {
|
|
74
|
+
background-color: rgba(59, 130, 246, 0.05);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.line-number {
|
|
78
|
+
color: var(--color-text-secondary);
|
|
79
|
+
text-align: right;
|
|
80
|
+
min-width: 40px;
|
|
81
|
+
user-select: none;
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
font-size: 11px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.line-content {
|
|
87
|
+
color: var(--color-text-terminal);
|
|
88
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
flex: 1;
|
|
91
|
+
white-space: pre-wrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.annotation-indicator {
|
|
95
|
+
background: none;
|
|
96
|
+
border: none;
|
|
97
|
+
color: var(--color-primary);
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
padding: 2px;
|
|
100
|
+
border-radius: 3px;
|
|
101
|
+
transition: all 0.2s;
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.annotation-indicator:hover {
|
|
106
|
+
background-color: var(--color-primary-alpha);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.annotation-detail {
|
|
110
|
+
padding: 8px 12px;
|
|
111
|
+
background-color: rgba(59, 130, 246, 0.08);
|
|
112
|
+
border-left: 3px solid var(--color-primary);
|
|
113
|
+
border-top: 1px solid var(--color-border);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.annotation-header {
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
gap: 8px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.badge {
|
|
123
|
+
background-color: var(--color-primary);
|
|
124
|
+
color: white;
|
|
125
|
+
padding: 2px 6px;
|
|
126
|
+
border-radius: 3px;
|
|
127
|
+
font-size: 10px;
|
|
128
|
+
font-weight: 600;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.annotation-text {
|
|
132
|
+
color: var(--color-text-primary);
|
|
133
|
+
line-height: 1.5;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.annotation-actions {
|
|
137
|
+
display: flex;
|
|
138
|
+
gap: 6px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.btn-micro {
|
|
142
|
+
background: none;
|
|
143
|
+
border: 1px solid var(--color-primary);
|
|
144
|
+
color: var(--color-primary);
|
|
145
|
+
padding: 2px 6px;
|
|
146
|
+
border-radius: 2px;
|
|
147
|
+
font-size: 10px;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
transition: all 0.2s;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.btn-micro:hover {
|
|
153
|
+
background-color: var(--color-primary-alpha);
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<!-- Code Suggestion Panel with Before/After -->
|
|
2
|
+
<div class="code-suggestion-panel card" role="region" aria-label="Code suggestion">
|
|
3
|
+
<div class="card-header flex items-start justify-between mb-3 pb-3 border-b">
|
|
4
|
+
<div class="flex-1">
|
|
5
|
+
<h4 class="font-semibold">{{ suggestionTitle }}</h4>
|
|
6
|
+
<p class="text-xs text-secondary mt-1">In {{ fileName }} at line {{ lineNumber }}</p>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="badge" data-severity="{{ severity }}">
|
|
9
|
+
{{ severityLabel }}
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<!-- Description -->
|
|
14
|
+
<div class="mb-3 pb-3 border-b">
|
|
15
|
+
<p class="text-sm">{{ suggestionDescription }}</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Before/After Code Tabs -->
|
|
19
|
+
<div class="tabs mb-3" role="tablist">
|
|
20
|
+
<button class="tab-button active" data-tab="before" role="tab" aria-selected="true">
|
|
21
|
+
Before
|
|
22
|
+
</button>
|
|
23
|
+
<button class="tab-button" data-tab="after" role="tab" aria-selected="false">
|
|
24
|
+
After
|
|
25
|
+
</button>
|
|
26
|
+
<button class="tab-button" data-tab="explanation" role="tab" aria-selected="false">
|
|
27
|
+
Why?
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- Before Code -->
|
|
32
|
+
<div class="tab-panel active" data-tab="before">
|
|
33
|
+
<pre class="code-block"><code class="language-{{ language }}">{{ codeBeforeHighlighted }}</code></pre>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- After Code -->
|
|
37
|
+
<div class="tab-panel hidden" data-tab="after">
|
|
38
|
+
<pre class="code-block"><code class="language-{{ language }}">{{ codeAfterHighlighted }}</code></pre>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Explanation -->
|
|
42
|
+
<div class="tab-panel hidden" data-tab="explanation">
|
|
43
|
+
<div class="explanation-content text-sm space-y-2">
|
|
44
|
+
<p>{{ explanationText }}</p>
|
|
45
|
+
{{ if benefitsText }}
|
|
46
|
+
<div class="bg-green-50 p-2 rounded text-xs">
|
|
47
|
+
<strong>Benefits:</strong> {{ benefitsText }}
|
|
48
|
+
</div>
|
|
49
|
+
{{ end }}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<!-- Actions -->
|
|
54
|
+
<div class="flex gap-2 mt-3 pt-3 border-t">
|
|
55
|
+
<button class="btn btn-sm btn-primary" data-action="apply" aria-label="Apply suggestion">
|
|
56
|
+
Apply
|
|
57
|
+
</button>
|
|
58
|
+
<button class="btn btn-sm btn-secondary" data-action="skip" aria-label="Skip suggestion">
|
|
59
|
+
Skip
|
|
60
|
+
</button>
|
|
61
|
+
<button class="btn btn-sm btn-secondary" data-action="dismiss" aria-label="Dismiss suggestion">
|
|
62
|
+
Dismiss
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<style scoped>
|
|
68
|
+
.code-suggestion-panel {
|
|
69
|
+
padding: 16px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.tabs {
|
|
73
|
+
display: flex;
|
|
74
|
+
gap: 8px;
|
|
75
|
+
border-bottom: 1px solid var(--color-border);
|
|
76
|
+
margin-bottom: 16px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tab-button {
|
|
80
|
+
padding: 8px 12px;
|
|
81
|
+
background: none;
|
|
82
|
+
border: none;
|
|
83
|
+
color: var(--color-text-secondary);
|
|
84
|
+
font-size: 13px;
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
border-bottom: 2px solid transparent;
|
|
87
|
+
transition: all 0.2s;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.tab-button.active {
|
|
91
|
+
color: var(--color-primary);
|
|
92
|
+
border-bottom-color: var(--color-primary);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.tab-panel {
|
|
96
|
+
display: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.tab-panel.active {
|
|
100
|
+
display: block;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.code-block {
|
|
104
|
+
background-color: var(--color-bg-code);
|
|
105
|
+
border: 1px solid var(--color-border);
|
|
106
|
+
border-radius: 4px;
|
|
107
|
+
padding: 12px;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
line-height: 1.5;
|
|
110
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
111
|
+
overflow-x: auto;
|
|
112
|
+
margin: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.badge {
|
|
116
|
+
padding: 4px 8px;
|
|
117
|
+
border-radius: 3px;
|
|
118
|
+
font-size: 11px;
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.badge[data-severity="critical"] {
|
|
123
|
+
background-color: #dc2626;
|
|
124
|
+
color: white;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.badge[data-severity="high"] {
|
|
128
|
+
background-color: #ea580c;
|
|
129
|
+
color: white;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.badge[data-severity="medium"] {
|
|
133
|
+
background-color: #f59e0b;
|
|
134
|
+
color: white;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.badge[data-severity="low"] {
|
|
138
|
+
background-color: #3b82f6;
|
|
139
|
+
color: white;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.explanation-content {
|
|
143
|
+
padding: 8px;
|
|
144
|
+
background-color: var(--color-bg-secondary);
|
|
145
|
+
border-radius: 3px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.btn {
|
|
149
|
+
padding: 6px 10px;
|
|
150
|
+
border-radius: 3px;
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
border: none;
|
|
154
|
+
transition: all 0.2s;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.btn-sm {
|
|
158
|
+
padding: 4px 8px;
|
|
159
|
+
font-size: 11px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.btn-primary {
|
|
163
|
+
background-color: var(--color-primary);
|
|
164
|
+
color: white;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.btn-primary:hover {
|
|
168
|
+
background-color: var(--color-primary-dark);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.btn-secondary {
|
|
172
|
+
background-color: var(--color-bg-secondary);
|
|
173
|
+
color: var(--color-text-primary);
|
|
174
|
+
border: 1px solid var(--color-border);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.btn-secondary:hover {
|
|
178
|
+
background-color: var(--color-bg-hover);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.hidden {
|
|
182
|
+
display: none;
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!-- Command Execution Header -->
|
|
2
|
+
<div class="command-header" role="region" aria-label="Command information">
|
|
3
|
+
<div class="flex items-start justify-between gap-3 mb-3">
|
|
4
|
+
<!-- Command Info -->
|
|
5
|
+
<div class="flex-1 min-w-0">
|
|
6
|
+
<div class="flex items-center gap-2 mb-2">
|
|
7
|
+
<svg class="w-4 h-4 text-primary flex-shrink-0" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
|
8
|
+
<path d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z"/>
|
|
9
|
+
</svg>
|
|
10
|
+
<h4 class="font-semibold truncate">{{ commandName }}</h4>
|
|
11
|
+
</div>
|
|
12
|
+
<code class="text-xs font-mono text-secondary truncate block" title="{{ fullCommand }}">
|
|
13
|
+
$ {{ fullCommand }}
|
|
14
|
+
</code>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Status Badge -->
|
|
18
|
+
<div class="flex-shrink-0">
|
|
19
|
+
<div class="badge badge-lg" data-status="{{ status }}" role="status" aria-label="Command status: {{ statusLabel }}">
|
|
20
|
+
<span class="badge-dot"></span>
|
|
21
|
+
{{ statusLabel }}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- Details Row -->
|
|
27
|
+
<div class="grid grid-cols-4 gap-3 text-sm">
|
|
28
|
+
<div class="border-r pr-3">
|
|
29
|
+
<span class="text-secondary">Exit Code</span>
|
|
30
|
+
<p class="font-mono font-semibold" data-exit-code="{{ exitCode }}">{{ exitCode }}</p>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="border-r pr-3">
|
|
33
|
+
<span class="text-secondary">Duration</span>
|
|
34
|
+
<p class="font-mono font-semibold">{{ duration }}</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="border-r pr-3">
|
|
37
|
+
<span class="text-secondary">Memory</span>
|
|
38
|
+
<p class="font-mono font-semibold">{{ memoryUsed }}</p>
|
|
39
|
+
</div>
|
|
40
|
+
<div>
|
|
41
|
+
<span class="text-secondary">PID</span>
|
|
42
|
+
<p class="font-mono font-semibold">{{ pid }}</p>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<style scoped>
|
|
48
|
+
.command-header {
|
|
49
|
+
padding: 12px;
|
|
50
|
+
background: var(--color-bg-secondary);
|
|
51
|
+
border-radius: 4px;
|
|
52
|
+
border-left: 3px solid var(--color-primary);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.badge-dot {
|
|
56
|
+
display: inline-block;
|
|
57
|
+
width: 6px;
|
|
58
|
+
height: 6px;
|
|
59
|
+
border-radius: 50%;
|
|
60
|
+
background-color: currentColor;
|
|
61
|
+
margin-right: 6px;
|
|
62
|
+
animation: pulse 2s infinite;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes pulse {
|
|
66
|
+
0%, 100% { opacity: 1; }
|
|
67
|
+
50% { opacity: 0.5; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[data-exit-code="0"] {
|
|
71
|
+
color: #10b981;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-exit-code]:not([data-exit-code="0"]) {
|
|
75
|
+
color: #ef4444;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<!-- Scrollable Command Output with Virtual Rendering -->
|
|
2
|
+
<div class="command-output-scrollable" role="region" aria-label="Scrollable command output" aria-live="polite">
|
|
3
|
+
<!-- Virtual Scroll Container -->
|
|
4
|
+
<div class="scroll-viewport" data-total-lines="{{ totalLines }}" data-visible-lines="20">
|
|
5
|
+
<div class="scroll-content" role="list">
|
|
6
|
+
<!-- Lines rendered dynamically by JavaScript -->
|
|
7
|
+
{{ for line in visibleLines }}
|
|
8
|
+
<div class="output-line" data-line-number="{{ lineNumber }}" role="listitem">
|
|
9
|
+
<span class="line-number" aria-hidden="true">{{ lineNumber }}</span>
|
|
10
|
+
<code class="line-content ansi-colored" data-has-stderr="{{ hasStderr }}">{{ line }}</code>
|
|
11
|
+
</div>
|
|
12
|
+
{{ end }}
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<!-- Scrollbar -->
|
|
16
|
+
<div class="scroll-thumb" role="scrollbar" aria-orientation="vertical"></div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<!-- Search Bar -->
|
|
20
|
+
<div class="output-search mt-3 pt-3 border-t">
|
|
21
|
+
<input type="text"
|
|
22
|
+
class="search-input"
|
|
23
|
+
placeholder="Search output..."
|
|
24
|
+
aria-label="Search in command output"
|
|
25
|
+
data-search-index="true" />
|
|
26
|
+
<div class="search-results text-xs text-secondary mt-1" aria-live="polite">
|
|
27
|
+
{{ matchCount }} matches
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style scoped>
|
|
33
|
+
.command-output-scrollable {
|
|
34
|
+
background: var(--color-bg-terminal);
|
|
35
|
+
border: 1px solid var(--color-border);
|
|
36
|
+
border-radius: 4px;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
max-height: 500px;
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.scroll-viewport {
|
|
44
|
+
flex: 1;
|
|
45
|
+
overflow-y: auto;
|
|
46
|
+
overflow-x: auto;
|
|
47
|
+
position: relative;
|
|
48
|
+
background-color: var(--color-bg-code);
|
|
49
|
+
font-size: 12px;
|
|
50
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
51
|
+
line-height: 1.5;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.scroll-content {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
list-style: none;
|
|
58
|
+
margin: 0;
|
|
59
|
+
padding: 8px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.output-line {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 12px;
|
|
65
|
+
white-space: pre-wrap;
|
|
66
|
+
word-break: break-word;
|
|
67
|
+
padding: 0 4px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.line-number {
|
|
71
|
+
color: var(--color-text-secondary);
|
|
72
|
+
text-align: right;
|
|
73
|
+
min-width: 40px;
|
|
74
|
+
user-select: none;
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.line-content {
|
|
79
|
+
color: var(--color-text-terminal);
|
|
80
|
+
flex: 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.line-content[data-has-stderr="true"] {
|
|
84
|
+
color: var(--color-error);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.scroll-thumb {
|
|
88
|
+
position: absolute;
|
|
89
|
+
right: 0;
|
|
90
|
+
top: 0;
|
|
91
|
+
width: 8px;
|
|
92
|
+
height: 100%;
|
|
93
|
+
background-color: var(--color-scrollbar);
|
|
94
|
+
border-radius: 4px;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.output-search {
|
|
99
|
+
padding: 8px;
|
|
100
|
+
border-top: 1px solid var(--color-border);
|
|
101
|
+
background: var(--color-bg-secondary);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.search-input {
|
|
105
|
+
width: 100%;
|
|
106
|
+
padding: 6px 8px;
|
|
107
|
+
border: 1px solid var(--color-border);
|
|
108
|
+
border-radius: 3px;
|
|
109
|
+
font-size: 12px;
|
|
110
|
+
font-family: inherit;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.search-input:focus {
|
|
114
|
+
outline: none;
|
|
115
|
+
border-color: var(--color-primary);
|
|
116
|
+
box-shadow: 0 0 0 2px var(--color-primary-alpha);
|
|
117
|
+
}
|
|
118
|
+
</style>
|