muralipala1504 commited on
Commit
9b654e3
·
1 Parent(s): 99051d0

app.js index.html did changes to suite for current changes

Browse files
Files changed (2) hide show
  1. app.js +34 -30
  2. index.html +71 -46
app.js CHANGED
@@ -1,3 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  // =====================
2
  // Chat (default tab)
3
  // =====================
@@ -5,7 +26,6 @@ document.addEventListener('DOMContentLoaded', () => {
5
  const form = document.getElementById('chat-form');
6
  const input = document.getElementById('chat-input');
7
  const output = document.getElementById('chat-output');
8
-
9
  if (!form || !input || !output) return;
10
 
11
  form.addEventListener('submit', async (e) => {
@@ -29,18 +49,17 @@ document.addEventListener('DOMContentLoaded', () => {
29
  }
30
 
31
  const data = await response.json();
32
- appendMessage('Deepshell', data.output || 'No response');
33
  Prism.highlightAll();
34
  } catch (err) {
35
  appendMessage('Error', `Network error: ${err.message}`);
36
  }
37
  });
38
 
39
- function appendMessage(sender, message) {
40
  const msgDiv = document.createElement('div');
41
  msgDiv.classList.add('message');
42
 
43
- // Detect fenced code block ```lang\n...\n```
44
  const codeBlockMatch = typeof message === 'string'
45
  ? message.match(/```(\w+)?\n([\s\S]*?)```/)
46
  : null;
@@ -48,7 +67,12 @@ document.addEventListener('DOMContentLoaded', () => {
48
  if (codeBlockMatch) {
49
  const lang = codeBlockMatch[1] || 'bash';
50
  const code = codeBlockMatch[2];
51
- msgDiv.innerHTML = `<strong>${sender}:</strong><pre><code class="language-${lang}">${escapeHtml(code)}</code></pre>`;
 
 
 
 
 
52
  } else {
53
  msgDiv.innerHTML = `<strong>${sender}:</strong> <pre>${escapeHtml(String(message))}</pre>`;
54
  }
@@ -56,16 +80,6 @@ document.addEventListener('DOMContentLoaded', () => {
56
  output.appendChild(msgDiv);
57
  output.scrollTop = output.scrollHeight;
58
  }
59
-
60
- function escapeHtml(text) {
61
- return text.replace(/[&<>"']/g, (m) => ({
62
- '&': '&amp;',
63
- '<': '&lt;',
64
- '>': '&gt;',
65
- '"': '&quot;',
66
- "'": '&#39;',
67
- })[m]);
68
- }
69
  });
70
 
71
  // =====================
@@ -97,11 +111,13 @@ document.addEventListener('DOMContentLoaded', () => {
97
  runBtn.addEventListener('click', async () => {
98
  const paste = document.getElementById('clinic-input').value.trim();
99
  const osName = document.getElementById('clinic-os').value;
100
- const service = document.getElementById('clinic-service').value;
101
  const context = document.getElementById('clinic-context').value;
102
  const filesInput = document.getElementById('clinic-files');
103
  const files = filesInput ? filesInput.files : [];
104
 
 
 
105
  if (!paste && (!files || files.length === 0)) {
106
  out.innerHTML = '<div class="message"><strong>Clinic:</strong> <pre>Please paste logs or attach files.</pre></div>';
107
  return;
@@ -109,7 +125,6 @@ document.addEventListener('DOMContentLoaded', () => {
109
 
110
  const formData = new FormData();
111
  formData.append('paste', paste);
112
- // IMPORTANT: backend expects os_name (not os)
113
  formData.append('os_name', osName);
114
  formData.append('service', service);
115
  formData.append('context', context);
@@ -123,7 +138,7 @@ document.addEventListener('DOMContentLoaded', () => {
123
  let data;
124
  try {
125
  data = await res.json();
126
- } catch (jsonErr) {
127
  out.innerHTML = `<div class="message"><strong>Error:</strong> <pre>Bad JSON response (${res.status} ${res.statusText})</pre></div>`;
128
  return;
129
  }
@@ -135,21 +150,10 @@ document.addEventListener('DOMContentLoaded', () => {
135
  }
136
 
137
  const message = data.markdown || data.output || 'No response';
138
- // If you add marked.js, you can render markdown. For now, show preformatted text.
139
- out.innerHTML = `<div class="message"><strong>Clinic:</strong>\n<pre>${escapeHtml(message)}</pre></div>`;
140
  Prism.highlightAll();
141
  } catch (e) {
142
  out.innerHTML = `<div class="message"><strong>Error:</strong> <pre>${escapeHtml(e.message)}</pre></div>`;
143
  }
144
  });
145
-
146
- function escapeHtml(text) {
147
- return text.replace(/[&<>"']/g, (m) => ({
148
- '&': '&amp;',
149
- '<': '&lt;',
150
- '>': '&gt;',
151
- '"': '&quot;',
152
- "'": '&#39;',
153
- })[m]);
154
- }
155
  });
 
1
+ // =====================
2
+ // Utilities
3
+ // =====================
4
+ function escapeHtml(text) {
5
+ return String(text).replace(/[&<>"']/g, (m) => ({
6
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
7
+ })[m]);
8
+ }
9
+ function renderMarkdown(md) {
10
+ if (window.marked) return marked.parse(md);
11
+ return `<pre>${escapeHtml(md)}</pre>`;
12
+ }
13
+ function detectServiceFromPaste(paste) {
14
+ const p = (paste || '').toLowerCase();
15
+ if (p.includes('nginx')) return 'nginx';
16
+ if (p.includes('sshd') || p.includes('ssh')) return 'sshd';
17
+ if (p.includes('docker')) return 'docker';
18
+ if (p.includes('systemd') || p.includes('unit')) return 'systemd';
19
+ return 'auto';
20
+ }
21
+
22
  // =====================
23
  // Chat (default tab)
24
  // =====================
 
26
  const form = document.getElementById('chat-form');
27
  const input = document.getElementById('chat-input');
28
  const output = document.getElementById('chat-output');
 
29
  if (!form || !input || !output) return;
30
 
31
  form.addEventListener('submit', async (e) => {
 
49
  }
50
 
51
  const data = await response.json();
52
+ appendMessage('Deepshell', data.output || 'No response', true);
53
  Prism.highlightAll();
54
  } catch (err) {
55
  appendMessage('Error', `Network error: ${err.message}`);
56
  }
57
  });
58
 
59
+ function appendMessage(sender, message, tryMarkdown = false) {
60
  const msgDiv = document.createElement('div');
61
  msgDiv.classList.add('message');
62
 
 
63
  const codeBlockMatch = typeof message === 'string'
64
  ? message.match(/```(\w+)?\n([\s\S]*?)```/)
65
  : null;
 
67
  if (codeBlockMatch) {
68
  const lang = codeBlockMatch[1] || 'bash';
69
  const code = codeBlockMatch[2];
70
+ msgDiv.innerHTML = `
71
+ <strong>${sender}:</strong>
72
+ <pre><code class="language-${lang}">${escapeHtml(code)}</code></pre>
73
+ `;
74
+ } else if (tryMarkdown) {
75
+ msgDiv.innerHTML = `<strong>${sender}:</strong>${renderMarkdown(String(message))}`;
76
  } else {
77
  msgDiv.innerHTML = `<strong>${sender}:</strong> <pre>${escapeHtml(String(message))}</pre>`;
78
  }
 
80
  output.appendChild(msgDiv);
81
  output.scrollTop = output.scrollHeight;
82
  }
 
 
 
 
 
 
 
 
 
 
83
  });
84
 
85
  // =====================
 
111
  runBtn.addEventListener('click', async () => {
112
  const paste = document.getElementById('clinic-input').value.trim();
113
  const osName = document.getElementById('clinic-os').value;
114
+ let service = document.getElementById('clinic-service').value;
115
  const context = document.getElementById('clinic-context').value;
116
  const filesInput = document.getElementById('clinic-files');
117
  const files = filesInput ? filesInput.files : [];
118
 
119
+ if (service === 'auto' && paste) service = detectServiceFromPaste(paste);
120
+
121
  if (!paste && (!files || files.length === 0)) {
122
  out.innerHTML = '<div class="message"><strong>Clinic:</strong> <pre>Please paste logs or attach files.</pre></div>';
123
  return;
 
125
 
126
  const formData = new FormData();
127
  formData.append('paste', paste);
 
128
  formData.append('os_name', osName);
129
  formData.append('service', service);
130
  formData.append('context', context);
 
138
  let data;
139
  try {
140
  data = await res.json();
141
+ } catch {
142
  out.innerHTML = `<div class="message"><strong>Error:</strong> <pre>Bad JSON response (${res.status} ${res.statusText})</pre></div>`;
143
  return;
144
  }
 
150
  }
151
 
152
  const message = data.markdown || data.output || 'No response';
153
+ out.innerHTML = `<div class="message"><strong>Clinic:</strong>${renderMarkdown(message)}</div>`;
 
154
  Prism.highlightAll();
155
  } catch (e) {
156
  out.innerHTML = `<div class="message"><strong>Error:</strong> <pre>${escapeHtml(e.message)}</pre></div>`;
157
  }
158
  });
 
 
 
 
 
 
 
 
 
 
159
  });
index.html CHANGED
@@ -2,56 +2,82 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Deepshell Chat UI</title>
7
- <link rel="stylesheet" href="/static/services.css" />
8
- <link href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism.css" rel="stylesheet" />
9
- <link href="https://cdn.jsdelivr.net/npm/prismjs/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet" />
 
 
 
 
 
 
10
  <style>
11
- .tabs { display:flex; gap:10px; margin:12px 0; }
12
- .tab { padding:8px 12px; border-radius:6px; background:#e5e7eb; cursor:pointer; font-weight:600; }
13
- .tab.active { background:#2563eb; color:white; }
14
- .panel { display:none; }
 
 
 
15
  .panel.active { display:block; }
16
- .clinic-controls { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:8px; }
17
- textarea#clinic-input { width:100%; height:200px; font-family:ui-monospace,monospace; }
18
- #clinic-output { margin-top:10px; }
19
- .muted { color:#6b7280; font-size:0.9rem; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  </style>
21
  </head>
22
  <body>
23
- <div class="navbar">💻 Deepshell Chat UI</div>
24
-
25
  <div class="container">
26
- <!-- Tabs -->
 
27
  <div class="tabs">
28
- <div class="tab active" data-target="chat-panel">Chat</div>
29
- <div class="tab" data-target="clinic-panel">Clinic</div>
30
  </div>
31
 
32
- <!-- Chat Panel (existing) -->
33
- <div id="chat-panel" class="panel active">
34
- <div id="chat-output"></div>
35
- <form id="chat-form">
36
- <input type="text" id="chat-input" placeholder="Enter command..." autocomplete="off" required />
37
- <button type="submit">Send</button>
38
  </form>
 
39
  </div>
40
 
41
- <!-- Clinic Panel (new) -->
42
- <div id="clinic-panel" class="panel">
43
- <div class="clinic-controls">
44
  <label>OS:
45
  <select id="clinic-os">
46
- <option value="">auto</option>
47
- <option value="ubuntu">Ubuntu/Debian</option>
48
- <option value="rhel">RHEL/CentOS</option>
49
- <option value="alpine">Alpine</option>
50
  </select>
51
  </label>
52
  <label>Service:
53
  <select id="clinic-service">
54
- <option value="">auto</option>
55
  <option value="nginx">nginx</option>
56
  <option value="sshd">sshd</option>
57
  <option value="docker">docker</option>
@@ -60,31 +86,30 @@
60
  </label>
61
  <label>Context:
62
  <select id="clinic-context">
63
- <option value="dev">dev</option>
64
- <option value="stage">stage</option>
65
  <option value="prod">prod</option>
66
  </select>
67
  </label>
68
- <label>
69
- Attach files
70
- <input type="file" id="clinic-files" multiple />
 
 
71
  </label>
72
  </div>
73
 
74
- <textarea id="clinic-input" placeholder="Paste logs, errors, config snippets here..."></textarea>
75
- <div>
76
- <button id="clinic-run">Diagnose</button>
77
- <span class="muted">Deepshell Clinic analyzes the paste/files and returns diagnosis, fixes, and verification steps.</span>
 
78
  </div>
79
 
80
- <div id="clinic-output"></div>
81
  </div>
82
  </div>
83
 
 
84
  <script src="/static/app.js"></script>
85
- <script src="https://cdn.jsdelivr.net/npm/prismjs/prism.js"></script>
86
- <script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
87
- <script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/toolbar/prism-toolbar.min.js"></script>
88
- <script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
89
  </body>
90
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Deepshell UI</title>
7
+
8
+ <!-- Prism (syntax highlighting) -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" />
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
12
+
13
+ <!-- Marked (markdown renderer) -->
14
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.2/marked.min.js"></script>
15
+
16
  <style>
17
+ body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; background:#fafafa; margin:0; }
18
+ .container { max-width: 960px; margin: 24px auto; padding: 0 16px; }
19
+ h1 { font-size: 22px; margin: 0 0 12px; }
20
+ .tabs { display: flex; gap: 8px; margin-bottom: 12px; }
21
+ .tab { background:#eee; border:1px solid #ddd; padding:8px 12px; border-radius:8px; cursor:pointer; }
22
+ .tab.active { background:#2563eb; color:#fff; border-color:#2563eb; }
23
+ .panel { display:none; background:#fff; border:1px solid #e5e7eb; border-radius: 10px; padding:14px; }
24
  .panel.active { display:block; }
25
+ textarea { width:100%; min-height: 180px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; }
26
+ .message { background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:12px; margin:12px 0; white-space: normal; }
27
+ pre { background:#0b1021; color:#e4e7f0; padding:10px; border-radius:8px; overflow:auto; }
28
+ .controls { display:flex; gap:8px; align-items:center; margin: 8px 0; }
29
+ .btn { background:#2563eb; color:#fff; border:none; border-radius:8px; padding:8px 12px; cursor:pointer; }
30
+ .btn:disabled { opacity: 0.6; cursor: default; }
31
+ select { padding:6px 8px; border-radius:8px; border:1px solid #e5e7eb; }
32
+
33
+ /* Copy button style tweak */
34
+ .copy-btn {
35
+ background: #eef2ff;
36
+ color: #1f3fd1;
37
+ border: 1px solid #c7d2fe;
38
+ border-radius: 6px;
39
+ padding: 4px 8px;
40
+ font-size: 12px;
41
+ cursor: pointer;
42
+ transition: all 0.15s ease;
43
+ }
44
+ .copy-btn:hover { background: #e0e7ff; border-color: #a5b4fc; }
45
+ .copy-btn:active { transform: translateY(1px); }
46
+ .message .copy-btn { float: right; margin: 6px 0 6px 8px; }
47
  </style>
48
  </head>
49
  <body>
 
 
50
  <div class="container">
51
+ <h1>Deepshell UI</h1>
52
+
53
  <div class="tabs">
54
+ <div class="tab active" data-target="chat">Chat</div>
55
+ <div class="tab" data-target="clinic">Clinic</div>
56
  </div>
57
 
58
+ <!-- Chat Panel -->
59
+ <div id="chat" class="panel active">
60
+ <form id="chat-form" class="controls">
61
+ <input id="chat-input" type="text" placeholder="Describe your infra task..." style="flex:1; padding:8px; border:1px solid #e5e7eb; border-radius:8px;" />
62
+ <button class="btn" type="submit">Run</button>
 
63
  </form>
64
+ <div id="chat-output"></div>
65
  </div>
66
 
67
+ <!-- Clinic Panel -->
68
+ <div id="clinic" class="panel">
69
+ <div class="controls" style="flex-wrap: wrap;">
70
  <label>OS:
71
  <select id="clinic-os">
72
+ <option value="auto" selected>auto</option>
73
+ <option value="rhel">rhel</option>
74
+ <option value="ubuntu">ubuntu</option>
75
+ <option value="debian">debian</option>
76
  </select>
77
  </label>
78
  <label>Service:
79
  <select id="clinic-service">
80
+ <option value="auto" selected>auto</option>
81
  <option value="nginx">nginx</option>
82
  <option value="sshd">sshd</option>
83
  <option value="docker">docker</option>
 
86
  </label>
87
  <label>Context:
88
  <select id="clinic-context">
89
+ <option value="dev" selected>dev</option>
 
90
  <option value="prod">prod</option>
91
  </select>
92
  </label>
93
+ </div>
94
+
95
+ <div class="controls">
96
+ <label>Attach files
97
+ <input id="clinic-files" type="file" multiple />
98
  </label>
99
  </div>
100
 
101
+ <textarea id="clinic-input" placeholder="Paste logs or config snippets here..."></textarea>
102
+
103
+ <div class="controls">
104
+ <button id="clinic-run" class="btn" type="button">Diagnose</button>
105
+ <span style="color:#6b7280;">Deepshell Clinic analyzes the paste/files and returns diagnosis, fixes, and verification steps.</span>
106
  </div>
107
 
108
+ <div id="clinic-output" style="margin-top:12px;"></div>
109
  </div>
110
  </div>
111
 
112
+ <!-- Your application script -->
113
  <script src="/static/app.js"></script>
 
 
 
 
114
  </body>
115
  </html>