RemiFabre commited on
Commit
cdcfe18
·
1 Parent(s): 1f737e5

Improved install guide

Browse files
Files changed (2) hide show
  1. Theremini/webui/app.js +59 -0
  2. Theremini/webui/style.css +30 -0
Theremini/webui/app.js CHANGED
@@ -118,6 +118,7 @@ function updateHealth(health = {}) {
118
  const guideHtml = typeof health.fluidsynth_guide_html === "string" ? health.fluidsynth_guide_html.trim() : "";
119
  if (guideHtml) {
120
  guideContentEl.innerHTML = guideHtml;
 
121
  } else {
122
  guideContentEl.innerHTML =
123
  "<p class=\"meta\">Unable to load the installation guide. Please check the app logs.</p>";
@@ -125,6 +126,64 @@ function updateHealth(health = {}) {
125
  }
126
  }
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  async function pollStatus() {
129
  try {
130
  const response = await fetch("/api/status", { cache: "no-store" });
 
118
  const guideHtml = typeof health.fluidsynth_guide_html === "string" ? health.fluidsynth_guide_html.trim() : "";
119
  if (guideHtml) {
120
  guideContentEl.innerHTML = guideHtml;
121
+ enhanceGuideContent();
122
  } else {
123
  guideContentEl.innerHTML =
124
  "<p class=\"meta\">Unable to load the installation guide. Please check the app logs.</p>";
 
126
  }
127
  }
128
 
129
+ function enhanceGuideContent() {
130
+ const blocks = guideContentEl.querySelectorAll("pre");
131
+ blocks.forEach((pre) => {
132
+ if (pre.dataset.copyEnhanced === "1") {
133
+ return;
134
+ }
135
+ pre.dataset.copyEnhanced = "1";
136
+ const wrapper = document.createElement("div");
137
+ wrapper.className = "guide-code-wrapper";
138
+ pre.parentNode.insertBefore(wrapper, pre);
139
+ wrapper.appendChild(pre);
140
+ const button = document.createElement("button");
141
+ button.type = "button";
142
+ button.className = "guide-copy-button";
143
+ button.textContent = "Copy";
144
+ button.addEventListener("click", () => copyGuideText(pre.innerText, button));
145
+ wrapper.appendChild(button);
146
+ });
147
+ }
148
+
149
+ async function copyGuideText(content, button) {
150
+ try {
151
+ await navigator.clipboard.writeText(content);
152
+ indicateCopySuccess(button);
153
+ return;
154
+ } catch (error) {
155
+ console.warn("Clipboard copy failed", error);
156
+ }
157
+ const textarea = document.createElement("textarea");
158
+ textarea.value = content;
159
+ textarea.style.position = "fixed";
160
+ textarea.style.top = "-9999px";
161
+ document.body.appendChild(textarea);
162
+ textarea.focus();
163
+ textarea.select();
164
+ try {
165
+ document.execCommand("copy");
166
+ indicateCopySuccess(button);
167
+ } catch (error) {
168
+ console.error("execCommand copy failed", error);
169
+ } finally {
170
+ document.body.removeChild(textarea);
171
+ }
172
+ }
173
+
174
+ function indicateCopySuccess(button) {
175
+ if (!button) {
176
+ return;
177
+ }
178
+ const original = button.textContent;
179
+ button.textContent = "Copied!";
180
+ button.classList.add("copied");
181
+ setTimeout(() => {
182
+ button.textContent = original;
183
+ button.classList.remove("copied");
184
+ }, 1500);
185
+ }
186
+
187
  async function pollStatus() {
188
  try {
189
  const response = await fetch("/api/status", { cache: "no-store" });
Theremini/webui/style.css CHANGED
@@ -143,6 +143,11 @@ body[data-guide="hidden"] #guidePanel {
143
  flex-direction: column;
144
  gap: 0.75rem;
145
  line-height: 1.5;
 
 
 
 
 
146
  }
147
 
148
  .guide-content h2,
@@ -163,6 +168,7 @@ body[data-guide="hidden"] #guidePanel {
163
  background: rgba(15, 23, 42, 0.85);
164
  border: 1px solid rgba(148, 163, 184, 0.35);
165
  overflow-x: auto;
 
166
  }
167
 
168
  .guide-content a {
@@ -179,6 +185,30 @@ body[data-guide="hidden"] #guidePanel {
179
  margin-top: 0.2rem;
180
  }
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  .card h2,
183
  .card h3 {
184
  margin: 0.25rem 0;
 
143
  flex-direction: column;
144
  gap: 0.75rem;
145
  line-height: 1.5;
146
+ user-select: text;
147
+ }
148
+
149
+ .guide-content * {
150
+ user-select: text;
151
  }
152
 
153
  .guide-content h2,
 
168
  background: rgba(15, 23, 42, 0.85);
169
  border: 1px solid rgba(148, 163, 184, 0.35);
170
  overflow-x: auto;
171
+ margin: 0;
172
  }
173
 
174
  .guide-content a {
 
185
  margin-top: 0.2rem;
186
  }
187
 
188
+ .guide-code-wrapper {
189
+ position: relative;
190
+ }
191
+
192
+ .guide-copy-button {
193
+ position: absolute;
194
+ top: 0.35rem;
195
+ right: 0.35rem;
196
+ border: 1px solid rgba(148, 163, 184, 0.5);
197
+ background: rgba(2, 6, 23, 0.8);
198
+ color: var(--text);
199
+ border-radius: 999px;
200
+ padding: 0.1rem 0.7rem;
201
+ font-size: 0.7rem;
202
+ text-transform: uppercase;
203
+ letter-spacing: 0.05em;
204
+ cursor: pointer;
205
+ }
206
+
207
+ .guide-copy-button.copied {
208
+ color: #34d399;
209
+ border-color: rgba(52, 211, 153, 0.7);
210
+ }
211
+
212
  .card h2,
213
  .card h3 {
214
  margin: 0.25rem 0;