clay-server 2.13.0-beta.2 → 2.13.0-beta.4
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 +48 -0
- package/lib/daemon.js +16 -3
- package/lib/notes.js +1 -1
- package/lib/project.js +275 -7
- package/lib/public/app.js +250 -34
- package/lib/public/css/admin.css +71 -0
- package/lib/public/css/command-palette.css +319 -0
- package/lib/public/css/icon-strip.css +19 -3
- package/lib/public/css/input.css +2 -1
- package/lib/public/css/loop.css +26 -0
- package/lib/public/css/mates.css +73 -0
- package/lib/public/css/overlays.css +3 -0
- package/lib/public/css/scheduler.css +29 -5
- package/lib/public/css/sidebar.css +28 -6
- package/lib/public/css/sticky-notes.css +61 -12
- package/lib/public/css/title-bar.css +24 -30
- package/lib/public/index.html +48 -13
- package/lib/public/modules/admin.js +109 -1
- package/lib/public/modules/command-palette.js +549 -0
- package/lib/public/modules/input.js +10 -2
- package/lib/public/modules/mate-wizard.js +17 -6
- package/lib/public/modules/scheduler.js +56 -64
- package/lib/public/modules/session-search.js +6 -2
- package/lib/public/modules/sidebar.js +128 -72
- package/lib/public/modules/sticky-notes.js +37 -0
- package/lib/public/style.css +1 -0
- package/lib/scheduler.js +4 -0
- package/lib/sdk-bridge.js +10 -8
- package/lib/server.js +334 -7
- package/lib/sessions.js +5 -0
- package/lib/users.js +64 -0
- package/lib/worktree.js +2 -2
- package/package.json +1 -1
|
@@ -172,19 +172,12 @@ function ensurePanel() {
|
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
//
|
|
175
|
+
// Add task button (opens wizard modal)
|
|
176
176
|
var addRow = document.createElement("div");
|
|
177
177
|
addRow.className = "scheduler-add-row";
|
|
178
178
|
addRow.innerHTML =
|
|
179
179
|
'<div class="scheduler-add-trigger" id="scheduler-add-trigger">' +
|
|
180
180
|
'<i data-lucide="plus-circle"></i> <span>Add new task</span>' +
|
|
181
|
-
'</div>' +
|
|
182
|
-
'<div class="scheduler-add-form hidden" id="scheduler-add-form">' +
|
|
183
|
-
'<textarea id="scheduler-add-input" rows="2" placeholder="Describe what to build..."></textarea>' +
|
|
184
|
-
'<div class="scheduler-add-actions">' +
|
|
185
|
-
'<button type="button" class="scheduler-add-submit" id="scheduler-add-submit">Add</button>' +
|
|
186
|
-
'<button type="button" class="scheduler-add-cancel" id="scheduler-add-cancel">Cancel</button>' +
|
|
187
|
-
'</div>' +
|
|
188
181
|
'</div>';
|
|
189
182
|
sidebar.appendChild(addRow);
|
|
190
183
|
|
|
@@ -255,59 +248,12 @@ function ensurePanel() {
|
|
|
255
248
|
closeScheduler();
|
|
256
249
|
});
|
|
257
250
|
|
|
258
|
-
//
|
|
251
|
+
// Add task button — opens the Ralph wizard in "task" mode (step 1 skipped)
|
|
259
252
|
var addTrigger = addRow.querySelector("#scheduler-add-trigger");
|
|
260
|
-
var addForm = addRow.querySelector("#scheduler-add-form");
|
|
261
|
-
var addInput = addRow.querySelector("#scheduler-add-input");
|
|
262
|
-
var addSubmitBtn = addRow.querySelector("#scheduler-add-submit");
|
|
263
|
-
var addCancelBtn = addRow.querySelector("#scheduler-add-cancel");
|
|
264
|
-
|
|
265
253
|
addTrigger.addEventListener("click", function () {
|
|
266
|
-
|
|
267
|
-
addForm.classList.remove("hidden");
|
|
268
|
-
addInput.value = "";
|
|
269
|
-
addInput.focus();
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
addCancelBtn.addEventListener("click", function () {
|
|
273
|
-
addForm.classList.add("hidden");
|
|
274
|
-
addTrigger.classList.remove("hidden");
|
|
254
|
+
ctx.openRalphWizard("task");
|
|
275
255
|
});
|
|
276
256
|
|
|
277
|
-
addInput.addEventListener("keydown", function (e) {
|
|
278
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
279
|
-
e.preventDefault();
|
|
280
|
-
submitInlineTask();
|
|
281
|
-
}
|
|
282
|
-
if (e.key === "Escape") {
|
|
283
|
-
addForm.classList.add("hidden");
|
|
284
|
-
addTrigger.classList.remove("hidden");
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
addSubmitBtn.addEventListener("click", function () {
|
|
289
|
-
submitInlineTask();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
var addSubmitting = false;
|
|
293
|
-
function submitInlineTask() {
|
|
294
|
-
if (addSubmitting) return;
|
|
295
|
-
var task = addInput.value.trim();
|
|
296
|
-
if (!task) { addInput.focus(); return; }
|
|
297
|
-
ctx.requireClayRalph(function () {
|
|
298
|
-
addSubmitting = true;
|
|
299
|
-
addInput.value = "";
|
|
300
|
-
addForm.classList.add("hidden");
|
|
301
|
-
addTrigger.classList.remove("hidden");
|
|
302
|
-
// Send wizard complete directly (skip modal)
|
|
303
|
-
send({
|
|
304
|
-
type: "ralph_wizard_complete",
|
|
305
|
-
data: { name: task, task: task, maxIterations: 3, cron: null, source: "task" }
|
|
306
|
-
});
|
|
307
|
-
setTimeout(function () { addSubmitting = false; }, 1000);
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
257
|
// Calendar controls
|
|
312
258
|
calHdr.querySelector("#scheduler-prev").addEventListener("click", function () { navigate(-1); });
|
|
313
259
|
calHdr.querySelector("#scheduler-next").addEventListener("click", function () { navigate(1); });
|
|
@@ -428,7 +374,16 @@ export function closeScheduler() {
|
|
|
428
374
|
if (!panelOpen) return;
|
|
429
375
|
panelOpen = false;
|
|
430
376
|
stopNowLineTimer();
|
|
431
|
-
if (currentMode === "crafting")
|
|
377
|
+
if (currentMode === "crafting") {
|
|
378
|
+
unparentChat();
|
|
379
|
+
// Switch back to previous session so crafting chat does not linger
|
|
380
|
+
if (craftingSessionId && logPreviousSessionId) {
|
|
381
|
+
send({ type: "switch_session", id: logPreviousSessionId });
|
|
382
|
+
logPreviousSessionId = null;
|
|
383
|
+
}
|
|
384
|
+
craftingTaskId = null;
|
|
385
|
+
craftingSessionId = null;
|
|
386
|
+
}
|
|
432
387
|
|
|
433
388
|
if (panel) panel.classList.add("hidden");
|
|
434
389
|
if (popoverEl) popoverEl.classList.add("hidden");
|
|
@@ -1572,6 +1527,10 @@ export function isSchedulerOpen() {
|
|
|
1572
1527
|
export function enterCraftingMode(sessionId, taskId) {
|
|
1573
1528
|
craftingSessionId = sessionId || null;
|
|
1574
1529
|
craftingTaskId = taskId || null;
|
|
1530
|
+
// Remember the current session so we can restore it when crafting ends
|
|
1531
|
+
if (!logPreviousSessionId && ctx && ctx.activeSessionId && ctx.activeSessionId !== sessionId) {
|
|
1532
|
+
logPreviousSessionId = ctx.activeSessionId;
|
|
1533
|
+
}
|
|
1575
1534
|
if (!panelOpen) openScheduler();
|
|
1576
1535
|
if (taskId) {
|
|
1577
1536
|
selectedTaskId = taskId;
|
|
@@ -1991,6 +1950,20 @@ function setupCreateModal() {
|
|
|
1991
1950
|
updateRecurrenceBtn();
|
|
1992
1951
|
});
|
|
1993
1952
|
|
|
1953
|
+
// Run mode toggle (single vs multi-round)
|
|
1954
|
+
var runModeContainer = createPopover.querySelector(".sched-create-run-mode");
|
|
1955
|
+
if (runModeContainer) {
|
|
1956
|
+
runModeContainer.addEventListener("click", function (e) {
|
|
1957
|
+
var btn = e.target.closest(".sched-run-mode-btn");
|
|
1958
|
+
if (!btn) return;
|
|
1959
|
+
var mode = btn.dataset.mode;
|
|
1960
|
+
var btns = runModeContainer.querySelectorAll(".sched-run-mode-btn");
|
|
1961
|
+
for (var i = 0; i < btns.length; i++) btns[i].classList.toggle("active", btns[i] === btn);
|
|
1962
|
+
var iterGroup = document.getElementById("sched-create-iter-group");
|
|
1963
|
+
if (iterGroup) iterGroup.classList.toggle("hidden", mode !== "multi");
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1994
1967
|
// Submit
|
|
1995
1968
|
document.getElementById("sched-create-submit").addEventListener("click", function () { submitCreateSchedule(); });
|
|
1996
1969
|
|
|
@@ -2238,8 +2211,15 @@ function openCreateModalWithRecord(rec, anchorEl) {
|
|
|
2238
2211
|
}
|
|
2239
2212
|
}
|
|
2240
2213
|
|
|
2241
|
-
// Set iterations
|
|
2242
|
-
|
|
2214
|
+
// Set run mode and iterations
|
|
2215
|
+
var editRunMode = (rec.maxIterations && rec.maxIterations > 1) ? "multi" : "single";
|
|
2216
|
+
var editRunBtns = createPopover.querySelectorAll(".sched-run-mode-btn");
|
|
2217
|
+
for (var rb = 0; rb < editRunBtns.length; rb++) {
|
|
2218
|
+
editRunBtns[rb].classList.toggle("active", editRunBtns[rb].dataset.mode === editRunMode);
|
|
2219
|
+
}
|
|
2220
|
+
var editIterGroup = document.getElementById("sched-create-iter-group");
|
|
2221
|
+
if (editIterGroup) editIterGroup.classList.toggle("hidden", editRunMode !== "multi");
|
|
2222
|
+
if (rec.maxIterations && rec.maxIterations > 1) {
|
|
2243
2223
|
var iterInput = document.getElementById("sched-create-iterations");
|
|
2244
2224
|
if (iterInput) iterInput.value = rec.maxIterations;
|
|
2245
2225
|
}
|
|
@@ -2293,6 +2273,13 @@ function openCreateModal(date, hour, anchorEl) {
|
|
|
2293
2273
|
document.getElementById("sched-create-desc").value = "";
|
|
2294
2274
|
var iterReset = document.getElementById("sched-create-iterations");
|
|
2295
2275
|
if (iterReset) iterReset.value = "3";
|
|
2276
|
+
// Reset run mode to single
|
|
2277
|
+
var runModeBtns = createPopover.querySelectorAll(".sched-run-mode-btn");
|
|
2278
|
+
for (var rm = 0; rm < runModeBtns.length; rm++) {
|
|
2279
|
+
runModeBtns[rm].classList.toggle("active", runModeBtns[rm].dataset.mode === "single");
|
|
2280
|
+
}
|
|
2281
|
+
var iterGroup = document.getElementById("sched-create-iter-group");
|
|
2282
|
+
if (iterGroup) iterGroup.classList.add("hidden");
|
|
2296
2283
|
|
|
2297
2284
|
// Reset color
|
|
2298
2285
|
var colorDot = document.getElementById("sched-create-color-dot");
|
|
@@ -2728,10 +2715,15 @@ function submitCreateSchedule() {
|
|
|
2728
2715
|
}
|
|
2729
2716
|
}
|
|
2730
2717
|
|
|
2731
|
-
var
|
|
2732
|
-
var
|
|
2733
|
-
|
|
2734
|
-
if (
|
|
2718
|
+
var activeRunMode = createPopover.querySelector(".sched-run-mode-btn.active");
|
|
2719
|
+
var runMode = activeRunMode ? activeRunMode.dataset.mode : "single";
|
|
2720
|
+
var maxIterations = 1;
|
|
2721
|
+
if (runMode === "multi") {
|
|
2722
|
+
var iterInput = document.getElementById("sched-create-iterations");
|
|
2723
|
+
maxIterations = iterInput ? (parseInt(iterInput.value, 10) || 3) : 3;
|
|
2724
|
+
if (maxIterations < 2) maxIterations = 2;
|
|
2725
|
+
if (maxIterations > 100) maxIterations = 100;
|
|
2726
|
+
}
|
|
2735
2727
|
|
|
2736
2728
|
send({
|
|
2737
2729
|
type: "schedule_create",
|
|
@@ -93,14 +93,18 @@ function isSearchOpen() {
|
|
|
93
93
|
return searchBarEl && !searchBarEl.classList.contains("hidden");
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
function openSearch() {
|
|
96
|
+
function openSearch(prefill) {
|
|
97
97
|
if (!searchBarEl) return;
|
|
98
98
|
searchBarEl.classList.remove("hidden");
|
|
99
99
|
var btn = document.getElementById("find-in-session-btn");
|
|
100
100
|
if (btn) btn.classList.add("active");
|
|
101
|
+
if (typeof prefill === "string" && prefill) {
|
|
102
|
+
searchInputEl.value = prefill;
|
|
103
|
+
performSearch(prefill);
|
|
104
|
+
}
|
|
101
105
|
searchInputEl.focus();
|
|
102
106
|
searchInputEl.select();
|
|
103
|
-
if (searchInputEl.value) {
|
|
107
|
+
if (!prefill && searchInputEl.value) {
|
|
104
108
|
performSearch(searchInputEl.value);
|
|
105
109
|
}
|
|
106
110
|
}
|
|
@@ -4,6 +4,8 @@ import { openProjectSettings } from './project-settings.js';
|
|
|
4
4
|
import { triggerShare } from './qrcode.js';
|
|
5
5
|
import { parseEmojis } from './markdown.js';
|
|
6
6
|
import { showMateProfilePopover } from './profile.js';
|
|
7
|
+
import { closeArchive } from './sticky-notes.js';
|
|
8
|
+
import { closeScheduler } from './scheduler.js';
|
|
7
9
|
|
|
8
10
|
var ctx;
|
|
9
11
|
|
|
@@ -18,6 +20,11 @@ var expandedLoopGroups = new Set();
|
|
|
18
20
|
var cachedProjectList = [];
|
|
19
21
|
var cachedCurrentSlug = null;
|
|
20
22
|
|
|
23
|
+
function dismissOverlayPanels() {
|
|
24
|
+
closeArchive();
|
|
25
|
+
closeScheduler();
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
// --- Session presence (multi-user: who is viewing which session) ---
|
|
22
29
|
var sessionPresence = {}; // { sessionId: [{ id, displayName, avatarStyle, avatarSeed }] }
|
|
23
30
|
|
|
@@ -72,20 +79,22 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
|
72
79
|
menu.appendChild(visItem);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
ws
|
|
85
|
-
|
|
82
|
+
if (!ctx.permissions || ctx.permissions.sessionDelete !== false) {
|
|
83
|
+
var deleteItem = document.createElement("button");
|
|
84
|
+
deleteItem.className = "session-ctx-item session-ctx-delete";
|
|
85
|
+
deleteItem.innerHTML = iconHtml("trash-2") + " <span>Delete</span>";
|
|
86
|
+
deleteItem.addEventListener("click", function (e) {
|
|
87
|
+
e.stopPropagation();
|
|
88
|
+
closeSessionCtxMenu();
|
|
89
|
+
ctx.showConfirm('Delete "' + (title || "New Session") + '"? This session and its history will be permanently removed.', function () {
|
|
90
|
+
var ws = ctx.ws;
|
|
91
|
+
if (ws && ctx.connected) {
|
|
92
|
+
ws.send(JSON.stringify({ type: "delete_session", id: sessionId }));
|
|
93
|
+
}
|
|
94
|
+
});
|
|
86
95
|
});
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
menu.appendChild(deleteItem);
|
|
97
|
+
}
|
|
89
98
|
|
|
90
99
|
document.body.appendChild(menu);
|
|
91
100
|
sessionCtxMenu = menu;
|
|
@@ -159,22 +168,24 @@ function showLoopCtxMenu(anchorBtn, loopId, loopName, childCount) {
|
|
|
159
168
|
});
|
|
160
169
|
menu.appendChild(renameItem);
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
ctx.ws
|
|
174
|
-
|
|
171
|
+
if (!ctx.permissions || ctx.permissions.sessionDelete !== false) {
|
|
172
|
+
var deleteItem = document.createElement("button");
|
|
173
|
+
deleteItem.className = "session-ctx-item session-ctx-delete";
|
|
174
|
+
deleteItem.innerHTML = iconHtml("trash-2") + " <span>Delete</span>";
|
|
175
|
+
deleteItem.addEventListener("click", function (e) {
|
|
176
|
+
e.stopPropagation();
|
|
177
|
+
closeSessionCtxMenu();
|
|
178
|
+
var msg = 'Delete "' + (loopName || "Ralph Loop") + '"';
|
|
179
|
+
if (childCount > 1) msg += " and its " + childCount + " sessions";
|
|
180
|
+
msg += "? This cannot be undone.";
|
|
181
|
+
ctx.showConfirm(msg, function () {
|
|
182
|
+
if (ctx.ws && ctx.connected) {
|
|
183
|
+
ctx.ws.send(JSON.stringify({ type: "delete_loop_group", loopId: loopId }));
|
|
184
|
+
}
|
|
185
|
+
});
|
|
175
186
|
});
|
|
176
|
-
|
|
177
|
-
|
|
187
|
+
menu.appendChild(deleteItem);
|
|
188
|
+
}
|
|
178
189
|
|
|
179
190
|
document.body.appendChild(menu);
|
|
180
191
|
sessionCtxMenu = menu;
|
|
@@ -282,6 +293,7 @@ function renderLoopChild(s) {
|
|
|
282
293
|
return function () {
|
|
283
294
|
if (ctx.ws && ctx.connected) {
|
|
284
295
|
ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
296
|
+
dismissOverlayPanels();
|
|
285
297
|
closeSidebar();
|
|
286
298
|
}
|
|
287
299
|
};
|
|
@@ -384,6 +396,7 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
384
396
|
return function () {
|
|
385
397
|
if (ctx.ws && ctx.connected) {
|
|
386
398
|
ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
399
|
+
dismissOverlayPanels();
|
|
387
400
|
closeSidebar();
|
|
388
401
|
}
|
|
389
402
|
};
|
|
@@ -450,6 +463,7 @@ function renderSessionItem(s) {
|
|
|
450
463
|
return function () {
|
|
451
464
|
if (ctx.ws && ctx.connected) {
|
|
452
465
|
ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
466
|
+
dismissOverlayPanels();
|
|
453
467
|
closeSidebar();
|
|
454
468
|
}
|
|
455
469
|
};
|
|
@@ -784,6 +798,7 @@ function renderSheetSessions(listEl) {
|
|
|
784
798
|
if (ctx.ws && ctx.connected) {
|
|
785
799
|
ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
786
800
|
}
|
|
801
|
+
dismissOverlayPanels();
|
|
787
802
|
closeMobileSheet();
|
|
788
803
|
});
|
|
789
804
|
})(s.id);
|
|
@@ -1110,6 +1125,23 @@ export function initSidebar(_ctx) {
|
|
|
1110
1125
|
// Initial sync even if no resize handle
|
|
1111
1126
|
syncUserIslandWidth();
|
|
1112
1127
|
|
|
1128
|
+
// --- User island tooltip on hover (collapsed sidebar) ---
|
|
1129
|
+
if (userIsland) {
|
|
1130
|
+
var profileArea = userIsland.querySelector(".user-island-profile");
|
|
1131
|
+
if (profileArea) {
|
|
1132
|
+
profileArea.addEventListener("mouseenter", function () {
|
|
1133
|
+
var layout = document.getElementById("layout");
|
|
1134
|
+
if (!layout || !layout.classList.contains("sidebar-collapsed")) return;
|
|
1135
|
+
var nameEl = userIsland.querySelector(".user-island-name");
|
|
1136
|
+
var text = nameEl ? nameEl.textContent : "";
|
|
1137
|
+
if (text) showIconTooltip(profileArea, text);
|
|
1138
|
+
});
|
|
1139
|
+
profileArea.addEventListener("mouseleave", function () {
|
|
1140
|
+
hideIconTooltip();
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1113
1145
|
// --- Schedule countdown timer ---
|
|
1114
1146
|
startCountdownTimer();
|
|
1115
1147
|
}
|
|
@@ -1842,23 +1874,25 @@ function showIconCtxMenu(anchorEl, slug, name) {
|
|
|
1842
1874
|
});
|
|
1843
1875
|
menu.appendChild(wtItem);
|
|
1844
1876
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
ctx.ws
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1877
|
+
if (!ctx.permissions || ctx.permissions.deleteProject !== false) {
|
|
1878
|
+
// --- Separator ---
|
|
1879
|
+
var sep = document.createElement("div");
|
|
1880
|
+
sep.className = "project-ctx-separator";
|
|
1881
|
+
menu.appendChild(sep);
|
|
1882
|
+
|
|
1883
|
+
// --- Remove Project ---
|
|
1884
|
+
var removeItem = document.createElement("button");
|
|
1885
|
+
removeItem.className = "project-ctx-item project-ctx-delete";
|
|
1886
|
+
removeItem.innerHTML = iconHtml("trash-2") + " <span>Remove Project</span>";
|
|
1887
|
+
removeItem.addEventListener("click", function (e) {
|
|
1888
|
+
e.stopPropagation();
|
|
1889
|
+
closeProjectCtxMenu();
|
|
1890
|
+
if (ctx.ws && ctx.connected) {
|
|
1891
|
+
ctx.ws.send(JSON.stringify({ type: "remove_project_check", slug: slug, name: name || slug }));
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
menu.appendChild(removeItem);
|
|
1895
|
+
}
|
|
1862
1896
|
}
|
|
1863
1897
|
|
|
1864
1898
|
document.body.appendChild(menu);
|
|
@@ -1889,15 +1923,17 @@ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
|
|
|
1889
1923
|
menu.className = "project-ctx-menu";
|
|
1890
1924
|
|
|
1891
1925
|
// --- Project Settings ---
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1926
|
+
if (!ctx.permissions || ctx.permissions.projectSettings !== false) {
|
|
1927
|
+
var settingsItem = document.createElement("button");
|
|
1928
|
+
settingsItem.className = "project-ctx-item";
|
|
1929
|
+
settingsItem.innerHTML = iconHtml("settings") + " <span>Project Settings</span>";
|
|
1930
|
+
settingsItem.addEventListener("click", function (e) {
|
|
1931
|
+
e.stopPropagation();
|
|
1932
|
+
closeProjectCtxMenu();
|
|
1933
|
+
openProjectSettings(slug, { slug: slug, name: name, icon: icon, projectOwnerId: ctx.projectOwnerId });
|
|
1934
|
+
});
|
|
1935
|
+
menu.appendChild(settingsItem);
|
|
1936
|
+
}
|
|
1901
1937
|
|
|
1902
1938
|
// --- Share ---
|
|
1903
1939
|
var shareItem = document.createElement("button");
|
|
@@ -1910,24 +1946,26 @@ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
|
|
|
1910
1946
|
});
|
|
1911
1947
|
menu.appendChild(shareItem);
|
|
1912
1948
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1949
|
+
if (!ctx.permissions || ctx.permissions.deleteProject !== false) {
|
|
1950
|
+
// --- Separator ---
|
|
1951
|
+
var sep = document.createElement("div");
|
|
1952
|
+
sep.className = "project-ctx-separator";
|
|
1953
|
+
menu.appendChild(sep);
|
|
1917
1954
|
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1955
|
+
// --- Delete ---
|
|
1956
|
+
var deleteItem = document.createElement("button");
|
|
1957
|
+
deleteItem.className = "project-ctx-item project-ctx-delete";
|
|
1958
|
+
deleteItem.innerHTML = iconHtml("trash-2") + " <span>Remove Project</span>";
|
|
1959
|
+
deleteItem.addEventListener("click", function (e) {
|
|
1960
|
+
e.stopPropagation();
|
|
1961
|
+
closeProjectCtxMenu();
|
|
1962
|
+
// Check for tasks/schedules first before removing
|
|
1963
|
+
if (ctx.ws && ctx.connected) {
|
|
1964
|
+
ctx.ws.send(JSON.stringify({ type: "remove_project_check", slug: slug, name: name }));
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
menu.appendChild(deleteItem);
|
|
1968
|
+
}
|
|
1931
1969
|
|
|
1932
1970
|
document.body.appendChild(menu);
|
|
1933
1971
|
projectCtxMenu = menu;
|
|
@@ -2376,6 +2414,11 @@ function createIconItem(p, currentSlug) {
|
|
|
2376
2414
|
}
|
|
2377
2415
|
el.appendChild(projectBadge);
|
|
2378
2416
|
|
|
2417
|
+
// Pending permission shake for non-active projects
|
|
2418
|
+
if (p.pendingPermissions > 0 && !isActive) {
|
|
2419
|
+
el.classList.add("has-pending-perm");
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2379
2422
|
(function (name, elem) {
|
|
2380
2423
|
elem.addEventListener("mouseenter", function () { showIconTooltip(elem, name); });
|
|
2381
2424
|
elem.addEventListener("mouseleave", hideIconTooltip);
|
|
@@ -2497,7 +2540,8 @@ function showWorktreeModal(parentSlug, parentName) {
|
|
|
2497
2540
|
if (ctx.ws && ctx.connected) {
|
|
2498
2541
|
ctx.ws.send(JSON.stringify({
|
|
2499
2542
|
type: "create_worktree",
|
|
2500
|
-
branch:
|
|
2543
|
+
branch: branch,
|
|
2544
|
+
dirName: dirName,
|
|
2501
2545
|
baseBranch: base
|
|
2502
2546
|
}));
|
|
2503
2547
|
}
|
|
@@ -2670,10 +2714,22 @@ export function renderIconStrip(projects, currentSlug) {
|
|
|
2670
2714
|
});
|
|
2671
2715
|
}
|
|
2672
2716
|
|
|
2717
|
+
// Pending permission shake for worktree items
|
|
2718
|
+
if (wt.pendingPermissions > 0 && !isWtActive) {
|
|
2719
|
+
wtEl.classList.add("has-pending-perm");
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2673
2722
|
itemsContainer.appendChild(wtEl);
|
|
2674
2723
|
})(worktrees[wi]);
|
|
2675
2724
|
}
|
|
2676
2725
|
|
|
2726
|
+
// Force expand if any worktree has pending permissions
|
|
2727
|
+
var hasWtPendingPerm = false;
|
|
2728
|
+
for (var wpi2 = 0; wpi2 < worktrees.length; wpi2++) {
|
|
2729
|
+
if (worktrees[wpi2].pendingPermissions > 0) { hasWtPendingPerm = true; break; }
|
|
2730
|
+
}
|
|
2731
|
+
if (hasWtPendingPerm) folder.classList.remove("collapsed");
|
|
2732
|
+
|
|
2677
2733
|
// "+" button to add new worktree
|
|
2678
2734
|
var addBtn = document.createElement("button");
|
|
2679
2735
|
addBtn.className = "icon-strip-group-add";
|
|
@@ -381,6 +381,36 @@ function renderNote(data) {
|
|
|
381
381
|
mdBtn.innerHTML = "<span class='sn-md-label'>MD</span>";
|
|
382
382
|
header.appendChild(mdBtn);
|
|
383
383
|
|
|
384
|
+
var opacityWrap = document.createElement("div");
|
|
385
|
+
opacityWrap.className = "sticky-note-opacity";
|
|
386
|
+
var opacitySlider = document.createElement("input");
|
|
387
|
+
opacitySlider.type = "range";
|
|
388
|
+
opacitySlider.min = "20";
|
|
389
|
+
opacitySlider.max = "100";
|
|
390
|
+
opacitySlider.value = String(Math.round((data.opacity || 1) * 100));
|
|
391
|
+
opacitySlider.className = "sticky-note-opacity-slider";
|
|
392
|
+
opacitySlider.title = "Opacity";
|
|
393
|
+
opacityWrap.appendChild(opacitySlider);
|
|
394
|
+
header.appendChild(opacityWrap);
|
|
395
|
+
|
|
396
|
+
// Apply saved opacity via CSS variable (not element opacity, so header stays visible on hover)
|
|
397
|
+
if (typeof data.opacity === "number") {
|
|
398
|
+
el.style.setProperty("--note-opacity", data.opacity);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
opacitySlider.addEventListener("input", function (e) {
|
|
402
|
+
e.stopPropagation();
|
|
403
|
+
var val = parseInt(opacitySlider.value, 10) / 100;
|
|
404
|
+
el.style.setProperty("--note-opacity", val);
|
|
405
|
+
});
|
|
406
|
+
opacitySlider.addEventListener("change", function (e) {
|
|
407
|
+
e.stopPropagation();
|
|
408
|
+
var val = parseInt(opacitySlider.value, 10) / 100;
|
|
409
|
+
el.style.setProperty("--note-opacity", val);
|
|
410
|
+
debouncedUpdate(data.id, { opacity: val }, 300);
|
|
411
|
+
});
|
|
412
|
+
opacitySlider.addEventListener("mousedown", function (e) { e.stopPropagation(); });
|
|
413
|
+
|
|
384
414
|
el.appendChild(header);
|
|
385
415
|
|
|
386
416
|
// Body
|
|
@@ -1008,6 +1038,13 @@ export function handleNoteUpdated(msg) {
|
|
|
1008
1038
|
syncTitle(entry.el, msg.note.text);
|
|
1009
1039
|
}
|
|
1010
1040
|
|
|
1041
|
+
// Handle opacity
|
|
1042
|
+
if (typeof msg.note.opacity === "number") {
|
|
1043
|
+
entry.el.style.setProperty("--note-opacity", msg.note.opacity);
|
|
1044
|
+
var slider = entry.el.querySelector(".sticky-note-opacity-slider");
|
|
1045
|
+
if (slider) slider.value = String(Math.round(msg.note.opacity * 100));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1011
1048
|
// Handle hidden state
|
|
1012
1049
|
if (msg.note.hidden) {
|
|
1013
1050
|
entry.el.classList.add("hidden");
|
package/lib/public/style.css
CHANGED
package/lib/scheduler.js
CHANGED
|
@@ -262,6 +262,8 @@ function createLoopRegistry(opts) {
|
|
|
262
262
|
source: data.source || null,
|
|
263
263
|
color: data.color || null,
|
|
264
264
|
recurrenceEnd: data.recurrenceEnd || null,
|
|
265
|
+
mode: data.mode || "loop",
|
|
266
|
+
prompt: data.prompt || null,
|
|
265
267
|
runs: [],
|
|
266
268
|
};
|
|
267
269
|
if (rec.cron && rec.enabled) {
|
|
@@ -290,6 +292,8 @@ function createLoopRegistry(opts) {
|
|
|
290
292
|
if (data.maxIterations !== undefined) rec.maxIterations = data.maxIterations;
|
|
291
293
|
if (data.date !== undefined) rec.date = data.date;
|
|
292
294
|
if (data.recurrenceEnd !== undefined) rec.recurrenceEnd = data.recurrenceEnd;
|
|
295
|
+
if (data.mode !== undefined) rec.mode = data.mode;
|
|
296
|
+
if (data.prompt !== undefined) rec.prompt = data.prompt;
|
|
293
297
|
rec.updatedAt = Date.now();
|
|
294
298
|
if (rec.cron && rec.enabled) {
|
|
295
299
|
rec.nextRunAt = nextRunTime(rec.cron);
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -1151,6 +1151,7 @@ function createSDKBridge(opts) {
|
|
|
1151
1151
|
decisionReason: opts.decisionReason || "",
|
|
1152
1152
|
};
|
|
1153
1153
|
sendAndRecord(session, permMsg);
|
|
1154
|
+
onProcessingChanged(); // update cross-project permission badge
|
|
1154
1155
|
|
|
1155
1156
|
if (pushModule) {
|
|
1156
1157
|
pushModule.sendPush({
|
|
@@ -1166,6 +1167,7 @@ function createSDKBridge(opts) {
|
|
|
1166
1167
|
opts.signal.addEventListener("abort", function() {
|
|
1167
1168
|
delete session.pendingPermissions[requestId];
|
|
1168
1169
|
sendAndRecord(session, { type: "permission_cancel", requestId: requestId });
|
|
1170
|
+
onProcessingChanged(); // update cross-project permission badge
|
|
1169
1171
|
resolve({ behavior: "deny", message: "Request cancelled" });
|
|
1170
1172
|
});
|
|
1171
1173
|
}
|
|
@@ -1327,14 +1329,14 @@ function createSDKBridge(opts) {
|
|
|
1327
1329
|
session.pendingPermissions = {};
|
|
1328
1330
|
session.pendingAskUser = {};
|
|
1329
1331
|
session.pendingElicitations = {};
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1332
|
+
// Ralph Loop: notify completion so loop orchestrator can proceed
|
|
1333
|
+
if (session.onQueryComplete) {
|
|
1334
|
+
console.log("[sdk-bridge] Calling onQueryComplete for session " + session.localId + " (title: " + (session.title || "?") + ")");
|
|
1335
|
+
try {
|
|
1336
|
+
session.onQueryComplete(session);
|
|
1337
|
+
} catch (err) {
|
|
1338
|
+
console.error("[sdk-bridge] onQueryComplete error:", err.message || err);
|
|
1339
|
+
}
|
|
1338
1340
|
}
|
|
1339
1341
|
}
|
|
1340
1342
|
}
|