clay-server 2.27.0-beta.8 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/lib/daemon-projects.js +164 -0
- package/lib/daemon.js +13 -126
- package/lib/mates-identity.js +132 -0
- package/lib/mates-knowledge.js +113 -0
- package/lib/mates-prompts.js +398 -0
- package/lib/mates.js +40 -599
- package/lib/project-connection.js +2 -0
- package/lib/project-debate.js +19 -12
- package/lib/project-http.js +4 -2
- package/lib/project-loop.js +110 -48
- package/lib/project-mate-interaction.js +4 -0
- package/lib/project-notifications.js +210 -0
- package/lib/project-sessions.js +5 -2
- package/lib/project-user-message.js +2 -1
- package/lib/project.js +26 -2
- package/lib/public/app.js +1193 -8521
- package/lib/public/css/command-palette.css +14 -0
- package/lib/public/css/loop.css +301 -0
- package/lib/public/css/notifications-center.css +190 -0
- package/lib/public/css/rewind.css +6 -0
- package/lib/public/index.html +89 -35
- package/lib/public/modules/app-connection.js +160 -0
- package/lib/public/modules/app-cursors.js +473 -0
- package/lib/public/modules/app-debate-ui.js +389 -0
- package/lib/public/modules/app-dm.js +627 -0
- package/lib/public/modules/app-favicon.js +212 -0
- package/lib/public/modules/app-header.js +229 -0
- package/lib/public/modules/app-home-hub.js +600 -0
- package/lib/public/modules/app-loop-ui.js +589 -0
- package/lib/public/modules/app-loop-wizard.js +439 -0
- package/lib/public/modules/app-messages.js +1560 -0
- package/lib/public/modules/app-misc.js +299 -0
- package/lib/public/modules/app-notifications.js +372 -0
- package/lib/public/modules/app-panels.js +888 -0
- package/lib/public/modules/app-projects.js +798 -0
- package/lib/public/modules/app-rate-limit.js +451 -0
- package/lib/public/modules/app-rendering.js +597 -0
- package/lib/public/modules/app-skills-install.js +234 -0
- package/lib/public/modules/command-palette.js +27 -4
- package/lib/public/modules/input.js +31 -20
- package/lib/public/modules/scheduler-config.js +1532 -0
- package/lib/public/modules/scheduler-history.js +79 -0
- package/lib/public/modules/scheduler.js +33 -1554
- package/lib/public/modules/session-search.js +13 -1
- package/lib/public/modules/sidebar-mates.js +812 -0
- package/lib/public/modules/sidebar-mobile.js +1269 -0
- package/lib/public/modules/sidebar-projects.js +1449 -0
- package/lib/public/modules/sidebar-sessions.js +986 -0
- package/lib/public/modules/sidebar.js +232 -4591
- package/lib/public/modules/store.js +27 -0
- package/lib/public/modules/ws-ref.js +7 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +96 -717
- package/lib/sdk-message-processor.js +587 -0
- package/lib/sdk-message-queue.js +42 -0
- package/lib/sdk-skill-discovery.js +131 -0
- package/lib/server-admin.js +712 -0
- package/lib/server-auth.js +737 -0
- package/lib/server-dm.js +221 -0
- package/lib/server-mates.js +281 -0
- package/lib/server-palette.js +110 -0
- package/lib/server-settings.js +479 -0
- package/lib/server-skills.js +280 -0
- package/lib/server.js +246 -2755
- package/lib/sessions.js +11 -4
- package/lib/users-auth.js +146 -0
- package/lib/users-permissions.js +118 -0
- package/lib/users-preferences.js +210 -0
- package/lib/users.js +48 -398
- package/lib/ws-schema.js +498 -0
- package/package.json +1 -1
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
// app-loop-ui.js - Ralph Loop UI: bars, banners, preview modal, execution modal
|
|
2
|
+
// Wizard logic extracted to app-loop-wizard.js
|
|
3
|
+
|
|
4
|
+
import { refreshIcons, iconHtml } from './icons.js';
|
|
5
|
+
import { escapeHtml } from './utils.js';
|
|
6
|
+
import { store } from './store.js';
|
|
7
|
+
import { getWs } from './ws-ref.js';
|
|
8
|
+
import { showConfirm } from './app-misc.js';
|
|
9
|
+
import { openRalphWizard } from './app-loop-wizard.js';
|
|
10
|
+
import { openSchedulerToTab } from './scheduler.js';
|
|
11
|
+
import { showDebateSticky } from './app-debate-ui.js';
|
|
12
|
+
|
|
13
|
+
// Execution modal: module-internal UI var (not in store)
|
|
14
|
+
var pendingIterations = 20;
|
|
15
|
+
|
|
16
|
+
// ========================================================
|
|
17
|
+
// Init
|
|
18
|
+
// ========================================================
|
|
19
|
+
export function initLoopUi() {
|
|
20
|
+
|
|
21
|
+
// --- Preview modal listeners ---
|
|
22
|
+
// Backdrop click intentionally does NOT close the modal (no way to reopen it)
|
|
23
|
+
|
|
24
|
+
// Run button in preview modal footer
|
|
25
|
+
var previewRunBtn = document.getElementById("ralph-preview-run");
|
|
26
|
+
if (previewRunBtn) {
|
|
27
|
+
previewRunBtn.addEventListener("click", function (e) {
|
|
28
|
+
e.stopPropagation();
|
|
29
|
+
closeRalphPreviewModal();
|
|
30
|
+
startLoopFromUi();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Delete/cancel button in preview modal header
|
|
35
|
+
var previewDeleteBtn = document.getElementById("ralph-preview-delete");
|
|
36
|
+
if (previewDeleteBtn) {
|
|
37
|
+
previewDeleteBtn.addEventListener("click", function (e) {
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
closeRalphPreviewModal();
|
|
40
|
+
showConfirm("Discard this loop setup?", function() {
|
|
41
|
+
var ws = getWs();
|
|
42
|
+
if (ws && ws.readyState === 1) {
|
|
43
|
+
ws.send(JSON.stringify({ type: "ralph_wizard_cancel" }));
|
|
44
|
+
}
|
|
45
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
46
|
+
if (stickyEl) {
|
|
47
|
+
stickyEl.classList.add("hidden");
|
|
48
|
+
stickyEl.classList.remove("ralph-ready");
|
|
49
|
+
stickyEl.innerHTML = "";
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
var previewTabs = document.querySelectorAll(".ralph-tab");
|
|
56
|
+
for (var ti = 0; ti < previewTabs.length; ti++) {
|
|
57
|
+
previewTabs[ti].addEventListener("click", function() {
|
|
58
|
+
showRalphPreviewTab(this.getAttribute("data-tab"));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Iterations input in preview modal footer
|
|
63
|
+
var previewIterInput = document.getElementById("ralph-preview-iterations");
|
|
64
|
+
if (previewIterInput) {
|
|
65
|
+
previewIterInput.addEventListener("input", function () {
|
|
66
|
+
pendingIterations = parseInt(this.value, 10) || pendingIterations;
|
|
67
|
+
syncIterationsUi();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ========================================================
|
|
73
|
+
// Iteration sync helper
|
|
74
|
+
// ========================================================
|
|
75
|
+
|
|
76
|
+
function syncIterationsUi() {
|
|
77
|
+
var wizData = store.getState().wizardData || {};
|
|
78
|
+
var isSimple = wizData.loopMode === "simple";
|
|
79
|
+
var label = isSimple ? ("Run x" + pendingIterations) : ("Start (max " + pendingIterations + ")");
|
|
80
|
+
|
|
81
|
+
// Sync sticky bar input
|
|
82
|
+
var stickyIter = document.getElementById("ralph-sticky-iterations");
|
|
83
|
+
if (stickyIter && parseInt(stickyIter.value, 10) !== pendingIterations) {
|
|
84
|
+
stickyIter.value = pendingIterations;
|
|
85
|
+
}
|
|
86
|
+
var stickyStartBtn = document.querySelector(".ralph-sticky-start");
|
|
87
|
+
if (stickyStartBtn) stickyStartBtn.title = label;
|
|
88
|
+
|
|
89
|
+
// Sync preview modal input
|
|
90
|
+
var previewIter = document.getElementById("ralph-preview-iterations");
|
|
91
|
+
if (previewIter && parseInt(previewIter.value, 10) !== pendingIterations) {
|
|
92
|
+
previewIter.value = pendingIterations;
|
|
93
|
+
}
|
|
94
|
+
var comboLabel = document.getElementById("ralph-run-combo-label");
|
|
95
|
+
if (comboLabel) comboLabel.textContent = isSimple ? "Run x" : "Start max";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ========================================================
|
|
99
|
+
// Start loop from UI (shared by sticky bar + exec modal)
|
|
100
|
+
// ========================================================
|
|
101
|
+
|
|
102
|
+
function startLoopFromUi() {
|
|
103
|
+
var basePath = store.getState().basePath;
|
|
104
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
105
|
+
|
|
106
|
+
fetch(basePath + "api/git-dirty")
|
|
107
|
+
.then(function (res) { return res.json(); })
|
|
108
|
+
.then(function (data) {
|
|
109
|
+
if (data.dirty) {
|
|
110
|
+
var fileList = (data.files || []).slice(0, 15).join("\n");
|
|
111
|
+
if (data.files && data.files.length > 15) fileList += "\n... and " + (data.files.length - 15) + " more";
|
|
112
|
+
var msg = "You have uncommitted changes. The loop uses git diff to track progress, so uncommitted files may cause unexpected results.\n\n" + fileList + "\n\nStart anyway?";
|
|
113
|
+
showConfirm(msg, function () {
|
|
114
|
+
sendLoopStart();
|
|
115
|
+
if (stickyEl) { stickyEl.classList.add("hidden"); stickyEl.innerHTML = ""; }
|
|
116
|
+
}, "Start anyway", false);
|
|
117
|
+
} else {
|
|
118
|
+
sendLoopStart();
|
|
119
|
+
if (stickyEl) { stickyEl.classList.add("hidden"); stickyEl.innerHTML = ""; }
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
.catch(function () {
|
|
123
|
+
sendLoopStart();
|
|
124
|
+
if (stickyEl) { stickyEl.classList.add("hidden"); stickyEl.innerHTML = ""; }
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function sendLoopStart() {
|
|
129
|
+
var ws = getWs();
|
|
130
|
+
if (ws && ws.readyState === 1) {
|
|
131
|
+
ws.send(JSON.stringify({ type: "loop_start", maxIterations: pendingIterations }));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ========================================================
|
|
136
|
+
// Loop UI (exported)
|
|
137
|
+
// ========================================================
|
|
138
|
+
|
|
139
|
+
export function updateLoopInputVisibility(loop) {
|
|
140
|
+
var inputArea = document.getElementById("input-area");
|
|
141
|
+
if (!inputArea) return;
|
|
142
|
+
if (loop && loop.active && loop.role !== "crafting") {
|
|
143
|
+
inputArea.style.display = "none";
|
|
144
|
+
} else {
|
|
145
|
+
inputArea.style.display = "";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function updateLoopButton() {
|
|
150
|
+
var section = document.getElementById("ralph-loop-section");
|
|
151
|
+
if (!section) return;
|
|
152
|
+
|
|
153
|
+
var s = store.getState();
|
|
154
|
+
var busy = s.loopActive || s.ralphPhase === "executing";
|
|
155
|
+
var phase = busy ? "executing" : s.ralphPhase;
|
|
156
|
+
|
|
157
|
+
var statusHtml = "";
|
|
158
|
+
var statusClass = "";
|
|
159
|
+
var clickAction = "wizard"; // default
|
|
160
|
+
|
|
161
|
+
if (phase === "crafting") {
|
|
162
|
+
statusHtml = '<span class="ralph-section-status crafting">' + iconHtml("loader", "icon-spin") + ' Crafting\u2026</span>';
|
|
163
|
+
clickAction = "none";
|
|
164
|
+
} else if (phase === "approval") {
|
|
165
|
+
statusHtml = '<span class="ralph-section-status ready">Ready</span>';
|
|
166
|
+
statusClass = "ralph-section-ready";
|
|
167
|
+
clickAction = "none";
|
|
168
|
+
} else if (phase === "executing") {
|
|
169
|
+
var iterText = s.loopIteration > 0 ? "Running \u00b7 iteration " + s.loopIteration + "/" + s.loopMaxIterations : "Starting\u2026";
|
|
170
|
+
statusHtml = '<span class="ralph-section-status running">' + iconHtml("loader", "icon-spin") + ' ' + iterText + '</span>';
|
|
171
|
+
statusClass = "ralph-section-running";
|
|
172
|
+
clickAction = "popover";
|
|
173
|
+
} else if (phase === "done") {
|
|
174
|
+
statusHtml = '<span class="ralph-section-status done">\u2713 Done</span>';
|
|
175
|
+
statusHtml += '<a href="#" class="ralph-section-tasks-link">View in Scheduled Tasks</a>';
|
|
176
|
+
statusClass = "ralph-section-done";
|
|
177
|
+
clickAction = "wizard";
|
|
178
|
+
} else {
|
|
179
|
+
// idle
|
|
180
|
+
statusHtml = '<span class="ralph-section-hint">Start a new loop</span>';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
section.className = "ralph-loop-section" + (statusClass ? " " + statusClass : "");
|
|
184
|
+
section.innerHTML =
|
|
185
|
+
'<div class="ralph-section-inner">' +
|
|
186
|
+
'<div class="ralph-section-header">' +
|
|
187
|
+
'<span class="ralph-section-icon">' + iconHtml("repeat") + '</span>' +
|
|
188
|
+
'<span class="ralph-section-label">Loop</span>' +
|
|
189
|
+
'<span class="loop-experimental"><i data-lucide="flask-conical"></i> experimental</span>' +
|
|
190
|
+
'</div>' +
|
|
191
|
+
'<div class="ralph-section-body">' + statusHtml + '</div>' +
|
|
192
|
+
'</div>';
|
|
193
|
+
|
|
194
|
+
refreshIcons();
|
|
195
|
+
|
|
196
|
+
// Click handler on header
|
|
197
|
+
var header = section.querySelector(".ralph-section-header");
|
|
198
|
+
if (header) {
|
|
199
|
+
header.style.cursor = clickAction === "none" ? "default" : "pointer";
|
|
200
|
+
header.addEventListener("click", function() {
|
|
201
|
+
if (clickAction === "popover") {
|
|
202
|
+
toggleLoopPopover();
|
|
203
|
+
} else if (clickAction === "wizard") {
|
|
204
|
+
openRalphWizard();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// "View in Scheduled Tasks" link
|
|
210
|
+
var tasksLink = section.querySelector(".ralph-section-tasks-link");
|
|
211
|
+
if (tasksLink) {
|
|
212
|
+
tasksLink.addEventListener("click", function(e) {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
e.stopPropagation();
|
|
215
|
+
openSchedulerToTab("library");
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function showLoopBanner(show) {
|
|
221
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
222
|
+
if (!stickyEl) { updateLoopButton(); return; }
|
|
223
|
+
if (!show) {
|
|
224
|
+
stickyEl.classList.add("hidden");
|
|
225
|
+
stickyEl.classList.remove("ralph-running");
|
|
226
|
+
stickyEl.innerHTML = "";
|
|
227
|
+
updateLoopButton();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
var bannerLabel = store.getState().loopBannerName || "Loop";
|
|
232
|
+
stickyEl.innerHTML =
|
|
233
|
+
'<div class="ralph-sticky-inner">' +
|
|
234
|
+
'<div class="ralph-sticky-header">' +
|
|
235
|
+
'<span class="ralph-sticky-icon">' + iconHtml("repeat") + '</span>' +
|
|
236
|
+
'<span class="ralph-sticky-label">' + escapeHtml(bannerLabel) + '</span>' +
|
|
237
|
+
'<span class="ralph-sticky-status" id="loop-status">Starting\u2026</span>' +
|
|
238
|
+
'<button class="ralph-sticky-action ralph-sticky-stop" title="Stop loop">' + iconHtml("square") + '</button>' +
|
|
239
|
+
'</div>' +
|
|
240
|
+
'</div>';
|
|
241
|
+
stickyEl.classList.remove("hidden", "ralph-ready");
|
|
242
|
+
stickyEl.classList.add("ralph-running");
|
|
243
|
+
refreshIcons();
|
|
244
|
+
|
|
245
|
+
stickyEl.querySelector(".ralph-sticky-stop").addEventListener("click", function(e) {
|
|
246
|
+
e.stopPropagation();
|
|
247
|
+
var w = getWs();
|
|
248
|
+
if (w && w.readyState === 1) {
|
|
249
|
+
w.send(JSON.stringify({ type: "loop_stop" }));
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
updateLoopButton();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function updateLoopBanner(iteration, maxIterations, phase) {
|
|
256
|
+
var statusEl = document.getElementById("loop-status");
|
|
257
|
+
if (!statusEl) return;
|
|
258
|
+
var text;
|
|
259
|
+
if (phase === "stopping") {
|
|
260
|
+
text = "Stopping\u2026";
|
|
261
|
+
} else if (maxIterations <= 1) {
|
|
262
|
+
text = phase === "judging" ? "judging\u2026" : "running";
|
|
263
|
+
} else {
|
|
264
|
+
text = "#" + iteration + "/" + maxIterations;
|
|
265
|
+
if (phase === "judging") text += " judging\u2026";
|
|
266
|
+
else text += " running";
|
|
267
|
+
}
|
|
268
|
+
statusEl.textContent = text;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function updateRalphBars() {
|
|
272
|
+
// Task source uses the scheduler panel, not the sticky bar
|
|
273
|
+
var s = store.getState();
|
|
274
|
+
var isTaskSource = s.ralphCraftingSource !== "ralph";
|
|
275
|
+
var onCraftingSession = s.ralphCraftingSessionId && s.activeSessionId === s.ralphCraftingSessionId;
|
|
276
|
+
// If approval phase but no craftingSessionId (recovered after server restart), show bar anyway
|
|
277
|
+
var recoveredApproval = s.ralphPhase === "approval" && !s.ralphCraftingSessionId;
|
|
278
|
+
if (!isTaskSource && s.ralphPhase === "crafting" && onCraftingSession) {
|
|
279
|
+
showRalphCraftingBar(true);
|
|
280
|
+
} else {
|
|
281
|
+
showRalphCraftingBar(false);
|
|
282
|
+
}
|
|
283
|
+
if (!isTaskSource && s.ralphPhase === "approval") {
|
|
284
|
+
showRalphApprovalBar(true);
|
|
285
|
+
} else {
|
|
286
|
+
showRalphApprovalBar(false);
|
|
287
|
+
}
|
|
288
|
+
// Restore running loop banner on session switch
|
|
289
|
+
if (s.loopActive && s.ralphPhase === "executing") {
|
|
290
|
+
showLoopBanner(true);
|
|
291
|
+
if (s.loopIteration > 0) {
|
|
292
|
+
updateLoopBanner(s.loopIteration, s.loopMaxIterations, "running");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Restore debate sticky on session switch
|
|
297
|
+
var debateStickyState = store.getState().debateStickyState;
|
|
298
|
+
if (debateStickyState && debateStickyState.phase) {
|
|
299
|
+
showDebateSticky(debateStickyState.phase, debateStickyState.msg);
|
|
300
|
+
} else {
|
|
301
|
+
showDebateSticky("hide", null);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ========================================================
|
|
306
|
+
// Internal: toggleLoopPopover
|
|
307
|
+
// ========================================================
|
|
308
|
+
|
|
309
|
+
function toggleLoopPopover() {
|
|
310
|
+
var existing = document.getElementById("loop-status-modal");
|
|
311
|
+
if (existing) {
|
|
312
|
+
existing.remove();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
var wizData = store.getState().wizardData || {};
|
|
317
|
+
var taskPreview = wizData.task || "\u2014";
|
|
318
|
+
if (taskPreview.length > 120) taskPreview = taskPreview.substring(0, 120) + "\u2026";
|
|
319
|
+
var _s = store.getState();
|
|
320
|
+
var statusText = "Iteration #" + _s.loopIteration + " / " + _s.loopMaxIterations;
|
|
321
|
+
|
|
322
|
+
var modal = document.createElement("div");
|
|
323
|
+
modal.id = "loop-status-modal";
|
|
324
|
+
modal.className = "loop-status-modal";
|
|
325
|
+
modal.innerHTML =
|
|
326
|
+
'<div class="loop-status-backdrop"></div>' +
|
|
327
|
+
'<div class="loop-status-dialog">' +
|
|
328
|
+
'<div class="loop-status-dialog-header">' +
|
|
329
|
+
'<span class="loop-status-dialog-icon">' + iconHtml("repeat") + '</span>' +
|
|
330
|
+
'<span class="loop-status-dialog-title">Loop</span>' +
|
|
331
|
+
'<button class="loop-status-dialog-close" title="Close">' + iconHtml("x") + '</button>' +
|
|
332
|
+
'</div>' +
|
|
333
|
+
'<div class="loop-status-dialog-body">' +
|
|
334
|
+
'<div class="loop-status-dialog-row">' +
|
|
335
|
+
'<span class="loop-status-dialog-label">Progress</span>' +
|
|
336
|
+
'<span class="loop-status-dialog-value">' + escapeHtml(statusText) + '</span>' +
|
|
337
|
+
'</div>' +
|
|
338
|
+
'<div class="loop-status-dialog-row">' +
|
|
339
|
+
'<span class="loop-status-dialog-label">Task</span>' +
|
|
340
|
+
'<span class="loop-status-dialog-value loop-status-dialog-task">' + escapeHtml(taskPreview) + '</span>' +
|
|
341
|
+
'</div>' +
|
|
342
|
+
'</div>' +
|
|
343
|
+
'<div class="loop-status-dialog-footer">' +
|
|
344
|
+
'<button class="loop-status-dialog-stop">' + iconHtml("square") + ' Stop loop</button>' +
|
|
345
|
+
'</div>' +
|
|
346
|
+
'</div>';
|
|
347
|
+
|
|
348
|
+
document.body.appendChild(modal);
|
|
349
|
+
refreshIcons();
|
|
350
|
+
|
|
351
|
+
function closeModal() { modal.remove(); }
|
|
352
|
+
|
|
353
|
+
modal.querySelector(".loop-status-backdrop").addEventListener("click", closeModal);
|
|
354
|
+
modal.querySelector(".loop-status-dialog-close").addEventListener("click", closeModal);
|
|
355
|
+
|
|
356
|
+
modal.querySelector(".loop-status-dialog-stop").addEventListener("click", function(e) {
|
|
357
|
+
e.stopPropagation();
|
|
358
|
+
closeModal();
|
|
359
|
+
showConfirm("Stop the running " + (store.getState().loopBannerName || "loop") + "?", function() {
|
|
360
|
+
var w = getWs();
|
|
361
|
+
if (w && w.readyState === 1) {
|
|
362
|
+
w.send(JSON.stringify({ type: "loop_stop" }));
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ========================================================
|
|
369
|
+
// Crafting / Approval bars (exported)
|
|
370
|
+
// ========================================================
|
|
371
|
+
|
|
372
|
+
export function showRalphCraftingBar(show) {
|
|
373
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
374
|
+
if (!stickyEl) return;
|
|
375
|
+
if (!show) {
|
|
376
|
+
stickyEl.classList.add("hidden");
|
|
377
|
+
stickyEl.innerHTML = "";
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
stickyEl.innerHTML =
|
|
381
|
+
'<div class="ralph-sticky-inner">' +
|
|
382
|
+
'<div class="ralph-sticky-header">' +
|
|
383
|
+
'<span class="ralph-sticky-icon">' + iconHtml("repeat") + '</span>' +
|
|
384
|
+
'<span class="ralph-sticky-label">Ralph</span>' +
|
|
385
|
+
'<span class="ralph-sticky-status">' + iconHtml("loader", "icon-spin") + ' Preparing\u2026</span>' +
|
|
386
|
+
'<button class="ralph-sticky-cancel" title="Cancel">' + iconHtml("x") + '</button>' +
|
|
387
|
+
'</div>' +
|
|
388
|
+
'</div>';
|
|
389
|
+
stickyEl.classList.remove("hidden");
|
|
390
|
+
refreshIcons();
|
|
391
|
+
|
|
392
|
+
var cancelBtn = stickyEl.querySelector(".ralph-sticky-cancel");
|
|
393
|
+
if (cancelBtn) {
|
|
394
|
+
cancelBtn.addEventListener("click", function(e) {
|
|
395
|
+
e.stopPropagation();
|
|
396
|
+
var ws = getWs();
|
|
397
|
+
if (ws && ws.readyState === 1) {
|
|
398
|
+
ws.send(JSON.stringify({ type: "ralph_cancel_crafting" }));
|
|
399
|
+
}
|
|
400
|
+
showRalphCraftingBar(false);
|
|
401
|
+
showRalphApprovalBar(false);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function showRalphApprovalBar(show) {
|
|
407
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
408
|
+
if (!stickyEl) return;
|
|
409
|
+
if (!show) {
|
|
410
|
+
// Only clear if we're in approval mode (don't clobber crafting)
|
|
411
|
+
if (store.getState().ralphPhase !== "crafting") {
|
|
412
|
+
stickyEl.classList.add("hidden");
|
|
413
|
+
stickyEl.innerHTML = "";
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
var wizData = store.getState().wizardData || {};
|
|
419
|
+
var isSimple = wizData.loopMode === "simple";
|
|
420
|
+
var defaultIter = isSimple ? 5 : 20;
|
|
421
|
+
pendingIterations = defaultIter;
|
|
422
|
+
|
|
423
|
+
var runLabel = isSimple ? ("Run x" + defaultIter) : ("Start (max " + defaultIter + ")");
|
|
424
|
+
|
|
425
|
+
stickyEl.innerHTML =
|
|
426
|
+
'<div class="ralph-sticky-inner">' +
|
|
427
|
+
'<div class="ralph-sticky-header" id="ralph-sticky-header">' +
|
|
428
|
+
'<span class="ralph-sticky-icon">' + iconHtml("repeat") + '</span>' +
|
|
429
|
+
'<span class="ralph-sticky-label">Ralph</span>' +
|
|
430
|
+
'<span class="ralph-sticky-status" id="ralph-sticky-status">Ready</span>' +
|
|
431
|
+
'<input type="number" id="ralph-sticky-iterations" class="ralph-input-number ralph-sticky-iter" value="' + defaultIter + '" min="1" max="100">' +
|
|
432
|
+
'<button class="ralph-sticky-action ralph-sticky-preview" title="Preview files">' + iconHtml("eye") + '</button>' +
|
|
433
|
+
'<button class="ralph-sticky-action ralph-sticky-start" title="' + runLabel + '">' + iconHtml(wizData.cron ? "calendar-clock" : "play") + '</button>' +
|
|
434
|
+
'<button class="ralph-sticky-action ralph-sticky-dismiss" title="Cancel and discard">' + iconHtml("x") + '</button>' +
|
|
435
|
+
'</div>' +
|
|
436
|
+
'</div>';
|
|
437
|
+
stickyEl.classList.remove("hidden");
|
|
438
|
+
refreshIcons();
|
|
439
|
+
|
|
440
|
+
// Iteration input handler
|
|
441
|
+
var stickyIterInput = document.getElementById("ralph-sticky-iterations");
|
|
442
|
+
if (stickyIterInput) {
|
|
443
|
+
stickyIterInput.addEventListener("input", function () {
|
|
444
|
+
pendingIterations = parseInt(this.value, 10) || pendingIterations;
|
|
445
|
+
syncIterationsUi();
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
stickyEl.querySelector(".ralph-sticky-preview").addEventListener("click", function(e) {
|
|
450
|
+
e.stopPropagation();
|
|
451
|
+
var ws = getWs();
|
|
452
|
+
if (ws && ws.readyState === 1) {
|
|
453
|
+
ws.send(JSON.stringify({ type: "ralph_preview_files" }));
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
stickyEl.querySelector(".ralph-sticky-start").addEventListener("click", function(e) {
|
|
458
|
+
e.stopPropagation();
|
|
459
|
+
startLoopFromUi();
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
stickyEl.querySelector(".ralph-sticky-dismiss").addEventListener("click", function(e) {
|
|
463
|
+
e.stopPropagation();
|
|
464
|
+
showConfirm("Discard this Ralph Loop setup?", function() {
|
|
465
|
+
var ws = getWs();
|
|
466
|
+
if (ws && ws.readyState === 1) {
|
|
467
|
+
ws.send(JSON.stringify({ type: "ralph_wizard_cancel" }));
|
|
468
|
+
}
|
|
469
|
+
stickyEl.classList.add("hidden");
|
|
470
|
+
stickyEl.classList.remove("ralph-ready");
|
|
471
|
+
stickyEl.innerHTML = "";
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
updateRalphApprovalStatus();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export function updateRalphApprovalStatus() {
|
|
479
|
+
var stickyEl = document.getElementById("ralph-sticky");
|
|
480
|
+
var statusEl = document.getElementById("ralph-sticky-status");
|
|
481
|
+
var startBtn = document.querySelector(".ralph-sticky-start");
|
|
482
|
+
if (!statusEl) return;
|
|
483
|
+
|
|
484
|
+
var wizData = store.getState().wizardData || {};
|
|
485
|
+
var isSimple = wizData.loopMode === "simple";
|
|
486
|
+
var _rf = store.getState().ralphFilesReady;
|
|
487
|
+
var ready = isSimple ? _rf.promptReady : _rf.bothReady;
|
|
488
|
+
|
|
489
|
+
if (ready) {
|
|
490
|
+
statusEl.textContent = "Ready";
|
|
491
|
+
if (startBtn) startBtn.disabled = false;
|
|
492
|
+
if (stickyEl) stickyEl.classList.add("ralph-ready");
|
|
493
|
+
} else if (_rf.promptReady || _rf.judgeReady) {
|
|
494
|
+
statusEl.textContent = "Partial\u2026";
|
|
495
|
+
if (startBtn) startBtn.disabled = true;
|
|
496
|
+
if (stickyEl) stickyEl.classList.remove("ralph-ready");
|
|
497
|
+
} else {
|
|
498
|
+
statusEl.textContent = "Waiting\u2026";
|
|
499
|
+
if (startBtn) startBtn.disabled = true;
|
|
500
|
+
if (stickyEl) stickyEl.classList.remove("ralph-ready");
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ========================================================
|
|
505
|
+
// Preview modal (exported: openRalphPreviewModal, showExecModal)
|
|
506
|
+
// The preview modal doubles as the execution modal.
|
|
507
|
+
// showExecModal opens it with auto-popup flag set.
|
|
508
|
+
// ========================================================
|
|
509
|
+
|
|
510
|
+
export function showExecModal() {
|
|
511
|
+
store.setState({ execModalShown: true });
|
|
512
|
+
// Open the preview modal immediately, then request file content to fill in
|
|
513
|
+
openRalphPreviewModal();
|
|
514
|
+
var ws = getWs();
|
|
515
|
+
if (ws && ws.readyState === 1) {
|
|
516
|
+
ws.send(JSON.stringify({ type: "ralph_preview_files" }));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function closeExecModal() {
|
|
521
|
+
closeRalphPreviewModal();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export function updateExecModalStatus() {
|
|
525
|
+
// no-op, preview modal updates on open
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function openRalphPreviewModal() {
|
|
529
|
+
var modal = document.getElementById("ralph-preview-modal");
|
|
530
|
+
if (!modal) return;
|
|
531
|
+
|
|
532
|
+
var wizData = store.getState().wizardData || {};
|
|
533
|
+
var isSimple = wizData.loopMode === "simple";
|
|
534
|
+
|
|
535
|
+
// Set defaults if not yet set
|
|
536
|
+
if (!pendingIterations || pendingIterations <= 0) {
|
|
537
|
+
pendingIterations = isSimple ? 5 : 20;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Set name
|
|
541
|
+
var nameEl = document.getElementById("ralph-preview-name");
|
|
542
|
+
if (nameEl) {
|
|
543
|
+
nameEl.textContent = (wizData && wizData.name) || "Loop";
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Hide JUDGE.md tab for simple loop
|
|
547
|
+
var judgeTab = document.querySelector('#ralph-preview-modal .ralph-tab[data-tab="judge"]');
|
|
548
|
+
if (judgeTab) judgeTab.style.display = isSimple ? "none" : "";
|
|
549
|
+
|
|
550
|
+
// Update footer: iteration label + run button
|
|
551
|
+
var iterInput = document.getElementById("ralph-preview-iterations");
|
|
552
|
+
if (iterInput) iterInput.value = pendingIterations;
|
|
553
|
+
|
|
554
|
+
var comboLabel = document.getElementById("ralph-run-combo-label");
|
|
555
|
+
if (comboLabel) comboLabel.textContent = isSimple ? "Run x" : "Start max";
|
|
556
|
+
|
|
557
|
+
var runBtn = document.getElementById("ralph-preview-run");
|
|
558
|
+
if (runBtn) runBtn.disabled = !store.getState().ralphFilesReady.bothReady;
|
|
559
|
+
|
|
560
|
+
showRalphPreviewTab("prompt");
|
|
561
|
+
modal.classList.remove("hidden");
|
|
562
|
+
refreshIcons();
|
|
563
|
+
syncIterationsUi();
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function closeRalphPreviewModal() {
|
|
567
|
+
var modal = document.getElementById("ralph-preview-modal");
|
|
568
|
+
if (modal) modal.classList.add("hidden");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function showRalphPreviewTab(tab) {
|
|
572
|
+
var tabs = document.querySelectorAll("#ralph-preview-modal .ralph-tab");
|
|
573
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
574
|
+
if (tabs[i].getAttribute("data-tab") === tab) {
|
|
575
|
+
tabs[i].classList.add("active");
|
|
576
|
+
} else {
|
|
577
|
+
tabs[i].classList.remove("active");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
var body = document.getElementById("ralph-preview-body");
|
|
581
|
+
if (!body) return;
|
|
582
|
+
var _rpc = store.getState().ralphPreviewContent;
|
|
583
|
+
var content = tab === "prompt" ? _rpc.prompt : _rpc.judge;
|
|
584
|
+
if (typeof marked !== "undefined" && marked.parse) {
|
|
585
|
+
body.innerHTML = '<div class="md-content">' + DOMPurify.sanitize(marked.parse(content)) + '</div>';
|
|
586
|
+
} else {
|
|
587
|
+
body.textContent = content;
|
|
588
|
+
}
|
|
589
|
+
}
|