clay-server 3.4.0-beta.21 → 3.4.0-beta.23
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/css/filebrowser.css +6 -1
- package/lib/public/css/pane.css +2 -0
- package/lib/public/index.html +1 -0
- package/lib/public/modules/app-messages.js +5 -1
- package/lib/public/modules/filebrowser-tabs.js +65 -0
- package/lib/public/modules/filebrowser.js +23 -3
- package/lib/public/modules/pane-bridge.js +6 -0
- package/lib/public/modules/split-view.js +8 -1
- package/lib/yoke/adapters/codex.js +33 -0
- package/lib/yoke/codex-background-tasks.js +84 -0
- package/package.json +1 -1
|
@@ -960,7 +960,7 @@
|
|
|
960
960
|
width: 50%;
|
|
961
961
|
max-width: 720px;
|
|
962
962
|
min-width: 360px;
|
|
963
|
-
border-
|
|
963
|
+
border-right: 1px solid var(--border);
|
|
964
964
|
background: var(--bg);
|
|
965
965
|
display: flex;
|
|
966
966
|
flex-direction: column;
|
|
@@ -1011,6 +1011,11 @@
|
|
|
1011
1011
|
min-height: 44px;
|
|
1012
1012
|
}
|
|
1013
1013
|
|
|
1014
|
+
.file-viewer-tabs { display: flex; min-width: 0; flex: 1; overflow-x: auto; gap: 2px; }
|
|
1015
|
+
.file-viewer-tab { display: inline-flex; align-items: center; gap: 6px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: 0; border-radius: 5px; padding: 4px 7px; background: transparent; color: var(--text-secondary); font: inherit; font-size: 12px; cursor: pointer; }
|
|
1016
|
+
.file-viewer-tab.active { background: var(--sidebar-hover); color: var(--text); }
|
|
1017
|
+
.file-viewer-tab-close { font-size: 16px; line-height: 12px; color: var(--text-muted); }
|
|
1018
|
+
|
|
1014
1019
|
.file-viewer-path {
|
|
1015
1020
|
flex: 1;
|
|
1016
1021
|
font-family: "Roboto Mono", monospace;
|
package/lib/public/css/pane.css
CHANGED
package/lib/public/index.html
CHANGED
|
@@ -680,6 +680,7 @@
|
|
|
680
680
|
|
|
681
681
|
<div id="file-viewer" class="hidden">
|
|
682
682
|
<div class="file-viewer-header">
|
|
683
|
+
<div class="file-viewer-tabs" id="file-viewer-tabs" role="tablist"></div>
|
|
683
684
|
<span class="file-viewer-path" id="file-viewer-path"></span>
|
|
684
685
|
<span class="file-viewer-live-status hidden" id="file-viewer-live-status" aria-live="polite">
|
|
685
686
|
<span class="file-viewer-live-dot"></span>
|
|
@@ -28,6 +28,7 @@ import { startThinking, appendThinking, stopThinking, resetThinkingGroup, create
|
|
|
28
28
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
29
29
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
|
|
30
30
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
31
|
+
import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
|
|
31
32
|
import { isProjectSettingsOpen, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, handleProjectSharedEnv, handleProjectSharedEnvSaved, handleProjectOwnerChanged } from './project-settings.js';
|
|
32
33
|
import { updateSettingsModels, updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleInheritGroupsChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './server-settings.js';
|
|
33
34
|
import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
@@ -1400,7 +1401,10 @@ export function processMessage(msg) {
|
|
|
1400
1401
|
break;
|
|
1401
1402
|
|
|
1402
1403
|
case "markdown_edit_present":
|
|
1403
|
-
if (!store.get('replayingHistory') && !store.get('dmMode'))
|
|
1404
|
+
if (!store.get('replayingHistory') && !store.get('dmMode')) {
|
|
1405
|
+
if (store.get('paneMode')) forwardPaneMarkdownPresentation(msg);
|
|
1406
|
+
else if (!store.get('splitPanes')) presentMarkdownEdit(msg);
|
|
1407
|
+
}
|
|
1404
1408
|
break;
|
|
1405
1409
|
|
|
1406
1410
|
case "fs_dir_changed":
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var tabs = new Map();
|
|
2
|
+
var focusedPath = null;
|
|
3
|
+
var onFocus = null;
|
|
4
|
+
var onEmpty = null;
|
|
5
|
+
|
|
6
|
+
function label(path) {
|
|
7
|
+
var parts = String(path || "").split("/");
|
|
8
|
+
return parts[parts.length - 1] || path;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function render() {
|
|
12
|
+
var root = document.getElementById("file-viewer-tabs");
|
|
13
|
+
if (!root) return;
|
|
14
|
+
root.innerHTML = "";
|
|
15
|
+
tabs.forEach(function(value, path) {
|
|
16
|
+
var tab = document.createElement("button");
|
|
17
|
+
tab.type = "button";
|
|
18
|
+
tab.className = "file-viewer-tab" + (path === focusedPath ? " active" : "");
|
|
19
|
+
tab.textContent = label(path);
|
|
20
|
+
tab.title = path;
|
|
21
|
+
tab.addEventListener("click", function() { focusFileViewerTab(path); });
|
|
22
|
+
var close = document.createElement("span");
|
|
23
|
+
close.className = "file-viewer-tab-close";
|
|
24
|
+
close.textContent = "×";
|
|
25
|
+
close.addEventListener("click", function(event) { event.stopPropagation(); closeFileViewerTab(path); });
|
|
26
|
+
tab.appendChild(close);
|
|
27
|
+
root.appendChild(tab);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function initFileViewerTabs(options) {
|
|
32
|
+
onFocus = options && options.onFocus;
|
|
33
|
+
onEmpty = options && options.onEmpty;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function openFileViewerTab(path, data) {
|
|
37
|
+
if (!path) return false;
|
|
38
|
+
var exists = tabs.has(path);
|
|
39
|
+
tabs.set(path, Object.assign(tabs.get(path) || {}, data || {}));
|
|
40
|
+
focusedPath = path;
|
|
41
|
+
render();
|
|
42
|
+
return !exists;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function focusFileViewerTab(path) {
|
|
46
|
+
if (!tabs.has(path)) return false;
|
|
47
|
+
focusedPath = path;
|
|
48
|
+
render();
|
|
49
|
+
if (onFocus) onFocus(path, tabs.get(path));
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function closeFileViewerTab(path) {
|
|
54
|
+
if (!tabs.has(path)) return;
|
|
55
|
+
var paths = Array.from(tabs.keys());
|
|
56
|
+
var index = paths.indexOf(path);
|
|
57
|
+
tabs.delete(path);
|
|
58
|
+
if (focusedPath === path) focusedPath = paths[index + 1] || paths[index - 1] || null;
|
|
59
|
+
render();
|
|
60
|
+
if (focusedPath && onFocus) onFocus(focusedPath, tabs.get(focusedPath));
|
|
61
|
+
if (!focusedPath && onEmpty) onEmpty();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function focusedFileViewerTab() { return focusedPath; }
|
|
65
|
+
export function clearFileViewerTabs() { tabs.clear(); focusedPath = null; render(); }
|
|
@@ -9,6 +9,7 @@ import { copyMarkdownFormatting } from './rich-clipboard.js';
|
|
|
9
9
|
import { animateMarkdownChange, beginMarkdownPresentation, cancelMarkdownFollow, isFollowingMarkdown } from './markdown-live-edit.js';
|
|
10
10
|
import { store } from './store.js';
|
|
11
11
|
import { enterMarkdownSlides, exitMarkdownSlides, handleMarkdownSlideKey, syncMarkdownSlidesButton, toggleMarkdownSlideLevelMenu } from './markdown-slides.js';
|
|
12
|
+
import { initFileViewerTabs, openFileViewerTab, closeFileViewerTab, focusedFileViewerTab, clearFileViewerTabs } from './filebrowser-tabs.js';
|
|
12
13
|
|
|
13
14
|
var ctx;
|
|
14
15
|
var showDropHint = function () {};
|
|
@@ -30,6 +31,14 @@ var pendingFileAt = null; // callback for pending file-at
|
|
|
30
31
|
|
|
31
32
|
export function initFileBrowser(_ctx) {
|
|
32
33
|
ctx = _ctx;
|
|
34
|
+
var mainPanels = document.getElementById("main-panels");
|
|
35
|
+
if (mainPanels && ctx.fileViewerEl) mainPanels.insertBefore(ctx.fileViewerEl, mainPanels.firstChild);
|
|
36
|
+
initFileViewerTabs({
|
|
37
|
+
onFocus: function(path) {
|
|
38
|
+
if (path !== currentFilePath) requestFileContent(path);
|
|
39
|
+
},
|
|
40
|
+
onEmpty: function() { teardownFileViewer(); },
|
|
41
|
+
});
|
|
33
42
|
|
|
34
43
|
// Load material file icons in background
|
|
35
44
|
initFileIcons().then(function () {
|
|
@@ -133,7 +142,7 @@ export function initFileBrowser(_ctx) {
|
|
|
133
142
|
|
|
134
143
|
// Close button
|
|
135
144
|
document.getElementById("file-viewer-close").addEventListener("click", function () {
|
|
136
|
-
|
|
145
|
+
closeFileViewerTab(focusedFileViewerTab());
|
|
137
146
|
});
|
|
138
147
|
|
|
139
148
|
// Full-viewport presentation toggle
|
|
@@ -448,6 +457,14 @@ function ensureRenderedMarkdown() {
|
|
|
448
457
|
}
|
|
449
458
|
|
|
450
459
|
export function closeFileViewer() {
|
|
460
|
+
if (focusedFileViewerTab()) {
|
|
461
|
+
closeFileViewerTab(focusedFileViewerTab());
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
teardownFileViewer();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function teardownFileViewer() {
|
|
451
468
|
if (currentFilePath && isFollowingMarkdown(currentFilePath)) cancelMarkdownFollow();
|
|
452
469
|
sendUnwatch();
|
|
453
470
|
inlineDiffActive = false;
|
|
@@ -475,8 +492,8 @@ export function setFileViewerFullscreen(enabled) {
|
|
|
475
492
|
}
|
|
476
493
|
|
|
477
494
|
export function resetFileBrowser() {
|
|
478
|
-
|
|
479
|
-
|
|
495
|
+
clearFileViewerTabs();
|
|
496
|
+
teardownFileViewer();
|
|
480
497
|
// Clear all cached state
|
|
481
498
|
treeData = {};
|
|
482
499
|
currentContent = null;
|
|
@@ -513,6 +530,7 @@ export function openFile(filePath, opts) {
|
|
|
513
530
|
cancelMarkdownFollow();
|
|
514
531
|
}
|
|
515
532
|
pendingRenderedOpen = !!(opts && opts.rendered);
|
|
533
|
+
openFileViewerTab(filePath);
|
|
516
534
|
if (opts && opts.diff) {
|
|
517
535
|
pendingOpenMode = { type: "diff", oldStr: opts.diff.oldStr, newStr: opts.diff.newStr };
|
|
518
536
|
} else {
|
|
@@ -540,6 +558,7 @@ export function openWorkingTreeDiff(diff) {
|
|
|
540
558
|
|
|
541
559
|
export function presentMarkdownEdit(msg) {
|
|
542
560
|
if (!msg || !beginMarkdownPresentation(msg.path)) return;
|
|
561
|
+
openFileViewerTab(msg.path, { content: msg.content });
|
|
543
562
|
pendingOpenMode = null;
|
|
544
563
|
pendingRenderedOpen = true;
|
|
545
564
|
pendingRefresh = false;
|
|
@@ -1031,6 +1050,7 @@ function renderEntries(container, entries, depth) {
|
|
|
1031
1050
|
// --- File viewer ---
|
|
1032
1051
|
|
|
1033
1052
|
function showFileContent(msg) {
|
|
1053
|
+
openFileViewerTab(msg.path, { content: msg.content });
|
|
1034
1054
|
var pathEl = document.getElementById("file-viewer-path");
|
|
1035
1055
|
var bodyEl = document.getElementById("file-viewer-body");
|
|
1036
1056
|
var renderBtn = document.getElementById("file-viewer-render");
|
|
@@ -23,6 +23,12 @@ export function reportPaneContext(data) {
|
|
|
23
23
|
}, window.location.origin);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function forwardPaneMarkdownPresentation(message) {
|
|
27
|
+
if (!store.get('paneMode') || window.parent === window) return false;
|
|
28
|
+
window.parent.postMessage({ type: "clay-pane-present-markdown", message: message }, window.location.origin);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
// Toggle without setContextView: panes share localStorage with the main app,
|
|
27
33
|
// so persisting here would silently rewrite the user's main-view preference.
|
|
28
34
|
function togglePaneContextPanel() {
|
|
@@ -10,6 +10,7 @@ import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
|
10
10
|
import { groupedSessionIds, findSplitGroup } from './split-group-helpers.js';
|
|
11
11
|
import { showConfirm } from './app-misc.js';
|
|
12
12
|
import { syncPairChrome } from './split-pair-ui.js';
|
|
13
|
+
import { presentMarkdownEdit } from './filebrowser.js';
|
|
13
14
|
|
|
14
15
|
var host = null;
|
|
15
16
|
var nativeApp = null;
|
|
@@ -269,7 +270,13 @@ function updatePaneContextChip(paneEl, msg) {
|
|
|
269
270
|
function handlePaneMessage(event) {
|
|
270
271
|
if (event.origin !== window.location.origin) return;
|
|
271
272
|
var msg = event.data;
|
|
272
|
-
if (!msg ||
|
|
273
|
+
if (!msg || !host) return;
|
|
274
|
+
if (msg.type === "clay-pane-present-markdown") {
|
|
275
|
+
var present = msg.message;
|
|
276
|
+
if (present) presentMarkdownEdit(present);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (msg.type !== "clay-pane-context") return;
|
|
273
280
|
var frames = host.querySelectorAll(".split-pane-frame");
|
|
274
281
|
for (var i = 0; i < frames.length; i++) {
|
|
275
282
|
if (frames[i].contentWindow === event.source) {
|
|
@@ -8,6 +8,7 @@ var fs = require("fs");
|
|
|
8
8
|
var { CodexAppServer } = require("../codex-app-server");
|
|
9
9
|
var skillDiscovery = require("../skill-discovery");
|
|
10
10
|
var { resolveOsUserInfo } = require("../../os-users");
|
|
11
|
+
var backgroundTasks = require("../codex-background-tasks");
|
|
11
12
|
|
|
12
13
|
// --- Event flattening ---
|
|
13
14
|
// Converts app-server JSON-RPC notifications into flat objects with a yokeType field.
|
|
@@ -672,6 +673,7 @@ function createEventState(model) {
|
|
|
672
673
|
toolBlocks: {},
|
|
673
674
|
commandInputs: {},
|
|
674
675
|
planTexts: {},
|
|
676
|
+
backgroundTasks: backgroundTasks.createState(),
|
|
675
677
|
};
|
|
676
678
|
}
|
|
677
679
|
|
|
@@ -698,6 +700,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
698
700
|
var eventWaiting = null;
|
|
699
701
|
var iteratorDone = false;
|
|
700
702
|
var finishedNotified = false;
|
|
703
|
+
var removeBackgroundTaskReset = null;
|
|
701
704
|
|
|
702
705
|
function notifyFinished() {
|
|
703
706
|
if (finishedNotified) return;
|
|
@@ -722,6 +725,12 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
722
725
|
}
|
|
723
726
|
}
|
|
724
727
|
|
|
728
|
+
if (typeof queryOpts.registerBackgroundTaskReset === "function") {
|
|
729
|
+
removeBackgroundTaskReset = queryOpts.registerBackgroundTaskReset(function() {
|
|
730
|
+
backgroundTasks.emitReset(state.backgroundTasks, pushEvent);
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
|
|
725
734
|
function endIterator() {
|
|
726
735
|
iteratorDone = true;
|
|
727
736
|
if (eventWaiting) {
|
|
@@ -729,6 +738,10 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
729
738
|
eventWaiting = null;
|
|
730
739
|
resolve({ value: undefined, done: true });
|
|
731
740
|
}
|
|
741
|
+
if (removeBackgroundTaskReset) {
|
|
742
|
+
removeBackgroundTaskReset();
|
|
743
|
+
removeBackgroundTaskReset = null;
|
|
744
|
+
}
|
|
732
745
|
notifyFinished();
|
|
733
746
|
}
|
|
734
747
|
|
|
@@ -934,6 +947,10 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
934
947
|
pushEvent(yokeEvents[i]);
|
|
935
948
|
}
|
|
936
949
|
|
|
950
|
+
if (method === "turn/completed" || method === "turn/failed") {
|
|
951
|
+
backgroundTasks.poll(appServer, state.threadId, state.backgroundTasks, pushEvent).catch(function() {});
|
|
952
|
+
}
|
|
953
|
+
|
|
937
954
|
// Resolve turn promise when turn ends
|
|
938
955
|
if (method === "turn/completed" || method === "turn/failed") {
|
|
939
956
|
if (turnResolve) {
|
|
@@ -1246,6 +1263,20 @@ function createCodexAdapter(opts) {
|
|
|
1246
1263
|
];
|
|
1247
1264
|
var _cachedModels = CODEX_MODELS.slice();
|
|
1248
1265
|
var _appServer = null;
|
|
1266
|
+
var _backgroundTaskResetListeners = [];
|
|
1267
|
+
|
|
1268
|
+
function registerBackgroundTaskReset(listener) {
|
|
1269
|
+
_backgroundTaskResetListeners.push(listener);
|
|
1270
|
+
return function() {
|
|
1271
|
+
var index = _backgroundTaskResetListeners.indexOf(listener);
|
|
1272
|
+
if (index !== -1) _backgroundTaskResetListeners.splice(index, 1);
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function emitBackgroundTaskReset() {
|
|
1277
|
+
var listeners = _backgroundTaskResetListeners.slice();
|
|
1278
|
+
for (var i = 0; i < listeners.length; i++) listeners[i]();
|
|
1279
|
+
}
|
|
1249
1280
|
var _initPromise = null;
|
|
1250
1281
|
var _shutdownPromise = null;
|
|
1251
1282
|
var _refCount = 0;
|
|
@@ -1541,6 +1572,7 @@ function createCodexAdapter(opts) {
|
|
|
1541
1572
|
// Spawn and initialize app-server
|
|
1542
1573
|
_appServer = _createAppServer(serverOpts);
|
|
1543
1574
|
await _appServer.start();
|
|
1575
|
+
emitBackgroundTaskReset();
|
|
1544
1576
|
|
|
1545
1577
|
await _appServer.send("initialize", {
|
|
1546
1578
|
clientInfo: { name: "clay", title: "Clay", version: "1.0.0" },
|
|
@@ -1676,6 +1708,7 @@ function createCodexAdapter(opts) {
|
|
|
1676
1708
|
canUseTool: queryOpts.canUseTool || null,
|
|
1677
1709
|
onElicitation: queryOpts.onElicitation || null,
|
|
1678
1710
|
resumeSessionId: queryOpts.resumeSessionId || null,
|
|
1711
|
+
registerBackgroundTaskReset: registerBackgroundTaskReset,
|
|
1679
1712
|
};
|
|
1680
1713
|
|
|
1681
1714
|
// Reasoning effort
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
function terminalList(result) {
|
|
2
|
+
if (Array.isArray(result)) return result;
|
|
3
|
+
if (!result || typeof result !== "object") return [];
|
|
4
|
+
if (Array.isArray(result.terminals)) return result.terminals;
|
|
5
|
+
if (Array.isArray(result.backgroundTerminals)) return result.backgroundTerminals;
|
|
6
|
+
if (Array.isArray(result.items)) return result.items;
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isLiveTerminal(terminal) {
|
|
11
|
+
var status = String((terminal && (terminal.status || terminal.state)) || "").toLowerCase();
|
|
12
|
+
return status !== "exited" && status !== "terminated" && status !== "completed" && status !== "failed";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function mapTerminals(result) {
|
|
16
|
+
var terminals = terminalList(result);
|
|
17
|
+
var tasks = [];
|
|
18
|
+
for (var i = 0; i < terminals.length; i++) {
|
|
19
|
+
var terminal = terminals[i] || {};
|
|
20
|
+
var taskId = terminal.id || terminal.terminalId || terminal.taskId || "";
|
|
21
|
+
if (!taskId || !isLiveTerminal(terminal)) continue;
|
|
22
|
+
tasks.push({
|
|
23
|
+
task_id: String(taskId),
|
|
24
|
+
task_type: "shell",
|
|
25
|
+
description: terminal.commandLine || terminal.command || terminal.name || "",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return tasks;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function taskIds(tasks) {
|
|
32
|
+
var ids = {};
|
|
33
|
+
for (var i = 0; i < tasks.length; i++) ids[tasks[i].task_id] = true;
|
|
34
|
+
return ids;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sameMembership(first, second) {
|
|
38
|
+
var firstKeys = Object.keys(first || {});
|
|
39
|
+
var secondKeys = Object.keys(second || {});
|
|
40
|
+
if (firstKeys.length !== secondKeys.length) return false;
|
|
41
|
+
for (var i = 0; i < firstKeys.length; i++) {
|
|
42
|
+
if (!second[firstKeys[i]]) return false;
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createState() {
|
|
48
|
+
return { taskIds: null };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function emitIfChanged(state, tasks, pushEvent) {
|
|
52
|
+
var ids = taskIds(tasks);
|
|
53
|
+
if (state.taskIds === null && tasks.length === 0) {
|
|
54
|
+
state.taskIds = ids;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (state.taskIds && sameMembership(state.taskIds, ids)) return false;
|
|
58
|
+
state.taskIds = ids;
|
|
59
|
+
pushEvent({ yokeType: "background_tasks_changed", tasks: tasks });
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function poll(appServer, threadId, state, pushEvent) {
|
|
64
|
+
if (!appServer || !threadId || appServer._clayBackgroundTasksPollingDisabled) return Promise.resolve(false);
|
|
65
|
+
return appServer.send("thread/backgroundTerminals/list", { threadId: threadId }).then(function(result) {
|
|
66
|
+
return emitIfChanged(state, mapTerminals(result), pushEvent);
|
|
67
|
+
}).catch(function() {
|
|
68
|
+
appServer._clayBackgroundTasksPollingDisabled = true;
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function emitReset(state, pushEvent) {
|
|
74
|
+
state.taskIds = {};
|
|
75
|
+
pushEvent({ yokeType: "background_tasks_changed", tasks: [] });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
createState: createState,
|
|
80
|
+
mapTerminals: mapTerminals,
|
|
81
|
+
emitIfChanged: emitIfChanged,
|
|
82
|
+
poll: poll,
|
|
83
|
+
emitReset: emitReset,
|
|
84
|
+
};
|
package/package.json
CHANGED