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/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, cron: null };
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 / scheduler panel when switching to other sidebar panels
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(); if (isSchedulerOpen()) closeScheduler(); });
2905
- if (terminalSidebarBtn) terminalSidebarBtn.addEventListener("click", function () { if (isArchiveOpen()) closeArchive(); if (isSchedulerOpen()) closeScheduler(); });
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 section = document.getElementById("ralph-loop-section");
2920
- if (!section) return;
2921
-
2922
- var busy = loopActive || ralphPhase === "executing";
2923
- var phase = busy ? "executing" : ralphPhase;
2924
-
2925
- var statusHtml = "";
2926
- var statusClass = "";
2927
- var clickAction = "wizard"; // default
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 if (clickAction === "wizard") {
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
- // "View in Scheduled Tasks" link
2978
- var tasksLink = section.querySelector(".ralph-section-tasks-link");
2979
- if (tasksLink) {
2980
- tasksLink.addEventListener("click", function(e) {
2981
- e.preventDefault();
2982
- e.stopPropagation();
2983
- openSchedulerToTab("library");
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 === 2 ? "Launch" : "Get Started";
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 < 2) {
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="' + (wizardData.cron ? 'Schedule' : 'Start loop') + '">' + iconHtml(wizardData.cron ? "calendar-clock" : "play") + '</button>' +
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 () {
@@ -38,68 +38,7 @@
38
38
  .loop-stop-btn:hover {
39
39
  opacity: 0.85;
40
40
  }
41
- /* Ralph Loop sidebar section */
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;
@@ -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
- <input type="hidden" id="ralph-max-iterations" value="25">
1030
+ <div class="ralph-step" data-step="3">
1031
+ <h3>Settings &amp; 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">&times;</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) {
@@ -15,5 +15,3 @@
15
15
  @import url("css/skills.css");
16
16
  @import url("css/mobile-nav.css");
17
17
  @import url("css/loop.css");
18
- @import url("css/scheduler.css");
19
- @import url("css/scheduler-modal.css");
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()].filter(function(s) { return !s.hidden; }).map(function(s) {
174
+ sessions: [...sessions.values()].map(function(s) {
175
175
  return {
176
176
  id: s.localId,
177
177
  cliSessionId: s.cliSessionId || null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",