@autocode-cli/autocode 0.0.41 → 0.0.43
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/dist/server/dashboard.d.ts +8 -0
- package/dist/server/dashboard.d.ts.map +1 -1
- package/dist/server/dashboard.js +1144 -10
- package/dist/server/dashboard.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +23 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/dashboard.js
CHANGED
|
@@ -191,7 +191,7 @@ export function generateDashboard() {
|
|
|
191
191
|
</div>
|
|
192
192
|
|
|
193
193
|
<footer>
|
|
194
|
-
<span>AutoCode v${pkg.version} | <span id="time">${timestamp}</span></span>
|
|
194
|
+
<span>AutoCode v${pkg.version} | <span id="time">${timestamp}</span> | <code title="autodoc root">${config.root}</code></span>
|
|
195
195
|
</footer>
|
|
196
196
|
|
|
197
197
|
<script>
|
|
@@ -1009,6 +1009,46 @@ function getStyles() {
|
|
|
1009
1009
|
color: var(--text);
|
|
1010
1010
|
margin: 0;
|
|
1011
1011
|
}
|
|
1012
|
+
/* Formatted log entries */
|
|
1013
|
+
.log-entry { margin-bottom: 8px; padding: 8px 12px; border-radius: 4px; border-left: 3px solid transparent; flex-shrink: 0; }
|
|
1014
|
+
.log-entry.timestamp { color: #8b949e; font-size: 11px; border-left-color: #484f58; background: transparent; padding: 4px 12px; }
|
|
1015
|
+
.log-entry.system { color: #8b949e; border-left-color: #484f58; background: rgba(139,148,158,0.1); }
|
|
1016
|
+
.log-entry.user { color: #58a6ff; border-left-color: #58a6ff; background: rgba(88,166,255,0.1); }
|
|
1017
|
+
.log-entry.assistant { color: #7ee787; border-left-color: #7ee787; background: rgba(126,231,135,0.1); }
|
|
1018
|
+
.log-entry.tool-call { color: #d2a8ff; border-left-color: #d2a8ff; background: rgba(210,168,255,0.1); padding: 12px; }
|
|
1019
|
+
.log-entry.tool-result { color: #ffa657; border-left-color: #ffa657; background: rgba(255,166,87,0.1); }
|
|
1020
|
+
.log-entry.error { color: #f85149; border-left-color: #f85149; background: rgba(248,81,73,0.1); }
|
|
1021
|
+
.log-entry.success { color: #7ee787; border-left-color: #7ee787; background: rgba(126,231,135,0.1); }
|
|
1022
|
+
.log-label { font-weight: 600; font-size: 11px; text-transform: uppercase; margin-bottom: 4px; display: block; opacity: 0.8; }
|
|
1023
|
+
.log-content { white-space: pre-wrap; word-break: break-word; }
|
|
1024
|
+
|
|
1025
|
+
/* Code blocks with line numbers */
|
|
1026
|
+
.log-code-block { background: #161b22; border-radius: 6px; overflow: hidden; margin: 8px 0; border: 1px solid #30363d; }
|
|
1027
|
+
.log-code-header { background: #21262d; padding: 8px 12px; font-size: 12px; color: #8b949e; border-bottom: 1px solid #30363d; display: flex; align-items: center; gap: 8px; }
|
|
1028
|
+
.log-code-header::before { content: ''; display: inline-block; width: 12px; height: 12px; background: #ffa657; border-radius: 50%; }
|
|
1029
|
+
.log-code-content { padding: 12px; overflow-x: auto; font-family: 'Fira Code', 'SF Mono', Monaco, monospace; font-size: 12px; line-height: 1.5; max-height: 400px; overflow-y: auto; }
|
|
1030
|
+
.log-code-line { display: flex; min-height: 20px; }
|
|
1031
|
+
.log-line-number { color: #484f58; text-align: right; padding-right: 16px; user-select: none; min-width: 40px; flex-shrink: 0; }
|
|
1032
|
+
.log-line-content { color: #c9d1d9; white-space: pre; }
|
|
1033
|
+
|
|
1034
|
+
/* Message cards */
|
|
1035
|
+
.log-message-card { background: #161b22; border-radius: 8px; margin: 12px 0; overflow: hidden; border: 1px solid #30363d; flex-shrink: 0; }
|
|
1036
|
+
.log-message-header { padding: 10px 14px; display: flex; align-items: center; gap: 8px; font-weight: 500; font-size: 13px; }
|
|
1037
|
+
.log-message-header.assistant-header { background: linear-gradient(135deg, #238636 0%, #2ea043 100%); color: white; }
|
|
1038
|
+
.log-message-header.user-header { background: linear-gradient(135deg, #1f6feb 0%, #388bfd 100%); color: white; }
|
|
1039
|
+
.log-message-header.tool-header { background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%); color: white; }
|
|
1040
|
+
.log-message-header.result-header { background: linear-gradient(135deg, #f97316 0%, #fb923c 100%); color: white; }
|
|
1041
|
+
.log-message-body { padding: 14px; border-top: 1px solid #30363d; color: #c9d1d9; white-space: pre-wrap; word-break: break-word; }
|
|
1042
|
+
|
|
1043
|
+
/* Tool badges */
|
|
1044
|
+
.log-tool-badge { display: inline-flex; align-items: center; gap: 6px; background: rgba(139,92,246,0.2); color: #a78bfa; padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: 500; }
|
|
1045
|
+
.log-tool-input { background: #0d1117; border-radius: 4px; padding: 8px 12px; margin-top: 8px; font-family: 'Fira Code', monospace; font-size: 11px; color: #8b949e; max-height: 150px; overflow: auto; white-space: pre-wrap; }
|
|
1046
|
+
|
|
1047
|
+
/* Collapsible raw JSON */
|
|
1048
|
+
.log-raw-details { margin: 8px 0; }
|
|
1049
|
+
.log-raw-summary { cursor: pointer; color: #8b949e; font-size: 12px; padding: 8px; background: rgba(139,148,158,0.1); border-radius: 4px; }
|
|
1050
|
+
.log-raw-summary:hover { background: rgba(139,148,158,0.2); }
|
|
1051
|
+
.log-raw-json { background: #0d1117; border-radius: 4px; padding: 12px; margin-top: 8px; font-size: 11px; color: #8b949e; max-height: 200px; overflow: auto; white-space: pre-wrap; }
|
|
1012
1052
|
@keyframes pulse {
|
|
1013
1053
|
0%, 100% { opacity: 1; }
|
|
1014
1054
|
50% { opacity: 0.5; }
|
|
@@ -2103,10 +2143,144 @@ function getScript() {
|
|
|
2103
2143
|
}
|
|
2104
2144
|
}
|
|
2105
2145
|
|
|
2146
|
+
function escapeHtml(str) {
|
|
2147
|
+
if (typeof str !== 'string') return '';
|
|
2148
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
function formatCodeBlock(content, filename) {
|
|
2152
|
+
const lines = content.split(/\\\\n|\\n/);
|
|
2153
|
+
// Détecter le langage depuis le nom de fichier
|
|
2154
|
+
let lang = 'plaintext';
|
|
2155
|
+
if (filename) {
|
|
2156
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
2157
|
+
const langMap = {
|
|
2158
|
+
'js': 'javascript', 'ts': 'typescript', 'vue': 'html', 'jsx': 'javascript',
|
|
2159
|
+
'tsx': 'typescript', 'py': 'python', 'rb': 'ruby', 'java': 'java',
|
|
2160
|
+
'go': 'go', 'rs': 'rust', 'cpp': 'cpp', 'c': 'c', 'h': 'c',
|
|
2161
|
+
'css': 'css', 'scss': 'scss', 'html': 'html', 'json': 'json',
|
|
2162
|
+
'md': 'markdown', 'sh': 'bash', 'yml': 'yaml', 'yaml': 'yaml'
|
|
2163
|
+
};
|
|
2164
|
+
lang = langMap[ext] || 'plaintext';
|
|
2165
|
+
}
|
|
2166
|
+
// Extraire le code sans les numéros de ligne pour highlight.js
|
|
2167
|
+
let codeLines = [];
|
|
2168
|
+
for (const line of lines) {
|
|
2169
|
+
const match = line.match(/^\\s*\\d+[→|](.*)$/);
|
|
2170
|
+
if (match) {
|
|
2171
|
+
codeLines.push(match[1]);
|
|
2172
|
+
} else if (line.trim()) {
|
|
2173
|
+
codeLines.push(line);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
const codeContent = codeLines.join('\\n');
|
|
2177
|
+
const lineCount = codeLines.length;
|
|
2178
|
+
const preview = filename || (lineCount + ' lignes');
|
|
2179
|
+
|
|
2180
|
+
let html = '<details class="log-code-block">';
|
|
2181
|
+
html += '<summary class="log-code-header"><span class="code-lang">' + lang + '</span> ' + escapeHtml(preview) + '</summary>';
|
|
2182
|
+
html += '<pre><code class="language-' + lang + '">' + escapeHtml(codeContent) + '</code></pre>';
|
|
2183
|
+
html += '</details>';
|
|
2184
|
+
return html;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
function formatLogContent(rawContent) {
|
|
2188
|
+
if (!rawContent) return '';
|
|
2189
|
+
// Supprimer les balises <system-reminder> et leur contenu
|
|
2190
|
+
let cleanedContent = rawContent.replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi, '');
|
|
2191
|
+
const lines = cleanedContent.split('\\n');
|
|
2192
|
+
const entries = [];
|
|
2193
|
+
|
|
2194
|
+
for (const line of lines) {
|
|
2195
|
+
if (!line.trim()) continue;
|
|
2196
|
+
|
|
2197
|
+
// Timestamp line
|
|
2198
|
+
const timestampMatch = line.match(/^\\[(\\d{4}-\\d{2}-\\d{2}T[^\\]]+)\\]\\s*(.*)$/);
|
|
2199
|
+
if (timestampMatch) {
|
|
2200
|
+
const date = new Date(timestampMatch[1]);
|
|
2201
|
+
const timeStr = date.toLocaleTimeString();
|
|
2202
|
+
entries.push('<div class="log-entry timestamp">' + timeStr + ' - ' + escapeHtml(timestampMatch[2]) + '</div>');
|
|
2203
|
+
continue;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
// [RAW] - Parse avec regex (pas JSON.parse car les lignes sont coupées)
|
|
2207
|
+
if (line.startsWith('[RAW] ')) {
|
|
2208
|
+
const raw = line.slice(6);
|
|
2209
|
+
|
|
2210
|
+
// Ignorer les lignes de continuation (ne commencent pas par {)
|
|
2211
|
+
if (!raw.startsWith('{')) {
|
|
2212
|
+
continue;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// Extraire code source avec numéros de ligne (tool_result)
|
|
2216
|
+
const codeMatch = raw.match(/"content":"(\\s*\\d+[→|][^"]*)/);
|
|
2217
|
+
if (codeMatch) {
|
|
2218
|
+
// Extraire tout le contenu entre "content":" et la fin
|
|
2219
|
+
const contentMatch = raw.match(/"content":"([^"]+)/);
|
|
2220
|
+
if (contentMatch) {
|
|
2221
|
+
const decoded = contentMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"');
|
|
2222
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Tool Result</div><div class="log-message-body">' + formatCodeBlock(decoded) + '</div></div>');
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
// Extraire message assistant texte
|
|
2228
|
+
const textMatch = raw.match(/"type":"text","text":"([^"]+)"/);
|
|
2229
|
+
if (textMatch) {
|
|
2230
|
+
const decoded = textMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"');
|
|
2231
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(decoded) + '</div></div>');
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
// Extraire tool_use
|
|
2236
|
+
const toolMatch = raw.match(/"type":"tool_use"[^}]*"name":"([^"]+)"/);
|
|
2237
|
+
if (toolMatch) {
|
|
2238
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(toolMatch[1]) + '</span></div>');
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
// Ignorer les autres RAW (métadonnées, etc.)
|
|
2243
|
+
continue;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// Other prefixed messages
|
|
2247
|
+
if (line.startsWith('[SYSTEM]')) {
|
|
2248
|
+
entries.push('<div class="log-entry system"><span class="log-label">System</span><div class="log-content">' + escapeHtml(line.slice(9)) + '</div></div>');
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
if (line.startsWith('[ASSISTANT]')) {
|
|
2252
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(line.slice(12)) + '</div></div>');
|
|
2253
|
+
continue;
|
|
2254
|
+
}
|
|
2255
|
+
if (line.startsWith('[TOOL]')) {
|
|
2256
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(line.slice(7)) + '</span></div>');
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
if (line.startsWith('[RESULT]')) {
|
|
2260
|
+
const content = line.slice(9);
|
|
2261
|
+
if (/^\\s*\\d+[→|]/.test(content)) {
|
|
2262
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + formatCodeBlock(content) + '</div></div>');
|
|
2263
|
+
} else {
|
|
2264
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + escapeHtml(content.slice(0, 1000)) + (content.length > 1000 ? '...' : '') + '</div></div>');
|
|
2265
|
+
}
|
|
2266
|
+
continue;
|
|
2267
|
+
}
|
|
2268
|
+
if (line.startsWith('[ERROR]')) {
|
|
2269
|
+
entries.push('<div class="log-entry error"><span class="log-label">Error</span><div class="log-content">' + escapeHtml(line.slice(8)) + '</div></div>');
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// Default
|
|
2274
|
+
entries.push('<div class="log-entry system">' + escapeHtml(line) + '</div>');
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
return entries.join('');
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2106
2280
|
function resetClaudeLog() {
|
|
2107
2281
|
stopLogPolling();
|
|
2108
2282
|
document.getElementById('claude-log-section').style.display = 'none';
|
|
2109
|
-
document.getElementById('claude-log').
|
|
2283
|
+
document.getElementById('claude-log').innerHTML = '';
|
|
2110
2284
|
document.getElementById('claude-log-status').className = 'claude-log-status';
|
|
2111
2285
|
document.getElementById('claude-log-status').textContent = t('status.waiting');
|
|
2112
2286
|
}
|
|
@@ -2118,11 +2292,10 @@ function getScript() {
|
|
|
2118
2292
|
if (json.success && json.data) {
|
|
2119
2293
|
const section = document.getElementById('claude-log-section');
|
|
2120
2294
|
const log = document.getElementById('claude-log');
|
|
2121
|
-
const status = document.getElementById('claude-log-status');
|
|
2122
2295
|
|
|
2123
2296
|
if (json.data.exists || json.data.content) {
|
|
2124
2297
|
section.style.display = 'block';
|
|
2125
|
-
log.
|
|
2298
|
+
log.innerHTML = formatLogContent(json.data.content || '');
|
|
2126
2299
|
// Auto-scroll
|
|
2127
2300
|
log.scrollTop = log.scrollHeight;
|
|
2128
2301
|
}
|
|
@@ -2913,6 +3086,13 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
2913
3086
|
padding: 16px;
|
|
2914
3087
|
border-radius: 8px;
|
|
2915
3088
|
}
|
|
3089
|
+
.log-container {
|
|
3090
|
+
max-height: 70vh;
|
|
3091
|
+
overflow-y: auto;
|
|
3092
|
+
display: flex;
|
|
3093
|
+
flex-direction: column;
|
|
3094
|
+
gap: 8px;
|
|
3095
|
+
}
|
|
2916
3096
|
.actions-bar {
|
|
2917
3097
|
display: flex;
|
|
2918
3098
|
gap: 12px;
|
|
@@ -3109,6 +3289,46 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3109
3289
|
margin: 0;
|
|
3110
3290
|
margin-top: 12px;
|
|
3111
3291
|
}
|
|
3292
|
+
/* Formatted log entries */
|
|
3293
|
+
.log-entry { margin-bottom: 8px; padding: 8px 12px; border-radius: 4px; border-left: 3px solid transparent; flex-shrink: 0; }
|
|
3294
|
+
.log-entry.timestamp { color: #8b949e; font-size: 11px; border-left-color: #484f58; background: transparent; padding: 4px 12px; }
|
|
3295
|
+
.log-entry.system { color: #8b949e; border-left-color: #484f58; background: rgba(139,148,158,0.1); }
|
|
3296
|
+
.log-entry.user { color: #58a6ff; border-left-color: #58a6ff; background: rgba(88,166,255,0.1); }
|
|
3297
|
+
.log-entry.assistant { color: #7ee787; border-left-color: #7ee787; background: rgba(126,231,135,0.1); }
|
|
3298
|
+
.log-entry.tool-call { color: #d2a8ff; border-left-color: #d2a8ff; background: rgba(210,168,255,0.1); padding: 12px; }
|
|
3299
|
+
.log-entry.tool-result { color: #ffa657; border-left-color: #ffa657; background: rgba(255,166,87,0.1); }
|
|
3300
|
+
.log-entry.error { color: #f85149; border-left-color: #f85149; background: rgba(248,81,73,0.1); }
|
|
3301
|
+
.log-entry.success { color: #7ee787; border-left-color: #7ee787; background: rgba(126,231,135,0.1); }
|
|
3302
|
+
.log-label { font-weight: 600; font-size: 11px; text-transform: uppercase; margin-bottom: 4px; display: block; opacity: 0.8; }
|
|
3303
|
+
.log-content { white-space: pre-wrap; word-break: break-word; }
|
|
3304
|
+
|
|
3305
|
+
/* Code blocks with line numbers */
|
|
3306
|
+
.log-code-block { background: #161b22; border-radius: 6px; overflow: hidden; margin: 8px 0; border: 1px solid #30363d; }
|
|
3307
|
+
.log-code-header { background: #21262d; padding: 8px 12px; font-size: 12px; color: #8b949e; border-bottom: 1px solid #30363d; display: flex; align-items: center; gap: 8px; }
|
|
3308
|
+
.log-code-header::before { content: ''; display: inline-block; width: 12px; height: 12px; background: #ffa657; border-radius: 50%; }
|
|
3309
|
+
.log-code-content { padding: 12px; overflow-x: auto; font-family: 'Fira Code', 'SF Mono', Monaco, monospace; font-size: 12px; line-height: 1.5; max-height: 400px; overflow-y: auto; }
|
|
3310
|
+
.log-code-line { display: flex; min-height: 20px; }
|
|
3311
|
+
.log-line-number { color: #484f58; text-align: right; padding-right: 16px; user-select: none; min-width: 40px; flex-shrink: 0; }
|
|
3312
|
+
.log-line-content { color: #c9d1d9; white-space: pre; }
|
|
3313
|
+
|
|
3314
|
+
/* Message cards */
|
|
3315
|
+
.log-message-card { background: #161b22; border-radius: 8px; margin: 12px 0; overflow: hidden; border: 1px solid #30363d; flex-shrink: 0; }
|
|
3316
|
+
.log-message-header { padding: 10px 14px; display: flex; align-items: center; gap: 8px; font-weight: 500; font-size: 13px; }
|
|
3317
|
+
.log-message-header.assistant-header { background: linear-gradient(135deg, #238636 0%, #2ea043 100%); color: white; }
|
|
3318
|
+
.log-message-header.user-header { background: linear-gradient(135deg, #1f6feb 0%, #388bfd 100%); color: white; }
|
|
3319
|
+
.log-message-header.tool-header { background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%); color: white; }
|
|
3320
|
+
.log-message-header.result-header { background: linear-gradient(135deg, #f97316 0%, #fb923c 100%); color: white; }
|
|
3321
|
+
.log-message-body { padding: 14px; border-top: 1px solid #30363d; color: #c9d1d9; white-space: pre-wrap; word-break: break-word; }
|
|
3322
|
+
|
|
3323
|
+
/* Tool badges */
|
|
3324
|
+
.log-tool-badge { display: inline-flex; align-items: center; gap: 6px; background: rgba(139,92,246,0.2); color: #a78bfa; padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: 500; }
|
|
3325
|
+
.log-tool-input { background: #0d1117; border-radius: 4px; padding: 8px 12px; margin-top: 8px; font-family: 'Fira Code', monospace; font-size: 11px; color: #8b949e; max-height: 150px; overflow: auto; white-space: pre-wrap; }
|
|
3326
|
+
|
|
3327
|
+
/* Collapsible raw JSON */
|
|
3328
|
+
.log-raw-details { margin: 8px 0; }
|
|
3329
|
+
.log-raw-summary { cursor: pointer; color: #8b949e; font-size: 12px; padding: 8px; background: rgba(139,148,158,0.1); border-radius: 4px; }
|
|
3330
|
+
.log-raw-summary:hover { background: rgba(139,148,158,0.2); }
|
|
3331
|
+
.log-raw-json { background: #0d1117; border-radius: 4px; padding: 12px; margin-top: 8px; font-size: 11px; color: #8b949e; max-height: 200px; overflow: auto; white-space: pre-wrap; }
|
|
3112
3332
|
@keyframes pulse {
|
|
3113
3333
|
0%, 100% { opacity: 1; }
|
|
3114
3334
|
50% { opacity: 0.5; }
|
|
@@ -3178,8 +3398,8 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3178
3398
|
${ticket.history.map(h => {
|
|
3179
3399
|
const date = new Date(h.at);
|
|
3180
3400
|
const dateStr = date.toLocaleDateString('en-US', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
|
3181
|
-
const showPromptBtn = h.to && h.action !== 'created' ? `<
|
|
3182
|
-
const showLogBtn = h.to && h.action !== 'created' ? `<
|
|
3401
|
+
const showPromptBtn = h.to && h.action !== 'created' ? `<a href="/ticket/${ticketKey}/${escapeHtml(h.to)}/prompt" class="btn-prompt" title="View prompt">📜</a>` : '';
|
|
3402
|
+
const showLogBtn = h.to && h.action !== 'created' ? `<a href="/ticket/${ticketKey}/${escapeHtml(h.to)}/terminal" class="btn-prompt" title="View terminal log">🖥️</a>` : '';
|
|
3183
3403
|
return `<li class="history-item">
|
|
3184
3404
|
<span class="history-action">${h.action}</span>
|
|
3185
3405
|
${h.from ? `<span class="history-from">${escapeHtml(h.from)}</span> →` : ''}
|
|
@@ -3234,7 +3454,7 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3234
3454
|
<button class="prompt-modal-close" onclick="closePromptModal()">×</button>
|
|
3235
3455
|
</div>
|
|
3236
3456
|
<div class="prompt-modal-body">
|
|
3237
|
-
<
|
|
3457
|
+
<div id="prompt-modal-content" class="log-container"></div>
|
|
3238
3458
|
</div>
|
|
3239
3459
|
</div>
|
|
3240
3460
|
</div>
|
|
@@ -3592,6 +3812,149 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3592
3812
|
};
|
|
3593
3813
|
}
|
|
3594
3814
|
|
|
3815
|
+
function escapeHtml(str) {
|
|
3816
|
+
if (typeof str !== 'string') return '';
|
|
3817
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
function formatCodeBlock(content, filename) {
|
|
3821
|
+
const lines = content.split(/\\\\n|\\n/);
|
|
3822
|
+
// Détecter le langage depuis le nom de fichier
|
|
3823
|
+
let lang = 'plaintext';
|
|
3824
|
+
if (filename) {
|
|
3825
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
3826
|
+
const langMap = {
|
|
3827
|
+
'js': 'javascript', 'ts': 'typescript', 'vue': 'html', 'jsx': 'javascript',
|
|
3828
|
+
'tsx': 'typescript', 'py': 'python', 'rb': 'ruby', 'java': 'java',
|
|
3829
|
+
'go': 'go', 'rs': 'rust', 'cpp': 'cpp', 'c': 'c', 'h': 'c',
|
|
3830
|
+
'css': 'css', 'scss': 'scss', 'html': 'html', 'json': 'json',
|
|
3831
|
+
'md': 'markdown', 'sh': 'bash', 'yml': 'yaml', 'yaml': 'yaml'
|
|
3832
|
+
};
|
|
3833
|
+
lang = langMap[ext] || 'plaintext';
|
|
3834
|
+
}
|
|
3835
|
+
// Extraire le code sans les numéros de ligne pour highlight.js
|
|
3836
|
+
let codeLines = [];
|
|
3837
|
+
for (const line of lines) {
|
|
3838
|
+
const match = line.match(/^\\s*\\d+[→|](.*)$/);
|
|
3839
|
+
if (match) {
|
|
3840
|
+
codeLines.push(match[1]);
|
|
3841
|
+
} else if (line.trim()) {
|
|
3842
|
+
codeLines.push(line);
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
const codeContent = codeLines.join('\\n');
|
|
3846
|
+
const lineCount = codeLines.length;
|
|
3847
|
+
const preview = filename || (lineCount + ' lignes');
|
|
3848
|
+
|
|
3849
|
+
let html = '<details class="log-code-block">';
|
|
3850
|
+
html += '<summary class="log-code-header"><span class="code-lang">' + lang + '</span> ' + escapeHtml(preview) + '</summary>';
|
|
3851
|
+
html += '<pre><code class="language-' + lang + '">' + escapeHtml(codeContent) + '</code></pre>';
|
|
3852
|
+
html += '</details>';
|
|
3853
|
+
return html;
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
function formatLogContent(rawContent) {
|
|
3857
|
+
if (!rawContent) return '';
|
|
3858
|
+
// Supprimer les balises <system-reminder> et leur contenu
|
|
3859
|
+
let cleanedContent = rawContent.replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi, '');
|
|
3860
|
+
const lines = cleanedContent.split('\\n');
|
|
3861
|
+
const entries = [];
|
|
3862
|
+
|
|
3863
|
+
for (const line of lines) {
|
|
3864
|
+
if (!line.trim()) continue;
|
|
3865
|
+
|
|
3866
|
+
const timestampMatch = line.match(/^\\[(\\d{4}-\\d{2}-\\d{2}T[^\\]]+)\\]\\s*(.*)$/);
|
|
3867
|
+
if (timestampMatch) {
|
|
3868
|
+
const date = new Date(timestampMatch[1]);
|
|
3869
|
+
const timeStr = date.toLocaleTimeString();
|
|
3870
|
+
entries.push('<div class="log-entry timestamp">' + timeStr + ' - ' + escapeHtml(timestampMatch[2]) + '</div>');
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
// [RAW] - Parse avec regex (pas JSON.parse car les lignes sont coupées)
|
|
3875
|
+
if (line.startsWith('[RAW] ')) {
|
|
3876
|
+
const raw = line.slice(6);
|
|
3877
|
+
|
|
3878
|
+
// Ignorer les lignes de continuation (ne commencent pas par {)
|
|
3879
|
+
if (!raw.startsWith('{')) {
|
|
3880
|
+
continue;
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
// Extraire tool_result content
|
|
3884
|
+
if (raw.includes('"type":"tool_result"')) {
|
|
3885
|
+
const contentStart = raw.indexOf('"content":"');
|
|
3886
|
+
if (contentStart !== -1) {
|
|
3887
|
+
// Extraire le contenu après "content":"
|
|
3888
|
+
let content = raw.slice(contentStart + 11);
|
|
3889
|
+
// Enlever le reste du JSON (approximatif car tronqué)
|
|
3890
|
+
const endQuote = content.lastIndexOf('"');
|
|
3891
|
+
if (endQuote > 0) content = content.slice(0, endQuote);
|
|
3892
|
+
// Décoder les échappements
|
|
3893
|
+
const decoded = content.replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"').replace(/\\\\t/g, '\\t');
|
|
3894
|
+
// Vérifier si c'est du code avec numéros de ligne
|
|
3895
|
+
if (/^\\s*\\d+[→|]/.test(decoded)) {
|
|
3896
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Tool Result</div><div class="log-message-body">' + formatCodeBlock(decoded) + '</div></div>');
|
|
3897
|
+
} else {
|
|
3898
|
+
// Résultat texte simple (liste de fichiers, etc.)
|
|
3899
|
+
const lines = decoded.split('\\n').slice(0, 20); // Limiter à 20 lignes
|
|
3900
|
+
const truncated = decoded.split('\\n').length > 20 ? '<div style="color:#8b949e;font-style:italic">... (truncated)</div>' : '';
|
|
3901
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Tool Result</div><div class="log-message-body" style="font-family:monospace;font-size:12px;white-space:pre-wrap;max-height:200px;overflow-y:auto">' + escapeHtml(lines.join('\\n')) + truncated + '</div></div>');
|
|
3902
|
+
}
|
|
3903
|
+
continue;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
// Extraire message assistant texte
|
|
3908
|
+
const textMatch = raw.match(/"type":"text","text":"([^"]+)"/);
|
|
3909
|
+
if (textMatch) {
|
|
3910
|
+
const decoded = textMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"');
|
|
3911
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(decoded) + '</div></div>');
|
|
3912
|
+
continue;
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
// Extraire tool_use
|
|
3916
|
+
const toolMatch = raw.match(/"type":"tool_use"[^}]*"name":"([^"]+)"/);
|
|
3917
|
+
if (toolMatch) {
|
|
3918
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(toolMatch[1]) + '</span></div>');
|
|
3919
|
+
continue;
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
// Ignorer les autres RAW
|
|
3923
|
+
continue;
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
if (line.startsWith('[SYSTEM]')) {
|
|
3927
|
+
entries.push('<div class="log-entry system"><span class="log-label">System</span><div class="log-content">' + escapeHtml(line.slice(9)) + '</div></div>');
|
|
3928
|
+
continue;
|
|
3929
|
+
}
|
|
3930
|
+
if (line.startsWith('[ASSISTANT]')) {
|
|
3931
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(line.slice(12)) + '</div></div>');
|
|
3932
|
+
continue;
|
|
3933
|
+
}
|
|
3934
|
+
if (line.startsWith('[TOOL]')) {
|
|
3935
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(line.slice(7)) + '</span></div>');
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
if (line.startsWith('[RESULT]')) {
|
|
3939
|
+
const content = line.slice(9);
|
|
3940
|
+
if (/^\\s*\\d+[→|]/.test(content)) {
|
|
3941
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + formatCodeBlock(content) + '</div></div>');
|
|
3942
|
+
} else {
|
|
3943
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + escapeHtml(content.slice(0, 1000)) + (content.length > 1000 ? '...' : '') + '</div></div>');
|
|
3944
|
+
}
|
|
3945
|
+
continue;
|
|
3946
|
+
}
|
|
3947
|
+
if (line.startsWith('[ERROR]')) {
|
|
3948
|
+
entries.push('<div class="log-entry error"><span class="log-label">Error</span><div class="log-content">' + escapeHtml(line.slice(8)) + '</div></div>');
|
|
3949
|
+
continue;
|
|
3950
|
+
}
|
|
3951
|
+
|
|
3952
|
+
entries.push('<div class="log-entry system">' + escapeHtml(line) + '</div>');
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
return entries.join('');
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3595
3958
|
function startLogPolling() {
|
|
3596
3959
|
stopLogPolling();
|
|
3597
3960
|
logPollingInterval = setInterval(fetchLog, 1000);
|
|
@@ -3612,10 +3975,10 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3612
3975
|
const log = document.getElementById('claude-log');
|
|
3613
3976
|
|
|
3614
3977
|
if (json.success && json.data && (json.data.exists || json.data.content)) {
|
|
3615
|
-
log.
|
|
3978
|
+
log.innerHTML = formatLogContent(json.data.content || '');
|
|
3616
3979
|
log.scrollTop = log.scrollHeight;
|
|
3617
3980
|
} else {
|
|
3618
|
-
log.
|
|
3981
|
+
log.innerHTML = '<div class="log-entry system">' + t('ticketView.noLog') + '</div>';
|
|
3619
3982
|
}
|
|
3620
3983
|
} catch (e) {
|
|
3621
3984
|
console.error('Log fetch error:', e);
|
|
@@ -3688,7 +4051,11 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3688
4051
|
const json = await res.json();
|
|
3689
4052
|
if (json.success) {
|
|
3690
4053
|
title.textContent = 'Terminal → ' + columnSlug;
|
|
3691
|
-
|
|
4054
|
+
if (json.data.content) {
|
|
4055
|
+
content.innerHTML = formatLogContent(json.data.content);
|
|
4056
|
+
} else {
|
|
4057
|
+
content.textContent = t('ticketView.noLog') || 'No log available';
|
|
4058
|
+
}
|
|
3692
4059
|
} else {
|
|
3693
4060
|
title.textContent = t('ticketView.logError') || 'Log Error';
|
|
3694
4061
|
content.textContent = json.error || 'Error loading log';
|
|
@@ -3709,4 +4076,771 @@ export function generateTicketViewPage(ticketKey, lang) {
|
|
|
3709
4076
|
</body>
|
|
3710
4077
|
</html>`;
|
|
3711
4078
|
}
|
|
4079
|
+
/**
|
|
4080
|
+
* Generate the column terminal page HTML
|
|
4081
|
+
*/
|
|
4082
|
+
export function generateColumnTerminalPage(ticketKey, columnSlug, lang) {
|
|
4083
|
+
const config = getConfig();
|
|
4084
|
+
const ticket = getTicket(config.root, ticketKey);
|
|
4085
|
+
// 404 page if ticket not found
|
|
4086
|
+
if (!ticket) {
|
|
4087
|
+
return `<!DOCTYPE html>
|
|
4088
|
+
<html lang="${lang}">
|
|
4089
|
+
<head>
|
|
4090
|
+
<meta charset="UTF-8">
|
|
4091
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4092
|
+
<title>Ticket Not Found - AutoCode</title>
|
|
4093
|
+
<style>
|
|
4094
|
+
:root {
|
|
4095
|
+
--bg: #0a0a0f;
|
|
4096
|
+
--text: #f1f5f9;
|
|
4097
|
+
--accent: #6366f1;
|
|
4098
|
+
}
|
|
4099
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
4100
|
+
body {
|
|
4101
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4102
|
+
background: var(--bg);
|
|
4103
|
+
color: var(--text);
|
|
4104
|
+
min-height: 100vh;
|
|
4105
|
+
display: flex;
|
|
4106
|
+
align-items: center;
|
|
4107
|
+
justify-content: center;
|
|
4108
|
+
}
|
|
4109
|
+
.not-found {
|
|
4110
|
+
text-align: center;
|
|
4111
|
+
padding: 48px;
|
|
4112
|
+
}
|
|
4113
|
+
h1 { font-size: 2rem; margin-bottom: 16px; }
|
|
4114
|
+
p { color: #94a3b8; margin-bottom: 24px; }
|
|
4115
|
+
a {
|
|
4116
|
+
display: inline-block;
|
|
4117
|
+
background: var(--accent);
|
|
4118
|
+
color: white;
|
|
4119
|
+
padding: 12px 24px;
|
|
4120
|
+
border-radius: 8px;
|
|
4121
|
+
text-decoration: none;
|
|
4122
|
+
font-weight: 500;
|
|
4123
|
+
}
|
|
4124
|
+
a:hover { opacity: 0.9; }
|
|
4125
|
+
</style>
|
|
4126
|
+
</head>
|
|
4127
|
+
<body>
|
|
4128
|
+
<div class="not-found">
|
|
4129
|
+
<h1>Ticket Not Found</h1>
|
|
4130
|
+
<p>Ticket ${escapeHtml(ticketKey)} does not exist or has been archived.</p>
|
|
4131
|
+
<a href="/">← Back to Dashboard</a>
|
|
4132
|
+
</div>
|
|
4133
|
+
</body>
|
|
4134
|
+
</html>`;
|
|
4135
|
+
}
|
|
4136
|
+
return `<!DOCTYPE html>
|
|
4137
|
+
<html lang="${lang}">
|
|
4138
|
+
<head>
|
|
4139
|
+
<meta charset="UTF-8">
|
|
4140
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4141
|
+
<title>Terminal → ${escapeHtml(columnSlug)} | ${ticketKey} - AutoCode</title>
|
|
4142
|
+
<style>
|
|
4143
|
+
:root {
|
|
4144
|
+
--bg: #0a0a0f;
|
|
4145
|
+
--bg-secondary: #12121a;
|
|
4146
|
+
--bg-tertiary: #1a1a24;
|
|
4147
|
+
--text: #f1f5f9;
|
|
4148
|
+
--muted: #94a3b8;
|
|
4149
|
+
--border: #2a2a3a;
|
|
4150
|
+
--accent: #6366f1;
|
|
4151
|
+
--blue: #4dabf7;
|
|
4152
|
+
--green: #4ade80;
|
|
4153
|
+
--yellow: #facc15;
|
|
4154
|
+
--orange: #fb923c;
|
|
4155
|
+
--red: #f87171;
|
|
4156
|
+
--purple: #a78bfa;
|
|
4157
|
+
}
|
|
4158
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
4159
|
+
body {
|
|
4160
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4161
|
+
background: var(--bg);
|
|
4162
|
+
color: var(--text);
|
|
4163
|
+
min-height: 100vh;
|
|
4164
|
+
display: flex;
|
|
4165
|
+
flex-direction: column;
|
|
4166
|
+
}
|
|
4167
|
+
.header {
|
|
4168
|
+
display: flex;
|
|
4169
|
+
align-items: center;
|
|
4170
|
+
gap: 24px;
|
|
4171
|
+
padding: 16px 24px;
|
|
4172
|
+
background: var(--bg-secondary);
|
|
4173
|
+
border-bottom: 1px solid var(--border);
|
|
4174
|
+
position: sticky;
|
|
4175
|
+
top: 0;
|
|
4176
|
+
z-index: 100;
|
|
4177
|
+
}
|
|
4178
|
+
.back-btn {
|
|
4179
|
+
color: var(--muted);
|
|
4180
|
+
text-decoration: none;
|
|
4181
|
+
font-size: 14px;
|
|
4182
|
+
display: flex;
|
|
4183
|
+
align-items: center;
|
|
4184
|
+
gap: 8px;
|
|
4185
|
+
padding: 8px 16px;
|
|
4186
|
+
background: var(--bg-tertiary);
|
|
4187
|
+
border-radius: 6px;
|
|
4188
|
+
transition: all 0.2s;
|
|
4189
|
+
}
|
|
4190
|
+
.back-btn:hover { color: var(--text); background: var(--border); }
|
|
4191
|
+
.ticket-key {
|
|
4192
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
4193
|
+
font-size: 12px;
|
|
4194
|
+
color: var(--accent);
|
|
4195
|
+
background: rgba(99, 102, 241, 0.15);
|
|
4196
|
+
padding: 4px 10px;
|
|
4197
|
+
border-radius: 4px;
|
|
4198
|
+
font-weight: 600;
|
|
4199
|
+
}
|
|
4200
|
+
.column-slug {
|
|
4201
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
4202
|
+
font-size: 14px;
|
|
4203
|
+
color: var(--green);
|
|
4204
|
+
background: rgba(74, 222, 128, 0.15);
|
|
4205
|
+
padding: 6px 12px;
|
|
4206
|
+
border-radius: 4px;
|
|
4207
|
+
font-weight: 500;
|
|
4208
|
+
}
|
|
4209
|
+
.page-title {
|
|
4210
|
+
flex: 1;
|
|
4211
|
+
font-size: 18px;
|
|
4212
|
+
font-weight: 600;
|
|
4213
|
+
display: flex;
|
|
4214
|
+
align-items: center;
|
|
4215
|
+
gap: 12px;
|
|
4216
|
+
}
|
|
4217
|
+
.main {
|
|
4218
|
+
flex: 1;
|
|
4219
|
+
padding: 24px;
|
|
4220
|
+
display: flex;
|
|
4221
|
+
flex-direction: column;
|
|
4222
|
+
}
|
|
4223
|
+
.terminal-container {
|
|
4224
|
+
flex: 1;
|
|
4225
|
+
background: var(--bg-secondary);
|
|
4226
|
+
border: 1px solid var(--border);
|
|
4227
|
+
border-radius: 12px;
|
|
4228
|
+
display: flex;
|
|
4229
|
+
flex-direction: column;
|
|
4230
|
+
overflow: hidden;
|
|
4231
|
+
}
|
|
4232
|
+
.terminal-header {
|
|
4233
|
+
display: flex;
|
|
4234
|
+
align-items: center;
|
|
4235
|
+
gap: 12px;
|
|
4236
|
+
padding: 12px 16px;
|
|
4237
|
+
background: var(--bg-tertiary);
|
|
4238
|
+
border-bottom: 1px solid var(--border);
|
|
4239
|
+
}
|
|
4240
|
+
.terminal-status {
|
|
4241
|
+
font-size: 12px;
|
|
4242
|
+
padding: 4px 12px;
|
|
4243
|
+
border-radius: 12px;
|
|
4244
|
+
font-weight: 500;
|
|
4245
|
+
}
|
|
4246
|
+
.terminal-status.idle { background: var(--bg-secondary); color: var(--muted); }
|
|
4247
|
+
.terminal-status.processing { background: rgba(251, 146, 60, 0.15); color: var(--orange); }
|
|
4248
|
+
.terminal-status.success { background: rgba(74, 222, 128, 0.15); color: var(--green); }
|
|
4249
|
+
.terminal-status.error { background: rgba(248, 113, 113, 0.15); color: var(--red); }
|
|
4250
|
+
.terminal-log {
|
|
4251
|
+
flex: 1;
|
|
4252
|
+
padding: 16px;
|
|
4253
|
+
overflow-y: auto;
|
|
4254
|
+
font-family: 'SF Mono', Monaco, Consolas, monospace;
|
|
4255
|
+
font-size: 13px;
|
|
4256
|
+
line-height: 1.6;
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
/* Log entry styles */
|
|
4260
|
+
.log-entry {
|
|
4261
|
+
padding: 4px 0;
|
|
4262
|
+
flex-shrink: 0;
|
|
4263
|
+
}
|
|
4264
|
+
.log-entry.timestamp { color: var(--muted); font-size: 11px; }
|
|
4265
|
+
.log-entry.system { color: var(--blue); }
|
|
4266
|
+
.log-entry.error { color: var(--red); }
|
|
4267
|
+
.log-entry.tool-call { margin: 8px 0; }
|
|
4268
|
+
.log-label {
|
|
4269
|
+
display: inline-block;
|
|
4270
|
+
padding: 2px 8px;
|
|
4271
|
+
border-radius: 4px;
|
|
4272
|
+
font-size: 11px;
|
|
4273
|
+
font-weight: 600;
|
|
4274
|
+
margin-right: 8px;
|
|
4275
|
+
text-transform: uppercase;
|
|
4276
|
+
}
|
|
4277
|
+
.log-entry.system .log-label { background: rgba(77, 171, 247, 0.15); color: var(--blue); }
|
|
4278
|
+
.log-entry.error .log-label { background: rgba(248, 113, 113, 0.15); color: var(--red); }
|
|
4279
|
+
.log-tool-badge {
|
|
4280
|
+
display: inline-block;
|
|
4281
|
+
background: rgba(163, 139, 250, 0.15);
|
|
4282
|
+
color: var(--purple);
|
|
4283
|
+
padding: 4px 10px;
|
|
4284
|
+
border-radius: 4px;
|
|
4285
|
+
font-size: 12px;
|
|
4286
|
+
font-weight: 500;
|
|
4287
|
+
}
|
|
4288
|
+
.log-content { display: inline; }
|
|
4289
|
+
|
|
4290
|
+
/* Message cards */
|
|
4291
|
+
.log-message-card {
|
|
4292
|
+
background: var(--bg-tertiary);
|
|
4293
|
+
border: 1px solid var(--border);
|
|
4294
|
+
border-radius: 8px;
|
|
4295
|
+
margin: 12px 0;
|
|
4296
|
+
overflow: hidden;
|
|
4297
|
+
flex-shrink: 0;
|
|
4298
|
+
}
|
|
4299
|
+
.log-message-header {
|
|
4300
|
+
padding: 8px 12px;
|
|
4301
|
+
font-size: 11px;
|
|
4302
|
+
font-weight: 600;
|
|
4303
|
+
text-transform: uppercase;
|
|
4304
|
+
letter-spacing: 0.5px;
|
|
4305
|
+
}
|
|
4306
|
+
.assistant-header { background: rgba(99, 102, 241, 0.15); color: var(--accent); }
|
|
4307
|
+
.result-header { background: rgba(74, 222, 128, 0.15); color: var(--green); }
|
|
4308
|
+
.log-message-body {
|
|
4309
|
+
padding: 12px;
|
|
4310
|
+
line-height: 1.6;
|
|
4311
|
+
white-space: pre-wrap;
|
|
4312
|
+
word-break: break-word;
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
/* Code blocks - collapsible */
|
|
4316
|
+
.log-code-block {
|
|
4317
|
+
background: #0d1117;
|
|
4318
|
+
border: 1px solid var(--border);
|
|
4319
|
+
border-radius: 6px;
|
|
4320
|
+
overflow: hidden;
|
|
4321
|
+
margin: 8px 0;
|
|
4322
|
+
}
|
|
4323
|
+
.log-code-block summary.log-code-header {
|
|
4324
|
+
background: #161b22;
|
|
4325
|
+
padding: 10px 14px;
|
|
4326
|
+
font-size: 12px;
|
|
4327
|
+
color: var(--muted);
|
|
4328
|
+
cursor: pointer;
|
|
4329
|
+
display: flex;
|
|
4330
|
+
align-items: center;
|
|
4331
|
+
gap: 8px;
|
|
4332
|
+
user-select: none;
|
|
4333
|
+
list-style: none;
|
|
4334
|
+
}
|
|
4335
|
+
.log-code-block summary.log-code-header::-webkit-details-marker { display: none; }
|
|
4336
|
+
.log-code-block summary.log-code-header::before {
|
|
4337
|
+
content: '▶';
|
|
4338
|
+
font-size: 10px;
|
|
4339
|
+
transition: transform 0.2s;
|
|
4340
|
+
}
|
|
4341
|
+
.log-code-block[open] summary.log-code-header::before {
|
|
4342
|
+
transform: rotate(90deg);
|
|
4343
|
+
}
|
|
4344
|
+
.log-code-block .code-lang {
|
|
4345
|
+
background: var(--accent);
|
|
4346
|
+
color: white;
|
|
4347
|
+
padding: 2px 6px;
|
|
4348
|
+
border-radius: 4px;
|
|
4349
|
+
font-size: 10px;
|
|
4350
|
+
font-weight: 600;
|
|
4351
|
+
text-transform: uppercase;
|
|
4352
|
+
}
|
|
4353
|
+
.log-code-block pre {
|
|
4354
|
+
margin: 0;
|
|
4355
|
+
padding: 0;
|
|
4356
|
+
background: transparent;
|
|
4357
|
+
}
|
|
4358
|
+
.log-code-block code {
|
|
4359
|
+
display: block;
|
|
4360
|
+
padding: 14px;
|
|
4361
|
+
overflow-x: auto;
|
|
4362
|
+
font-family: 'Fira Code', 'SF Mono', Monaco, monospace;
|
|
4363
|
+
font-size: 13px;
|
|
4364
|
+
line-height: 1.6;
|
|
4365
|
+
max-height: 500px;
|
|
4366
|
+
overflow-y: auto;
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4369
|
+
.no-log {
|
|
4370
|
+
color: var(--muted);
|
|
4371
|
+
text-align: center;
|
|
4372
|
+
padding: 48px;
|
|
4373
|
+
font-size: 14px;
|
|
4374
|
+
}
|
|
4375
|
+
</style>
|
|
4376
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
|
4377
|
+
</head>
|
|
4378
|
+
<body>
|
|
4379
|
+
<header class="header">
|
|
4380
|
+
<a href="/ticket/${ticketKey}" class="back-btn">← Retour</a>
|
|
4381
|
+
<span class="ticket-key">${ticketKey}</span>
|
|
4382
|
+
<div class="page-title">
|
|
4383
|
+
<span>Terminal →</span>
|
|
4384
|
+
<span class="column-slug">${escapeHtml(columnSlug)}</span>
|
|
4385
|
+
</div>
|
|
4386
|
+
</header>
|
|
4387
|
+
|
|
4388
|
+
<main class="main">
|
|
4389
|
+
<div class="terminal-container">
|
|
4390
|
+
<div class="terminal-header">
|
|
4391
|
+
<span class="terminal-status idle" id="terminal-status">En attente</span>
|
|
4392
|
+
</div>
|
|
4393
|
+
<div class="terminal-log" id="terminal-log">
|
|
4394
|
+
<div class="no-log">Chargement...</div>
|
|
4395
|
+
</div>
|
|
4396
|
+
</div>
|
|
4397
|
+
</main>
|
|
4398
|
+
|
|
4399
|
+
<script>
|
|
4400
|
+
const TICKET_KEY = '${ticketKey}';
|
|
4401
|
+
const COLUMN_SLUG = '${escapeHtml(columnSlug)}';
|
|
4402
|
+
|
|
4403
|
+
function escapeHtml(str) {
|
|
4404
|
+
if (typeof str !== 'string') return '';
|
|
4405
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
function formatCodeBlock(content, filename) {
|
|
4409
|
+
const lines = content.split(/\\\\n|\\n/);
|
|
4410
|
+
// Détecter le langage depuis le nom de fichier
|
|
4411
|
+
let lang = 'plaintext';
|
|
4412
|
+
if (filename) {
|
|
4413
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
4414
|
+
const langMap = {
|
|
4415
|
+
'js': 'javascript', 'ts': 'typescript', 'vue': 'html', 'jsx': 'javascript',
|
|
4416
|
+
'tsx': 'typescript', 'py': 'python', 'rb': 'ruby', 'java': 'java',
|
|
4417
|
+
'go': 'go', 'rs': 'rust', 'cpp': 'cpp', 'c': 'c', 'h': 'c',
|
|
4418
|
+
'css': 'css', 'scss': 'scss', 'html': 'html', 'json': 'json',
|
|
4419
|
+
'md': 'markdown', 'sh': 'bash', 'yml': 'yaml', 'yaml': 'yaml'
|
|
4420
|
+
};
|
|
4421
|
+
lang = langMap[ext] || 'plaintext';
|
|
4422
|
+
}
|
|
4423
|
+
// Extraire le code sans les numéros de ligne pour highlight.js
|
|
4424
|
+
let codeLines = [];
|
|
4425
|
+
for (const line of lines) {
|
|
4426
|
+
const match = line.match(/^\\s*\\d+[→|](.*)$/);
|
|
4427
|
+
if (match) {
|
|
4428
|
+
codeLines.push(match[1]);
|
|
4429
|
+
} else if (line.trim()) {
|
|
4430
|
+
codeLines.push(line);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
const codeContent = codeLines.join('\\n');
|
|
4434
|
+
const lineCount = codeLines.length;
|
|
4435
|
+
const preview = filename || (lineCount + ' lignes');
|
|
4436
|
+
|
|
4437
|
+
let html = '<details class="log-code-block">';
|
|
4438
|
+
html += '<summary class="log-code-header"><span class="code-lang">' + lang + '</span> ' + escapeHtml(preview) + '</summary>';
|
|
4439
|
+
html += '<pre><code class="language-' + lang + '">' + escapeHtml(codeContent) + '</code></pre>';
|
|
4440
|
+
html += '</details>';
|
|
4441
|
+
return html;
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
function formatLogContent(rawContent) {
|
|
4445
|
+
if (!rawContent) return '';
|
|
4446
|
+
// Supprimer les balises <system-reminder> et leur contenu
|
|
4447
|
+
let cleanedContent = rawContent.replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi, '');
|
|
4448
|
+
const lines = cleanedContent.split('\\n');
|
|
4449
|
+
const entries = [];
|
|
4450
|
+
|
|
4451
|
+
for (const line of lines) {
|
|
4452
|
+
if (!line.trim()) continue;
|
|
4453
|
+
|
|
4454
|
+
const timestampMatch = line.match(/^\\[(\\d{4}-\\d{2}-\\d{2}T[^\\]]+)\\]\\s*(.*)$/);
|
|
4455
|
+
if (timestampMatch) {
|
|
4456
|
+
const date = new Date(timestampMatch[1]);
|
|
4457
|
+
const timeStr = date.toLocaleTimeString();
|
|
4458
|
+
entries.push('<div class="log-entry timestamp">' + timeStr + ' - ' + escapeHtml(timestampMatch[2]) + '</div>');
|
|
4459
|
+
continue;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4462
|
+
if (line.startsWith('[RAW] ')) {
|
|
4463
|
+
const raw = line.slice(6);
|
|
4464
|
+
if (!raw.startsWith('{')) continue;
|
|
4465
|
+
|
|
4466
|
+
if (raw.includes('"type":"tool_result"')) {
|
|
4467
|
+
const contentStart = raw.indexOf('"content":"');
|
|
4468
|
+
if (contentStart !== -1) {
|
|
4469
|
+
let content = raw.slice(contentStart + 11);
|
|
4470
|
+
const endQuote = content.lastIndexOf('"');
|
|
4471
|
+
if (endQuote > 0) content = content.slice(0, endQuote);
|
|
4472
|
+
const decoded = content.replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"').replace(/\\\\t/g, '\\t');
|
|
4473
|
+
if (/^\\s*\\d+[→|]/.test(decoded)) {
|
|
4474
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Tool Result</div><div class="log-message-body">' + formatCodeBlock(decoded) + '</div></div>');
|
|
4475
|
+
} else {
|
|
4476
|
+
const lines = decoded.split('\\n').slice(0, 20);
|
|
4477
|
+
const truncated = decoded.split('\\n').length > 20 ? '<div style="color:#8b949e;font-style:italic">... (truncated)</div>' : '';
|
|
4478
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Tool Result</div><div class="log-message-body" style="font-family:monospace;font-size:12px;white-space:pre-wrap;max-height:200px;overflow-y:auto">' + escapeHtml(lines.join('\\n')) + truncated + '</div></div>');
|
|
4479
|
+
}
|
|
4480
|
+
continue;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
const textMatch = raw.match(/"type":"text","text":"([^"]+)"/);
|
|
4485
|
+
if (textMatch) {
|
|
4486
|
+
const decoded = textMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\"/g, '"');
|
|
4487
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(decoded) + '</div></div>');
|
|
4488
|
+
continue;
|
|
4489
|
+
}
|
|
4490
|
+
|
|
4491
|
+
const toolMatch = raw.match(/"type":"tool_use"[^}]*"name":"([^"]+)"/);
|
|
4492
|
+
if (toolMatch) {
|
|
4493
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(toolMatch[1]) + '</span></div>');
|
|
4494
|
+
continue;
|
|
4495
|
+
}
|
|
4496
|
+
continue;
|
|
4497
|
+
}
|
|
4498
|
+
|
|
4499
|
+
if (line.startsWith('[SYSTEM]')) {
|
|
4500
|
+
entries.push('<div class="log-entry system"><span class="log-label">System</span><div class="log-content">' + escapeHtml(line.slice(9)) + '</div></div>');
|
|
4501
|
+
continue;
|
|
4502
|
+
}
|
|
4503
|
+
if (line.startsWith('[ASSISTANT]')) {
|
|
4504
|
+
entries.push('<div class="log-message-card"><div class="log-message-header assistant-header">Assistant</div><div class="log-message-body">' + escapeHtml(line.slice(12)) + '</div></div>');
|
|
4505
|
+
continue;
|
|
4506
|
+
}
|
|
4507
|
+
if (line.startsWith('[TOOL]')) {
|
|
4508
|
+
entries.push('<div class="log-entry tool-call"><span class="log-tool-badge">' + escapeHtml(line.slice(7)) + '</span></div>');
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4511
|
+
if (line.startsWith('[RESULT]')) {
|
|
4512
|
+
const content = line.slice(9);
|
|
4513
|
+
if (/^\\s*\\d+[→|]/.test(content)) {
|
|
4514
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + formatCodeBlock(content) + '</div></div>');
|
|
4515
|
+
} else {
|
|
4516
|
+
entries.push('<div class="log-message-card"><div class="log-message-header result-header">Result</div><div class="log-message-body">' + escapeHtml(content.slice(0, 1000)) + (content.length > 1000 ? '...' : '') + '</div></div>');
|
|
4517
|
+
}
|
|
4518
|
+
continue;
|
|
4519
|
+
}
|
|
4520
|
+
if (line.startsWith('[ERROR]')) {
|
|
4521
|
+
entries.push('<div class="log-entry error"><span class="log-label">Error</span><div class="log-content">' + escapeHtml(line.slice(8)) + '</div></div>');
|
|
4522
|
+
continue;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4525
|
+
entries.push('<div class="log-entry system">' + escapeHtml(line) + '</div>');
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
return entries.join('');
|
|
4529
|
+
}
|
|
4530
|
+
|
|
4531
|
+
async function fetchLog() {
|
|
4532
|
+
try {
|
|
4533
|
+
const res = await fetch('/api/tickets/' + TICKET_KEY + '/log/' + COLUMN_SLUG);
|
|
4534
|
+
const json = await res.json();
|
|
4535
|
+
const log = document.getElementById('terminal-log');
|
|
4536
|
+
const status = document.getElementById('terminal-status');
|
|
4537
|
+
|
|
4538
|
+
if (json.success && json.data && json.data.content) {
|
|
4539
|
+
log.innerHTML = formatLogContent(json.data.content);
|
|
4540
|
+
// Appliquer le syntax highlighting
|
|
4541
|
+
if (window.hljs) {
|
|
4542
|
+
log.querySelectorAll('pre code').forEach((block) => {
|
|
4543
|
+
hljs.highlightElement(block);
|
|
4544
|
+
});
|
|
4545
|
+
}
|
|
4546
|
+
log.scrollTop = log.scrollHeight;
|
|
4547
|
+
status.className = 'terminal-status success';
|
|
4548
|
+
status.textContent = 'Terminé';
|
|
4549
|
+
} else {
|
|
4550
|
+
log.innerHTML = '<div class="no-log">Aucun log disponible pour cette colonne.</div>';
|
|
4551
|
+
status.className = 'terminal-status idle';
|
|
4552
|
+
status.textContent = 'Aucun log';
|
|
4553
|
+
}
|
|
4554
|
+
} catch (e) {
|
|
4555
|
+
console.error('Log fetch error:', e);
|
|
4556
|
+
document.getElementById('terminal-log').innerHTML = '<div class="no-log">Erreur de chargement.</div>';
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
// WebSocket for live updates
|
|
4561
|
+
let ws = null;
|
|
4562
|
+
function connectWebSocket() {
|
|
4563
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
4564
|
+
ws = new WebSocket(protocol + '//' + location.host + '/ws');
|
|
4565
|
+
|
|
4566
|
+
ws.onmessage = (event) => {
|
|
4567
|
+
try {
|
|
4568
|
+
const msg = JSON.parse(event.data);
|
|
4569
|
+
if (msg.type === 'claude:start' && msg.ticketKey === TICKET_KEY) {
|
|
4570
|
+
document.getElementById('terminal-status').className = 'terminal-status processing';
|
|
4571
|
+
document.getElementById('terminal-status').textContent = 'En cours...';
|
|
4572
|
+
}
|
|
4573
|
+
if (msg.type === 'claude:end' && msg.ticketKey === TICKET_KEY) {
|
|
4574
|
+
fetchLog();
|
|
4575
|
+
}
|
|
4576
|
+
if (msg.type === 'ticket:updated' && msg.ticketKey === TICKET_KEY) {
|
|
4577
|
+
fetchLog();
|
|
4578
|
+
}
|
|
4579
|
+
} catch (e) {
|
|
4580
|
+
console.error('WebSocket error:', e);
|
|
4581
|
+
}
|
|
4582
|
+
};
|
|
4583
|
+
|
|
4584
|
+
ws.onclose = () => {
|
|
4585
|
+
setTimeout(connectWebSocket, 3000);
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4589
|
+
// Init
|
|
4590
|
+
fetchLog();
|
|
4591
|
+
connectWebSocket();
|
|
4592
|
+
</script>
|
|
4593
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
4594
|
+
</body>
|
|
4595
|
+
</html>`;
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Generate the column prompt page HTML
|
|
4599
|
+
*/
|
|
4600
|
+
export function generateColumnPromptPage(ticketKey, columnSlug, lang) {
|
|
4601
|
+
const config = getConfig();
|
|
4602
|
+
const ticket = getTicket(config.root, ticketKey);
|
|
4603
|
+
// 404 page if ticket not found
|
|
4604
|
+
if (!ticket) {
|
|
4605
|
+
return `<!DOCTYPE html>
|
|
4606
|
+
<html lang="${lang}">
|
|
4607
|
+
<head>
|
|
4608
|
+
<meta charset="UTF-8">
|
|
4609
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4610
|
+
<title>Ticket Not Found - AutoCode</title>
|
|
4611
|
+
<style>
|
|
4612
|
+
:root {
|
|
4613
|
+
--bg: #0a0a0f;
|
|
4614
|
+
--text: #f1f5f9;
|
|
4615
|
+
--accent: #6366f1;
|
|
4616
|
+
}
|
|
4617
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
4618
|
+
body {
|
|
4619
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4620
|
+
background: var(--bg);
|
|
4621
|
+
color: var(--text);
|
|
4622
|
+
min-height: 100vh;
|
|
4623
|
+
display: flex;
|
|
4624
|
+
align-items: center;
|
|
4625
|
+
justify-content: center;
|
|
4626
|
+
}
|
|
4627
|
+
.not-found {
|
|
4628
|
+
text-align: center;
|
|
4629
|
+
padding: 48px;
|
|
4630
|
+
}
|
|
4631
|
+
h1 { font-size: 2rem; margin-bottom: 16px; }
|
|
4632
|
+
p { color: #94a3b8; margin-bottom: 24px; }
|
|
4633
|
+
a {
|
|
4634
|
+
display: inline-block;
|
|
4635
|
+
background: var(--accent);
|
|
4636
|
+
color: white;
|
|
4637
|
+
padding: 12px 24px;
|
|
4638
|
+
border-radius: 8px;
|
|
4639
|
+
text-decoration: none;
|
|
4640
|
+
font-weight: 500;
|
|
4641
|
+
}
|
|
4642
|
+
a:hover { opacity: 0.9; }
|
|
4643
|
+
</style>
|
|
4644
|
+
</head>
|
|
4645
|
+
<body>
|
|
4646
|
+
<div class="not-found">
|
|
4647
|
+
<h1>Ticket Not Found</h1>
|
|
4648
|
+
<p>Ticket ${escapeHtml(ticketKey)} does not exist or has been archived.</p>
|
|
4649
|
+
<a href="/">← Back to Dashboard</a>
|
|
4650
|
+
</div>
|
|
4651
|
+
</body>
|
|
4652
|
+
</html>`;
|
|
4653
|
+
}
|
|
4654
|
+
return `<!DOCTYPE html>
|
|
4655
|
+
<html lang="${lang}">
|
|
4656
|
+
<head>
|
|
4657
|
+
<meta charset="UTF-8">
|
|
4658
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4659
|
+
<title>Prompt → ${escapeHtml(columnSlug)} | ${ticketKey} - AutoCode</title>
|
|
4660
|
+
<style>
|
|
4661
|
+
:root {
|
|
4662
|
+
--bg: #0a0a0f;
|
|
4663
|
+
--bg-secondary: #12121a;
|
|
4664
|
+
--bg-tertiary: #1a1a24;
|
|
4665
|
+
--text: #f1f5f9;
|
|
4666
|
+
--muted: #94a3b8;
|
|
4667
|
+
--border: #2a2a3a;
|
|
4668
|
+
--accent: #6366f1;
|
|
4669
|
+
--blue: #4dabf7;
|
|
4670
|
+
--green: #4ade80;
|
|
4671
|
+
--yellow: #facc15;
|
|
4672
|
+
--orange: #fb923c;
|
|
4673
|
+
--red: #f87171;
|
|
4674
|
+
--purple: #a78bfa;
|
|
4675
|
+
}
|
|
4676
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
4677
|
+
body {
|
|
4678
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4679
|
+
background: var(--bg);
|
|
4680
|
+
color: var(--text);
|
|
4681
|
+
min-height: 100vh;
|
|
4682
|
+
display: flex;
|
|
4683
|
+
flex-direction: column;
|
|
4684
|
+
}
|
|
4685
|
+
.header {
|
|
4686
|
+
display: flex;
|
|
4687
|
+
align-items: center;
|
|
4688
|
+
gap: 24px;
|
|
4689
|
+
padding: 16px 24px;
|
|
4690
|
+
background: var(--bg-secondary);
|
|
4691
|
+
border-bottom: 1px solid var(--border);
|
|
4692
|
+
position: sticky;
|
|
4693
|
+
top: 0;
|
|
4694
|
+
z-index: 100;
|
|
4695
|
+
}
|
|
4696
|
+
.back-btn {
|
|
4697
|
+
color: var(--muted);
|
|
4698
|
+
text-decoration: none;
|
|
4699
|
+
font-size: 14px;
|
|
4700
|
+
display: flex;
|
|
4701
|
+
align-items: center;
|
|
4702
|
+
gap: 8px;
|
|
4703
|
+
padding: 8px 16px;
|
|
4704
|
+
background: var(--bg-tertiary);
|
|
4705
|
+
border-radius: 6px;
|
|
4706
|
+
transition: all 0.2s;
|
|
4707
|
+
}
|
|
4708
|
+
.back-btn:hover { color: var(--text); background: var(--border); }
|
|
4709
|
+
.ticket-key {
|
|
4710
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
4711
|
+
font-size: 12px;
|
|
4712
|
+
color: var(--accent);
|
|
4713
|
+
background: rgba(99, 102, 241, 0.15);
|
|
4714
|
+
padding: 4px 10px;
|
|
4715
|
+
border-radius: 4px;
|
|
4716
|
+
font-weight: 600;
|
|
4717
|
+
}
|
|
4718
|
+
.column-slug {
|
|
4719
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
4720
|
+
font-size: 14px;
|
|
4721
|
+
color: var(--yellow);
|
|
4722
|
+
background: rgba(250, 204, 21, 0.15);
|
|
4723
|
+
padding: 6px 12px;
|
|
4724
|
+
border-radius: 4px;
|
|
4725
|
+
font-weight: 500;
|
|
4726
|
+
}
|
|
4727
|
+
.page-title {
|
|
4728
|
+
flex: 1;
|
|
4729
|
+
font-size: 18px;
|
|
4730
|
+
font-weight: 600;
|
|
4731
|
+
display: flex;
|
|
4732
|
+
align-items: center;
|
|
4733
|
+
gap: 12px;
|
|
4734
|
+
}
|
|
4735
|
+
.main {
|
|
4736
|
+
flex: 1;
|
|
4737
|
+
padding: 24px;
|
|
4738
|
+
display: flex;
|
|
4739
|
+
flex-direction: column;
|
|
4740
|
+
}
|
|
4741
|
+
.prompt-container {
|
|
4742
|
+
flex: 1;
|
|
4743
|
+
background: var(--bg-secondary);
|
|
4744
|
+
border: 1px solid var(--border);
|
|
4745
|
+
border-radius: 12px;
|
|
4746
|
+
display: flex;
|
|
4747
|
+
flex-direction: column;
|
|
4748
|
+
overflow: hidden;
|
|
4749
|
+
}
|
|
4750
|
+
.prompt-header {
|
|
4751
|
+
display: flex;
|
|
4752
|
+
align-items: center;
|
|
4753
|
+
gap: 12px;
|
|
4754
|
+
padding: 12px 16px;
|
|
4755
|
+
background: var(--bg-tertiary);
|
|
4756
|
+
border-bottom: 1px solid var(--border);
|
|
4757
|
+
}
|
|
4758
|
+
.prompt-status {
|
|
4759
|
+
font-size: 12px;
|
|
4760
|
+
padding: 4px 12px;
|
|
4761
|
+
border-radius: 12px;
|
|
4762
|
+
font-weight: 500;
|
|
4763
|
+
}
|
|
4764
|
+
.prompt-status.loaded { background: rgba(74, 222, 128, 0.15); color: var(--green); }
|
|
4765
|
+
.prompt-status.loading { background: rgba(251, 146, 60, 0.15); color: var(--orange); }
|
|
4766
|
+
.prompt-status.error { background: rgba(248, 113, 113, 0.15); color: var(--red); }
|
|
4767
|
+
.prompt-content {
|
|
4768
|
+
flex: 1;
|
|
4769
|
+
padding: 24px;
|
|
4770
|
+
overflow-y: auto;
|
|
4771
|
+
font-family: 'SF Mono', Monaco, Consolas, monospace;
|
|
4772
|
+
font-size: 14px;
|
|
4773
|
+
line-height: 1.8;
|
|
4774
|
+
white-space: pre-wrap;
|
|
4775
|
+
word-break: break-word;
|
|
4776
|
+
}
|
|
4777
|
+
.no-prompt {
|
|
4778
|
+
color: var(--muted);
|
|
4779
|
+
text-align: center;
|
|
4780
|
+
padding: 48px;
|
|
4781
|
+
font-size: 14px;
|
|
4782
|
+
}
|
|
4783
|
+
</style>
|
|
4784
|
+
</head>
|
|
4785
|
+
<body>
|
|
4786
|
+
<header class="header">
|
|
4787
|
+
<a href="/ticket/${ticketKey}" class="back-btn">← Retour</a>
|
|
4788
|
+
<span class="ticket-key">${ticketKey}</span>
|
|
4789
|
+
<div class="page-title">
|
|
4790
|
+
<span>Prompt →</span>
|
|
4791
|
+
<span class="column-slug">${escapeHtml(columnSlug)}</span>
|
|
4792
|
+
</div>
|
|
4793
|
+
</header>
|
|
4794
|
+
|
|
4795
|
+
<main class="main">
|
|
4796
|
+
<div class="prompt-container">
|
|
4797
|
+
<div class="prompt-header">
|
|
4798
|
+
<span class="prompt-status loading" id="prompt-status">Chargement...</span>
|
|
4799
|
+
</div>
|
|
4800
|
+
<div class="prompt-content" id="prompt-content">
|
|
4801
|
+
<div class="no-prompt">Chargement du prompt...</div>
|
|
4802
|
+
</div>
|
|
4803
|
+
</div>
|
|
4804
|
+
</main>
|
|
4805
|
+
|
|
4806
|
+
<script>
|
|
4807
|
+
const TICKET_KEY = '${ticketKey}';
|
|
4808
|
+
const COLUMN_SLUG = '${escapeHtml(columnSlug)}';
|
|
4809
|
+
|
|
4810
|
+
function escapeHtml(str) {
|
|
4811
|
+
if (typeof str !== 'string') return '';
|
|
4812
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
async function fetchPrompt() {
|
|
4816
|
+
const content = document.getElementById('prompt-content');
|
|
4817
|
+
const status = document.getElementById('prompt-status');
|
|
4818
|
+
|
|
4819
|
+
try {
|
|
4820
|
+
const res = await fetch('/api/tickets/' + TICKET_KEY + '/prompt/' + COLUMN_SLUG);
|
|
4821
|
+
const json = await res.json();
|
|
4822
|
+
|
|
4823
|
+
if (json.success && json.data && json.data.prompt) {
|
|
4824
|
+
content.textContent = json.data.prompt;
|
|
4825
|
+
status.className = 'prompt-status loaded';
|
|
4826
|
+
status.textContent = 'Chargé';
|
|
4827
|
+
} else {
|
|
4828
|
+
content.innerHTML = '<div class="no-prompt">Aucun prompt disponible pour cette colonne.</div>';
|
|
4829
|
+
status.className = 'prompt-status error';
|
|
4830
|
+
status.textContent = 'Non disponible';
|
|
4831
|
+
}
|
|
4832
|
+
} catch (e) {
|
|
4833
|
+
console.error('Prompt fetch error:', e);
|
|
4834
|
+
content.innerHTML = '<div class="no-prompt">Erreur de chargement.</div>';
|
|
4835
|
+
status.className = 'prompt-status error';
|
|
4836
|
+
status.textContent = 'Erreur';
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
// Init
|
|
4841
|
+
fetchPrompt();
|
|
4842
|
+
</script>
|
|
4843
|
+
</body>
|
|
4844
|
+
</html>`;
|
|
4845
|
+
}
|
|
3712
4846
|
//# sourceMappingURL=dashboard.js.map
|