clay-server 2.7.1 → 2.7.2
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/bin/cli.js +1 -2
- package/lib/config.js +4 -7
- package/lib/project.js +5 -177
- package/lib/public/app.js +50 -204
- package/lib/public/css/loop.css +1 -62
- package/lib/public/index.html +10 -83
- package/lib/public/modules/markdown.js +0 -10
- package/lib/public/style.css +0 -2
- package/lib/sessions.js +1 -1
- package/package.json +1 -1
- package/lib/public/css/scheduler-modal.css +0 -546
- package/lib/public/css/scheduler.css +0 -944
- package/lib/public/modules/scheduler.js +0 -1240
- package/lib/scheduler.js +0 -362
package/lib/public/app.js
CHANGED
|
@@ -14,7 +14,6 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
14
14
|
import { initServerSettings, updateSettingsStats, updateSettingsModels, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './modules/server-settings.js';
|
|
15
15
|
import { initProjectSettings, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, isProjectSettingsOpen, handleProjectSharedEnv, handleProjectSharedEnvSaved } from './modules/project-settings.js';
|
|
16
16
|
import { initSkills, handleSkillInstalled, handleSkillUninstalled } from './modules/skills.js';
|
|
17
|
-
import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, openSchedulerToTab, isSchedulerOpen, closeScheduler, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './modules/scheduler.js';
|
|
18
17
|
|
|
19
18
|
// --- Base path for multi-project routing ---
|
|
20
19
|
var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
|
|
@@ -180,7 +179,7 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
180
179
|
var ralphPhase = "idle"; // idle | wizard | crafting | approval | executing | done
|
|
181
180
|
var ralphCraftingSessionId = null;
|
|
182
181
|
var wizardStep = 1;
|
|
183
|
-
var wizardData = { name: "", task: "", maxIterations: 25
|
|
182
|
+
var wizardData = { name: "", task: "", maxIterations: 25 };
|
|
184
183
|
var ralphFilesReady = { promptReady: false, judgeReady: false, bothReady: false };
|
|
185
184
|
var ralphPreviewContent = { prompt: "", judge: "" };
|
|
186
185
|
var slashCommands = [];
|
|
@@ -1745,7 +1744,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
1745
1744
|
if (!slug || slug === currentSlug) return;
|
|
1746
1745
|
resetFileBrowser();
|
|
1747
1746
|
closeArchive();
|
|
1748
|
-
if (isSchedulerOpen()) closeScheduler();
|
|
1749
1747
|
currentSlug = slug;
|
|
1750
1748
|
basePath = "/p/" + slug + "/";
|
|
1751
1749
|
wsPath = "/p/" + slug + "/ws";
|
|
@@ -1760,7 +1758,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
1760
1758
|
if (newSlug && newSlug !== currentSlug) {
|
|
1761
1759
|
resetFileBrowser();
|
|
1762
1760
|
closeArchive();
|
|
1763
|
-
if (isSchedulerOpen()) closeScheduler();
|
|
1764
1761
|
currentSlug = newSlug;
|
|
1765
1762
|
basePath = "/p/" + newSlug + "/";
|
|
1766
1763
|
wsPath = "/p/" + newSlug + "/ws";
|
|
@@ -2066,22 +2063,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2066
2063
|
handleSkillUninstalled(msg);
|
|
2067
2064
|
break;
|
|
2068
2065
|
|
|
2069
|
-
case "loop_registry_updated":
|
|
2070
|
-
handleLoopRegistryUpdated(msg);
|
|
2071
|
-
break;
|
|
2072
|
-
|
|
2073
|
-
case "schedule_run_started":
|
|
2074
|
-
handleScheduleRunStarted(msg);
|
|
2075
|
-
break;
|
|
2076
|
-
|
|
2077
|
-
case "schedule_run_finished":
|
|
2078
|
-
handleScheduleRunFinished(msg);
|
|
2079
|
-
break;
|
|
2080
|
-
|
|
2081
|
-
case "loop_scheduled":
|
|
2082
|
-
handleLoopScheduled(msg);
|
|
2083
|
-
break;
|
|
2084
|
-
|
|
2085
2066
|
case "input_sync":
|
|
2086
2067
|
handleInputSync(msg.text);
|
|
2087
2068
|
break;
|
|
@@ -2593,9 +2574,7 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2593
2574
|
|
|
2594
2575
|
case "loop_iteration":
|
|
2595
2576
|
loopIteration = msg.iteration;
|
|
2596
|
-
loopMaxIterations = msg.maxIterations;
|
|
2597
2577
|
updateLoopBanner(msg.iteration, msg.maxIterations, "running");
|
|
2598
|
-
updateLoopButton();
|
|
2599
2578
|
addSystemMessage("Ralph Loop iteration #" + msg.iteration + " started", false);
|
|
2600
2579
|
break;
|
|
2601
2580
|
|
|
@@ -2644,7 +2623,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2644
2623
|
ralphCraftingSessionId = msg.sessionId || activeSessionId;
|
|
2645
2624
|
updateLoopButton();
|
|
2646
2625
|
updateRalphBars();
|
|
2647
|
-
enterCraftingMode(ralphCraftingSessionId, msg.taskId || null);
|
|
2648
2626
|
break;
|
|
2649
2627
|
|
|
2650
2628
|
case "ralph_files_status":
|
|
@@ -2656,15 +2634,10 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2656
2634
|
if (msg.bothReady && (ralphPhase === "crafting" || ralphPhase === "approval")) {
|
|
2657
2635
|
ralphPhase = "approval";
|
|
2658
2636
|
showRalphApprovalBar(true);
|
|
2659
|
-
if (isSchedulerOpen()) exitCraftingMode(msg.taskId || null);
|
|
2660
2637
|
}
|
|
2661
2638
|
updateRalphApprovalStatus();
|
|
2662
2639
|
break;
|
|
2663
2640
|
|
|
2664
|
-
case "loop_registry_files_content":
|
|
2665
|
-
handleLoopRegistryFiles(msg);
|
|
2666
|
-
break;
|
|
2667
|
-
|
|
2668
2641
|
case "ralph_files_content":
|
|
2669
2642
|
ralphPreviewContent = { prompt: msg.prompt || "", judge: msg.judge || "" };
|
|
2670
2643
|
openRalphPreviewModal();
|
|
@@ -2889,7 +2862,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2889
2862
|
var stickyNotesSidebarBtn = $("sticky-notes-sidebar-btn");
|
|
2890
2863
|
if (stickyNotesSidebarBtn) {
|
|
2891
2864
|
stickyNotesSidebarBtn.addEventListener("click", function () {
|
|
2892
|
-
if (isSchedulerOpen()) closeScheduler();
|
|
2893
2865
|
if (isArchiveOpen()) {
|
|
2894
2866
|
closeArchive();
|
|
2895
2867
|
} else {
|
|
@@ -2898,11 +2870,11 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2898
2870
|
});
|
|
2899
2871
|
}
|
|
2900
2872
|
|
|
2901
|
-
// Close archive
|
|
2873
|
+
// Close archive when switching to other sidebar panels
|
|
2902
2874
|
var fileBrowserBtn = $("file-browser-btn");
|
|
2903
2875
|
var terminalSidebarBtn = $("terminal-sidebar-btn");
|
|
2904
|
-
if (fileBrowserBtn) fileBrowserBtn.addEventListener("click", function () { if (isArchiveOpen()) closeArchive();
|
|
2905
|
-
if (terminalSidebarBtn) terminalSidebarBtn.addEventListener("click", function () { if (isArchiveOpen()) closeArchive();
|
|
2876
|
+
if (fileBrowserBtn) fileBrowserBtn.addEventListener("click", function () { if (isArchiveOpen()) closeArchive(); });
|
|
2877
|
+
if (terminalSidebarBtn) terminalSidebarBtn.addEventListener("click", function () { if (isArchiveOpen()) closeArchive(); });
|
|
2906
2878
|
|
|
2907
2879
|
// --- Ralph Loop UI ---
|
|
2908
2880
|
function updateLoopInputVisibility(loop) {
|
|
@@ -2916,72 +2888,39 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
2916
2888
|
}
|
|
2917
2889
|
|
|
2918
2890
|
function updateLoopButton() {
|
|
2919
|
-
var
|
|
2920
|
-
if (!
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
if (phase === "crafting") {
|
|
2930
|
-
statusHtml = '<span class="ralph-section-status crafting">' + iconHtml("loader", "icon-spin") + ' Crafting\u2026</span>';
|
|
2931
|
-
clickAction = "none";
|
|
2932
|
-
} else if (phase === "approval") {
|
|
2933
|
-
statusHtml = '<span class="ralph-section-status ready">Ready</span>';
|
|
2934
|
-
statusClass = "ralph-section-ready";
|
|
2935
|
-
clickAction = "none";
|
|
2936
|
-
} else if (phase === "executing") {
|
|
2937
|
-
var iterText = loopIteration > 0 ? "Running \u00b7 iteration " + loopIteration + "/" + loopMaxIterations : "Starting\u2026";
|
|
2938
|
-
statusHtml = '<span class="ralph-section-status running">' + iconHtml("loader", "icon-spin") + ' ' + iterText + '</span>';
|
|
2939
|
-
statusClass = "ralph-section-running";
|
|
2940
|
-
clickAction = "popover";
|
|
2941
|
-
} else if (phase === "done") {
|
|
2942
|
-
statusHtml = '<span class="ralph-section-status done">\u2713 Done</span>';
|
|
2943
|
-
statusHtml += '<a href="#" class="ralph-section-tasks-link">View in Scheduled Tasks</a>';
|
|
2944
|
-
statusClass = "ralph-section-done";
|
|
2945
|
-
clickAction = "wizard";
|
|
2946
|
-
} else {
|
|
2947
|
-
// idle
|
|
2948
|
-
statusHtml = '<span class="ralph-section-hint">Start a new loop</span>';
|
|
2949
|
-
}
|
|
2950
|
-
|
|
2951
|
-
section.className = "ralph-loop-section" + (statusClass ? " " + statusClass : "");
|
|
2952
|
-
section.innerHTML =
|
|
2953
|
-
'<div class="ralph-section-inner">' +
|
|
2954
|
-
'<div class="ralph-section-header">' +
|
|
2955
|
-
'<span class="ralph-section-icon">' + iconHtml("repeat") + '</span>' +
|
|
2956
|
-
'<span class="ralph-section-label">Ralph Loop</span>' +
|
|
2957
|
-
'<span class="loop-experimental"><i data-lucide="flask-conical"></i> experimental</span>' +
|
|
2958
|
-
'</div>' +
|
|
2959
|
-
'<div class="ralph-section-body">' + statusHtml + '</div>' +
|
|
2960
|
-
'</div>';
|
|
2961
|
-
|
|
2962
|
-
refreshIcons();
|
|
2963
|
-
|
|
2964
|
-
// Click handler on header
|
|
2965
|
-
var header = section.querySelector(".ralph-section-header");
|
|
2966
|
-
if (header) {
|
|
2967
|
-
header.style.cursor = clickAction === "none" ? "default" : "pointer";
|
|
2968
|
-
header.addEventListener("click", function() {
|
|
2969
|
-
if (clickAction === "popover") {
|
|
2891
|
+
var existing = document.getElementById("loop-start-btn");
|
|
2892
|
+
if (!existing) {
|
|
2893
|
+
var btn = document.createElement("button");
|
|
2894
|
+
btn.id = "loop-start-btn";
|
|
2895
|
+
btn.innerHTML = '<i data-lucide="repeat"></i> <span>Ralph Loop</span><span class="loop-experimental"><i data-lucide="flask-conical"></i> Experimental</span>';
|
|
2896
|
+
btn.title = "Start a new Ralph Loop";
|
|
2897
|
+
btn.addEventListener("click", function() {
|
|
2898
|
+
var busy = loopActive || ralphPhase === "executing";
|
|
2899
|
+
if (busy) {
|
|
2970
2900
|
toggleLoopPopover();
|
|
2971
|
-
} else
|
|
2901
|
+
} else {
|
|
2972
2902
|
openRalphWizard();
|
|
2973
2903
|
}
|
|
2974
2904
|
});
|
|
2905
|
+
var sessionActions = document.getElementById("session-actions");
|
|
2906
|
+
if (sessionActions) sessionActions.appendChild(btn);
|
|
2907
|
+
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
2908
|
+
existing = btn;
|
|
2975
2909
|
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2910
|
+
var busy = loopActive || ralphPhase === "executing";
|
|
2911
|
+
var hint = existing.querySelector(".loop-busy-hint");
|
|
2912
|
+
if (busy) {
|
|
2913
|
+
existing.style.opacity = "";
|
|
2914
|
+
existing.style.pointerEvents = "";
|
|
2915
|
+
if (!hint) {
|
|
2916
|
+
hint = document.createElement("span");
|
|
2917
|
+
hint.className = "loop-busy-hint";
|
|
2918
|
+
hint.innerHTML = iconHtml("loader", "icon-spin");
|
|
2919
|
+
existing.appendChild(hint);
|
|
2920
|
+
refreshIcons();
|
|
2921
|
+
}
|
|
2922
|
+
} else {
|
|
2923
|
+
if (hint) hint.remove();
|
|
2985
2924
|
}
|
|
2986
2925
|
}
|
|
2987
2926
|
|
|
@@ -3164,7 +3103,20 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3164
3103
|
var nextBtn = document.getElementById("ralph-wizard-next");
|
|
3165
3104
|
if (backBtn) backBtn.style.visibility = wizardStep === 1 ? "hidden" : "visible";
|
|
3166
3105
|
if (skipBtn) skipBtn.style.display = "none";
|
|
3167
|
-
if (nextBtn) nextBtn.textContent = wizardStep ===
|
|
3106
|
+
if (nextBtn) nextBtn.textContent = wizardStep === 3 ? "Launch" : wizardStep === 1 ? "Get Started" : "Next";
|
|
3107
|
+
|
|
3108
|
+
// Build review on step 3
|
|
3109
|
+
if (wizardStep === 3) {
|
|
3110
|
+
collectWizardData();
|
|
3111
|
+
var summary = document.getElementById("ralph-review-summary");
|
|
3112
|
+
if (summary) {
|
|
3113
|
+
summary.innerHTML =
|
|
3114
|
+
'<div class="ralph-review-label">Name</div>' +
|
|
3115
|
+
'<div class="ralph-review-value">' + escapeHtml(wizardData.name || "(empty)") + '</div>' +
|
|
3116
|
+
'<div class="ralph-review-label">Task</div>' +
|
|
3117
|
+
'<div class="ralph-review-value">' + escapeHtml(wizardData.task || "(empty)") + '</div>';
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3168
3120
|
}
|
|
3169
3121
|
|
|
3170
3122
|
function collectWizardData() {
|
|
@@ -3174,59 +3126,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3174
3126
|
wizardData.name = nameEl ? nameEl.value.replace(/[^a-zA-Z0-9_-]/g, "").trim() : "";
|
|
3175
3127
|
wizardData.task = taskEl ? taskEl.value.trim() : "";
|
|
3176
3128
|
wizardData.maxIterations = iterEl ? parseInt(iterEl.value, 10) || 25 : 25;
|
|
3177
|
-
wizardData.cron = null;
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
function buildWizardCron() {
|
|
3181
|
-
var repeatEl = document.getElementById("ralph-repeat");
|
|
3182
|
-
if (!repeatEl) return null;
|
|
3183
|
-
var preset = repeatEl.value;
|
|
3184
|
-
if (preset === "none") return null;
|
|
3185
|
-
|
|
3186
|
-
var timeEl = document.getElementById("ralph-time");
|
|
3187
|
-
var timeVal = timeEl ? timeEl.value : "09:00";
|
|
3188
|
-
var timeParts = timeVal.split(":");
|
|
3189
|
-
var hour = parseInt(timeParts[0], 10) || 9;
|
|
3190
|
-
var minute = parseInt(timeParts[1], 10) || 0;
|
|
3191
|
-
|
|
3192
|
-
if (preset === "daily") return minute + " " + hour + " * * *";
|
|
3193
|
-
if (preset === "weekdays") return minute + " " + hour + " * * 1-5";
|
|
3194
|
-
if (preset === "weekly") return minute + " " + hour + " * * " + new Date().getDay();
|
|
3195
|
-
if (preset === "monthly") return minute + " " + hour + " " + new Date().getDate() + " * *";
|
|
3196
|
-
|
|
3197
|
-
if (preset === "custom") {
|
|
3198
|
-
var unitEl = document.getElementById("ralph-repeat-unit");
|
|
3199
|
-
var unit = unitEl ? unitEl.value : "day";
|
|
3200
|
-
if (unit === "day") return minute + " " + hour + " * * *";
|
|
3201
|
-
if (unit === "month") return minute + " " + hour + " " + new Date().getDate() + " * *";
|
|
3202
|
-
// week: collect selected days
|
|
3203
|
-
var dowBtns = document.querySelectorAll("#ralph-custom-repeat .sched-dow-btn.active");
|
|
3204
|
-
var days = [];
|
|
3205
|
-
for (var i = 0; i < dowBtns.length; i++) {
|
|
3206
|
-
days.push(dowBtns[i].dataset.dow);
|
|
3207
|
-
}
|
|
3208
|
-
if (days.length === 0) days.push(String(new Date().getDay()));
|
|
3209
|
-
return minute + " " + hour + " * * " + days.join(",");
|
|
3210
|
-
}
|
|
3211
|
-
return null;
|
|
3212
|
-
}
|
|
3213
|
-
|
|
3214
|
-
function cronToHumanText(cron) {
|
|
3215
|
-
if (!cron) return "";
|
|
3216
|
-
var parts = cron.trim().split(/\s+/);
|
|
3217
|
-
if (parts.length !== 5) return cron;
|
|
3218
|
-
var m = parts[0], h = parts[1], dom = parts[2], dow = parts[4];
|
|
3219
|
-
var pad = function(n) { return (parseInt(n,10) < 10 ? "0" : "") + parseInt(n,10); };
|
|
3220
|
-
var t = pad(h) + ":" + pad(m);
|
|
3221
|
-
var dayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
|
|
3222
|
-
if (dow === "*" && dom === "*") return "Every day at " + t;
|
|
3223
|
-
if (dow === "1-5" && dom === "*") return "Weekdays at " + t;
|
|
3224
|
-
if (dom !== "*" && dow === "*") return "Monthly on day " + dom + " at " + t;
|
|
3225
|
-
if (dow !== "*" && dom === "*") {
|
|
3226
|
-
var ds = dow.split(",").map(function(d) { return dayNames[parseInt(d,10)] || d; });
|
|
3227
|
-
return "Every " + ds.join(", ") + " at " + t;
|
|
3228
|
-
}
|
|
3229
|
-
return cron;
|
|
3230
3129
|
}
|
|
3231
3130
|
|
|
3232
3131
|
function wizardNext() {
|
|
@@ -3278,6 +3177,8 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3278
3177
|
if (taskEl) { taskEl.focus(); taskEl.style.borderColor = "#e74c3c"; setTimeout(function() { taskEl.style.borderColor = ""; }, 2000); }
|
|
3279
3178
|
return;
|
|
3280
3179
|
}
|
|
3180
|
+
}
|
|
3181
|
+
if (wizardStep === 3) {
|
|
3281
3182
|
wizardSubmit();
|
|
3282
3183
|
return;
|
|
3283
3184
|
}
|
|
@@ -3294,7 +3195,7 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3294
3195
|
}
|
|
3295
3196
|
|
|
3296
3197
|
function wizardSkip() {
|
|
3297
|
-
if (wizardStep <
|
|
3198
|
+
if (wizardStep < 3) {
|
|
3298
3199
|
wizardStep++;
|
|
3299
3200
|
updateWizardStep();
|
|
3300
3201
|
}
|
|
@@ -3329,52 +3230,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3329
3230
|
});
|
|
3330
3231
|
}
|
|
3331
3232
|
|
|
3332
|
-
// --- Repeat picker handlers ---
|
|
3333
|
-
var repeatSelect = document.getElementById("ralph-repeat");
|
|
3334
|
-
var repeatTimeRow = document.getElementById("ralph-time-row");
|
|
3335
|
-
var repeatCustom = document.getElementById("ralph-custom-repeat");
|
|
3336
|
-
var repeatUnitSelect = document.getElementById("ralph-repeat-unit");
|
|
3337
|
-
var repeatDowRow = document.getElementById("ralph-custom-dow-row");
|
|
3338
|
-
var cronPreview = document.getElementById("ralph-cron-preview");
|
|
3339
|
-
|
|
3340
|
-
function updateRepeatUI() {
|
|
3341
|
-
if (!repeatSelect) return;
|
|
3342
|
-
var val = repeatSelect.value;
|
|
3343
|
-
var isScheduled = val !== "none";
|
|
3344
|
-
if (repeatTimeRow) repeatTimeRow.style.display = isScheduled ? "" : "none";
|
|
3345
|
-
if (repeatCustom) repeatCustom.style.display = val === "custom" ? "" : "none";
|
|
3346
|
-
if (cronPreview) cronPreview.style.display = isScheduled ? "" : "none";
|
|
3347
|
-
if (isScheduled) {
|
|
3348
|
-
var cron = buildWizardCron();
|
|
3349
|
-
var humanEl = document.getElementById("ralph-cron-human");
|
|
3350
|
-
var cronEl = document.getElementById("ralph-cron-expr");
|
|
3351
|
-
if (humanEl) humanEl.textContent = cronToHumanText(cron);
|
|
3352
|
-
if (cronEl) cronEl.textContent = cron || "";
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
if (repeatSelect) {
|
|
3357
|
-
repeatSelect.addEventListener("change", updateRepeatUI);
|
|
3358
|
-
}
|
|
3359
|
-
if (repeatUnitSelect) {
|
|
3360
|
-
repeatUnitSelect.addEventListener("change", function () {
|
|
3361
|
-
if (repeatDowRow) repeatDowRow.style.display = this.value === "week" ? "" : "none";
|
|
3362
|
-
updateRepeatUI();
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
|
-
|
|
3366
|
-
var timeInput = document.getElementById("ralph-time");
|
|
3367
|
-
if (timeInput) timeInput.addEventListener("change", updateRepeatUI);
|
|
3368
|
-
|
|
3369
|
-
// DOW buttons in custom repeat
|
|
3370
|
-
var customDowBtns = document.querySelectorAll("#ralph-custom-repeat .sched-dow-btn");
|
|
3371
|
-
for (var di = 0; di < customDowBtns.length; di++) {
|
|
3372
|
-
customDowBtns[di].addEventListener("click", function () {
|
|
3373
|
-
this.classList.toggle("active");
|
|
3374
|
-
updateRepeatUI();
|
|
3375
|
-
});
|
|
3376
|
-
}
|
|
3377
|
-
|
|
3378
3233
|
// --- Ralph Sticky (title-bar island) ---
|
|
3379
3234
|
function showRalphCraftingBar(show) {
|
|
3380
3235
|
var stickyEl = document.getElementById("ralph-sticky");
|
|
@@ -3429,7 +3284,7 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3429
3284
|
'<span class="ralph-sticky-label">Ralph</span>' +
|
|
3430
3285
|
'<span class="ralph-sticky-status" id="ralph-sticky-status">Ready</span>' +
|
|
3431
3286
|
'<button class="ralph-sticky-action ralph-sticky-preview" title="Preview files">' + iconHtml("eye") + '</button>' +
|
|
3432
|
-
'<button class="ralph-sticky-action ralph-sticky-start" title="
|
|
3287
|
+
'<button class="ralph-sticky-action ralph-sticky-start" title="Start loop">' + iconHtml("play") + '</button>' +
|
|
3433
3288
|
'<button class="ralph-sticky-action ralph-sticky-dismiss" title="Cancel and discard">' + iconHtml("x") + '</button>' +
|
|
3434
3289
|
'</div>' +
|
|
3435
3290
|
'</div>';
|
|
@@ -3566,15 +3421,6 @@ import { initScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, han
|
|
|
3566
3421
|
sendTerminalCommand: function (cmd) { sendTerminalCommand(cmd); },
|
|
3567
3422
|
});
|
|
3568
3423
|
|
|
3569
|
-
// --- Scheduler ---
|
|
3570
|
-
initScheduler({
|
|
3571
|
-
get ws() { return ws; },
|
|
3572
|
-
get connected() { return connected; },
|
|
3573
|
-
get activeSessionId() { return activeSessionId; },
|
|
3574
|
-
basePath: basePath,
|
|
3575
|
-
openRalphWizard: function () { openRalphWizard(); },
|
|
3576
|
-
});
|
|
3577
|
-
|
|
3578
3424
|
// --- Remove project ---
|
|
3579
3425
|
function confirmRemoveProject(slug, name) {
|
|
3580
3426
|
showConfirm("Remove project \"" + name + "\"?", function () {
|
package/lib/public/css/loop.css
CHANGED
|
@@ -38,68 +38,7 @@
|
|
|
38
38
|
.loop-stop-btn:hover {
|
|
39
39
|
opacity: 0.85;
|
|
40
40
|
}
|
|
41
|
-
/*
|
|
42
|
-
#ralph-loop-section {
|
|
43
|
-
border-left: 3px solid var(--accent);
|
|
44
|
-
background: rgba(var(--overlay-rgb), 0.03);
|
|
45
|
-
padding: 8px 12px;
|
|
46
|
-
margin: 0 8px;
|
|
47
|
-
border-radius: 6px;
|
|
48
|
-
border-bottom: 1px solid var(--border);
|
|
49
|
-
margin-bottom: 4px;
|
|
50
|
-
}
|
|
51
|
-
.ralph-section-inner {
|
|
52
|
-
display: flex;
|
|
53
|
-
flex-direction: column;
|
|
54
|
-
gap: 4px;
|
|
55
|
-
}
|
|
56
|
-
.ralph-section-header {
|
|
57
|
-
display: flex;
|
|
58
|
-
align-items: center;
|
|
59
|
-
gap: 6px;
|
|
60
|
-
font-size: 12px;
|
|
61
|
-
font-weight: 600;
|
|
62
|
-
color: var(--text);
|
|
63
|
-
}
|
|
64
|
-
.ralph-section-header .ralph-section-icon { display: inline-flex; color: var(--accent); }
|
|
65
|
-
.ralph-section-header .ralph-section-icon .lucide { width: 13px; height: 13px; }
|
|
66
|
-
.ralph-section-header .ralph-section-label { flex: 1; }
|
|
67
|
-
.ralph-section-header .loop-experimental { margin-left: 0; font-size: 9.5px; }
|
|
68
|
-
.ralph-section-body {
|
|
69
|
-
font-size: 11.5px;
|
|
70
|
-
color: var(--text-muted);
|
|
71
|
-
padding-left: 2px;
|
|
72
|
-
margin-top: 2px;
|
|
73
|
-
}
|
|
74
|
-
.ralph-section-hint {
|
|
75
|
-
color: var(--text-muted);
|
|
76
|
-
}
|
|
77
|
-
.ralph-section-status {
|
|
78
|
-
display: inline-flex;
|
|
79
|
-
align-items: center;
|
|
80
|
-
gap: 4px;
|
|
81
|
-
}
|
|
82
|
-
.ralph-section-status .lucide { width: 11px; height: 11px; }
|
|
83
|
-
.ralph-section-status.crafting { color: var(--accent); }
|
|
84
|
-
.ralph-section-status.ready { color: var(--success, #27ae60); font-weight: 600; }
|
|
85
|
-
.ralph-section-status.running { color: var(--accent); }
|
|
86
|
-
.ralph-section-status.done { color: var(--success, #27ae60); }
|
|
87
|
-
.ralph-section-tasks-link {
|
|
88
|
-
display: block;
|
|
89
|
-
margin-top: 2px;
|
|
90
|
-
font-size: 11px;
|
|
91
|
-
color: var(--accent);
|
|
92
|
-
text-decoration: none;
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
}
|
|
95
|
-
.ralph-section-tasks-link:hover { text-decoration: underline; }
|
|
96
|
-
|
|
97
|
-
/* Section accent states */
|
|
98
|
-
.ralph-section-ready { border-left-color: var(--success, #27ae60); }
|
|
99
|
-
.ralph-section-running { border-left-color: var(--accent); }
|
|
100
|
-
.ralph-section-done { border-left-color: var(--success, #27ae60); }
|
|
101
|
-
|
|
102
|
-
/* .loop-start-btn is now inside #ralph-loop-section, inherits sidebar button styles */
|
|
41
|
+
/* .loop-start-btn is now inside #session-actions, inherits sidebar button styles */
|
|
103
42
|
.loop-experimental {
|
|
104
43
|
display: inline-flex;
|
|
105
44
|
align-items: center;
|
package/lib/public/index.html
CHANGED
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
<button id="terminal-sidebar-btn"><i data-lucide="square-terminal"></i> <span>Terminal</span><span id="terminal-sidebar-count" class="sidebar-badge hidden"></span></button>
|
|
101
101
|
<button id="sticky-notes-sidebar-btn"><i data-lucide="sticky-note"></i> <span>Sticky Notes</span><span id="sticky-notes-sidebar-count" class="sidebar-badge hidden"></span></button>
|
|
102
102
|
<button id="skills-btn"><i data-lucide="puzzle"></i> <span>Skills</span></button>
|
|
103
|
-
<button id="scheduler-btn"><i data-lucide="calendar-clock"></i> <span>Scheduled Tasks</span></button>
|
|
104
103
|
</div>
|
|
105
104
|
</div>
|
|
106
105
|
<div id="sidebar-sessions-header">
|
|
@@ -128,7 +127,6 @@
|
|
|
128
127
|
</div>
|
|
129
128
|
</div>
|
|
130
129
|
</div>
|
|
131
|
-
<div id="ralph-loop-section"></div>
|
|
132
130
|
<div id="sidebar-panel-projects" class="sidebar-panel hidden">
|
|
133
131
|
<div id="project-list"></div>
|
|
134
132
|
</div>
|
|
@@ -993,6 +991,7 @@
|
|
|
993
991
|
<div class="ralph-wizard-progress">
|
|
994
992
|
<span class="ralph-dot active" data-step="1"></span>
|
|
995
993
|
<span class="ralph-dot" data-step="2"></span>
|
|
994
|
+
<span class="ralph-dot" data-step="3"></span>
|
|
996
995
|
</div>
|
|
997
996
|
<div class="ralph-wizard-steps">
|
|
998
997
|
<div class="ralph-step active" data-step="1">
|
|
@@ -1028,7 +1027,15 @@
|
|
|
1028
1027
|
<label class="ralph-field-label" for="ralph-task">Task description <span class="ralph-field-req">*</span></label>
|
|
1029
1028
|
<textarea id="ralph-task" rows="5" placeholder="e.g. Add dark mode toggle to the settings page with system preference detection..."></textarea>
|
|
1030
1029
|
</div>
|
|
1031
|
-
<
|
|
1030
|
+
<div class="ralph-step" data-step="3">
|
|
1031
|
+
<h3>Settings & Review</h3>
|
|
1032
|
+
<div class="ralph-settings-row">
|
|
1033
|
+
<label for="ralph-max-iterations">Max iterations</label>
|
|
1034
|
+
<input type="number" id="ralph-max-iterations" min="1" max="100" value="25" class="ralph-input-number">
|
|
1035
|
+
<span class="ralph-settings-hint">How many code → judge cycles before stopping.</span>
|
|
1036
|
+
</div>
|
|
1037
|
+
<div id="ralph-review-summary"></div>
|
|
1038
|
+
</div>
|
|
1032
1039
|
</div>
|
|
1033
1040
|
<div class="ralph-wizard-footer">
|
|
1034
1041
|
<button id="ralph-wizard-back" class="ralph-btn secondary">Back</button>
|
|
@@ -1053,86 +1060,6 @@
|
|
|
1053
1060
|
</div>
|
|
1054
1061
|
</div>
|
|
1055
1062
|
|
|
1056
|
-
<!-- Scheduler panel is dynamically created in #app by scheduler.js -->
|
|
1057
|
-
|
|
1058
|
-
<!-- Schedule Edit Modal -->
|
|
1059
|
-
<div id="schedule-edit-modal" class="hidden">
|
|
1060
|
-
<div class="confirm-backdrop"></div>
|
|
1061
|
-
<div class="confirm-dialog schedule-edit-dialog">
|
|
1062
|
-
<div class="schedule-edit-header">
|
|
1063
|
-
<span class="schedule-edit-title" id="schedule-edit-title">New Schedule</span>
|
|
1064
|
-
<button class="schedule-edit-close" id="schedule-edit-close">×</button>
|
|
1065
|
-
</div>
|
|
1066
|
-
<div class="schedule-edit-body">
|
|
1067
|
-
<div class="sched-field">
|
|
1068
|
-
<label class="sched-field-label" for="sched-name">Name</label>
|
|
1069
|
-
<input type="text" class="sched-field-input" id="sched-name" placeholder="e.g. Daily Code Review" maxlength="60" spellcheck="false" autocomplete="off">
|
|
1070
|
-
</div>
|
|
1071
|
-
<div class="sched-field" id="sched-job-field">
|
|
1072
|
-
<label class="sched-field-label">Job</label>
|
|
1073
|
-
<div class="sched-field-hint" id="sched-job-name" style="font-weight:600;color:var(--text)"></div>
|
|
1074
|
-
</div>
|
|
1075
|
-
<div class="sched-field">
|
|
1076
|
-
<label class="sched-field-label">Repeat</label>
|
|
1077
|
-
<div class="sched-preset-group" id="sched-presets">
|
|
1078
|
-
<button class="sched-preset-btn" data-preset="daily">Every day</button>
|
|
1079
|
-
<button class="sched-preset-btn" data-preset="weekdays">Weekdays</button>
|
|
1080
|
-
<button class="sched-preset-btn" data-preset="weekly">Weekly</button>
|
|
1081
|
-
<button class="sched-preset-btn" data-preset="monthly">Monthly</button>
|
|
1082
|
-
<button class="sched-preset-btn" data-preset="custom">Custom</button>
|
|
1083
|
-
</div>
|
|
1084
|
-
</div>
|
|
1085
|
-
<div class="sched-field" id="sched-dow-field" style="display:none">
|
|
1086
|
-
<label class="sched-field-label">Days of week</label>
|
|
1087
|
-
<div class="sched-dow-row" id="sched-dow-row">
|
|
1088
|
-
<button class="sched-dow-btn" data-dow="0">Su</button>
|
|
1089
|
-
<button class="sched-dow-btn" data-dow="1">Mo</button>
|
|
1090
|
-
<button class="sched-dow-btn" data-dow="2">Tu</button>
|
|
1091
|
-
<button class="sched-dow-btn" data-dow="3">We</button>
|
|
1092
|
-
<button class="sched-dow-btn" data-dow="4">Th</button>
|
|
1093
|
-
<button class="sched-dow-btn" data-dow="5">Fr</button>
|
|
1094
|
-
<button class="sched-dow-btn" data-dow="6">Sa</button>
|
|
1095
|
-
</div>
|
|
1096
|
-
</div>
|
|
1097
|
-
<div class="sched-field">
|
|
1098
|
-
<label class="sched-field-label">Time</label>
|
|
1099
|
-
<div class="sched-time-row">
|
|
1100
|
-
<input type="time" class="sched-time-input" id="sched-time" value="09:00">
|
|
1101
|
-
</div>
|
|
1102
|
-
</div>
|
|
1103
|
-
<div class="sched-field">
|
|
1104
|
-
<label class="sched-field-label">Preview</label>
|
|
1105
|
-
<div class="sched-cron-preview" id="sched-cron-preview">
|
|
1106
|
-
<span class="human" id="sched-human-text">Every day at 09:00</span>
|
|
1107
|
-
<span id="sched-cron-text">0 9 * * *</span>
|
|
1108
|
-
</div>
|
|
1109
|
-
</div>
|
|
1110
|
-
<div class="sched-field">
|
|
1111
|
-
<div class="sched-toggle-row">
|
|
1112
|
-
<span class="sched-field-label" style="margin:0">Enabled</span>
|
|
1113
|
-
<label class="sched-toggle">
|
|
1114
|
-
<input type="checkbox" id="sched-enabled" checked>
|
|
1115
|
-
<span class="sched-toggle-track"></span>
|
|
1116
|
-
<span class="sched-toggle-thumb"></span>
|
|
1117
|
-
</label>
|
|
1118
|
-
</div>
|
|
1119
|
-
</div>
|
|
1120
|
-
<div class="sched-field" id="sched-history-field" style="display:none">
|
|
1121
|
-
<label class="sched-field-label">Run History</label>
|
|
1122
|
-
<div class="sched-history" id="sched-history"></div>
|
|
1123
|
-
</div>
|
|
1124
|
-
</div>
|
|
1125
|
-
<div class="schedule-edit-footer">
|
|
1126
|
-
<button class="sched-btn sched-btn-delete" id="sched-delete" style="display:none">Delete</button>
|
|
1127
|
-
<button class="sched-btn sched-btn-cancel" id="sched-cancel">Cancel</button>
|
|
1128
|
-
<button class="sched-btn sched-btn-save" id="sched-save">Save</button>
|
|
1129
|
-
</div>
|
|
1130
|
-
</div>
|
|
1131
|
-
</div>
|
|
1132
|
-
|
|
1133
|
-
<!-- Schedule Popover -->
|
|
1134
|
-
<div id="schedule-popover" class="schedule-popover hidden"></div>
|
|
1135
|
-
|
|
1136
1063
|
<script src="https://cdn.jsdelivr.net/npm/marked@14/marked.min.js"></script>
|
|
1137
1064
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
|
|
1138
1065
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>
|
|
@@ -26,18 +26,8 @@ export function renderMarkdown(text) {
|
|
|
26
26
|
return DOMPurify.sanitize(marked.parse(text));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
var langAliases = { jsonl: "json", dotenv: "bash" };
|
|
30
|
-
|
|
31
29
|
export function highlightCodeBlocks(el) {
|
|
32
30
|
el.querySelectorAll("pre code:not(.hljs):not(.language-mermaid)").forEach(function (block) {
|
|
33
|
-
var cls = Array.from(block.classList).find(function (c) { return c.startsWith("language-"); });
|
|
34
|
-
if (cls) {
|
|
35
|
-
var lang = cls.replace("language-", "");
|
|
36
|
-
if (langAliases[lang]) {
|
|
37
|
-
block.classList.remove(cls);
|
|
38
|
-
block.classList.add("language-" + langAliases[lang]);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
31
|
hljs.highlightElement(block);
|
|
42
32
|
});
|
|
43
33
|
el.querySelectorAll("pre:not(.has-copy-btn):not([data-mermaid-processed])").forEach(function (pre) {
|
package/lib/public/style.css
CHANGED
package/lib/sessions.js
CHANGED
|
@@ -171,7 +171,7 @@ function createSessionManager(opts) {
|
|
|
171
171
|
function broadcastSessionList() {
|
|
172
172
|
send({
|
|
173
173
|
type: "session_list",
|
|
174
|
-
sessions: [...sessions.values()].
|
|
174
|
+
sessions: [...sessions.values()].map(function(s) {
|
|
175
175
|
return {
|
|
176
176
|
id: s.localId,
|
|
177
177
|
cliSessionId: s.cliSessionId || null,
|