agentweaver 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -7
- package/dist/artifacts.js +9 -0
- package/dist/executors/git-commit-executor.js +24 -6
- package/dist/flow-state.js +3 -8
- package/dist/git/git-diff-parser.js +223 -0
- package/dist/git/git-service.js +562 -0
- package/dist/git/git-stage-selection.js +24 -0
- package/dist/git/git-status-parser.js +171 -0
- package/dist/git/git-types.js +1 -0
- package/dist/index.js +450 -108
- package/dist/interactive/auto-flow.js +644 -0
- package/dist/interactive/controller.js +417 -9
- package/dist/interactive/progress.js +194 -1
- package/dist/interactive/state.js +25 -0
- package/dist/interactive/web/index.js +97 -12
- package/dist/interactive/web/protocol.js +216 -1
- package/dist/interactive/web/server.js +72 -14
- package/dist/interactive/web/static/app.js +1603 -49
- package/dist/interactive/web/static/index.html +76 -11
- package/dist/interactive/web/static/styles.css +1 -1
- package/dist/interactive/web/static/styles.input.css +901 -47
- package/dist/pipeline/auto-flow-blocks.js +307 -0
- package/dist/pipeline/auto-flow-config.js +273 -0
- package/dist/pipeline/auto-flow-identity.js +49 -0
- package/dist/pipeline/auto-flow-presets.js +52 -0
- package/dist/pipeline/auto-flow-resolver.js +830 -0
- package/dist/pipeline/auto-flow-types.js +17 -0
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/declarative-flows.js +27 -1
- package/dist/pipeline/flow-specs/auto-common-guided.json +11 -0
- package/dist/pipeline/flow-specs/auto-golang.json +12 -1
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +54 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +19 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +33 -1
- package/dist/pipeline/flow-specs/review/review-project.json +19 -1
- package/dist/pipeline/flow-specs/task-source/manual-jira-input.json +70 -0
- package/dist/pipeline/node-registry.js +9 -0
- package/dist/pipeline/nodes/codex-prompt-node.js +8 -1
- package/dist/pipeline/nodes/flow-run-node.js +5 -3
- package/dist/pipeline/nodes/git-status-node.js +2 -168
- package/dist/pipeline/nodes/manual-jira-task-input-node.js +146 -0
- package/dist/pipeline/nodes/opencode-prompt-node.js +8 -1
- package/dist/pipeline/nodes/plan-codex-node.js +8 -1
- package/dist/pipeline/spec-loader.js +14 -4
- package/dist/runtime/artifact-catalog.js +29 -5
- package/dist/runtime/settings.js +114 -0
- package/dist/scope.js +14 -4
- package/package.json +1 -1
- package/dist/pipeline/flow-specs/auto-common.json +0 -179
- package/dist/pipeline/flow-specs/auto-simple.json +0 -141
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
var AUTO_FLOW_HEIGHT_DEFAULT = 520;
|
|
5
|
+
var AUTO_FLOW_HEIGHT_MIN = 120;
|
|
6
|
+
var AUTO_FLOW_HEIGHT_MAX = 640;
|
|
7
|
+
var AUTO_FLOW_LOWER_PANELS_MIN = 180;
|
|
8
|
+
var WORKSPACE_SPLIT_DEFAULT = 36;
|
|
9
|
+
var WORKSPACE_SPLIT_MIN = 24;
|
|
10
|
+
var WORKSPACE_SPLIT_MAX = 58;
|
|
11
|
+
var autoFlowResizeDrag = null;
|
|
12
|
+
var workspaceResizeDrag = null;
|
|
13
|
+
|
|
4
14
|
var state = {
|
|
5
15
|
viewModel: null,
|
|
6
16
|
connectionState: "connecting",
|
|
17
|
+
theme: "light",
|
|
18
|
+
autoFlowHeight: null,
|
|
19
|
+
workspaceSplit: WORKSPACE_SPLIT_DEFAULT,
|
|
20
|
+
logAutoscroll: true,
|
|
7
21
|
formValues: {},
|
|
8
22
|
modalSignature: null,
|
|
9
23
|
artifacts: {
|
|
@@ -18,6 +32,26 @@
|
|
|
18
32
|
previewRequestId: 0,
|
|
19
33
|
viewerModes: {},
|
|
20
34
|
},
|
|
35
|
+
gitSelectedPaths: [],
|
|
36
|
+
gitCommitMessage: "",
|
|
37
|
+
gitDiff: {
|
|
38
|
+
open: false,
|
|
39
|
+
selectedPath: null,
|
|
40
|
+
mode: "head",
|
|
41
|
+
loading: false,
|
|
42
|
+
error: null,
|
|
43
|
+
diff: null,
|
|
44
|
+
requestId: 0,
|
|
45
|
+
},
|
|
46
|
+
scrollSync: {
|
|
47
|
+
suppress: false,
|
|
48
|
+
releaseTimer: null,
|
|
49
|
+
sentOffsets: {
|
|
50
|
+
progress: null,
|
|
51
|
+
log: null,
|
|
52
|
+
help: null,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
21
55
|
};
|
|
22
56
|
|
|
23
57
|
var elements = {
|
|
@@ -28,15 +62,45 @@
|
|
|
28
62
|
run: document.getElementById("run-button"),
|
|
29
63
|
interrupt: document.getElementById("interrupt-button"),
|
|
30
64
|
help: document.getElementById("help-button"),
|
|
65
|
+
themeToggle: document.getElementById("theme-toggle-button"),
|
|
66
|
+
themeToggleLabel: document.getElementById("theme-toggle-label"),
|
|
31
67
|
flowsTitle: document.getElementById("flows-title"),
|
|
32
68
|
flows: document.getElementById("flows-list"),
|
|
33
|
-
|
|
69
|
+
autoFlowEditor: document.getElementById("auto-flow-editor"),
|
|
70
|
+
autoFlowResizer: document.getElementById("auto-flow-resizer"),
|
|
71
|
+
splitPanels: document.getElementById("split-panels"),
|
|
72
|
+
workspaceResizer: document.getElementById("workspace-resizer"),
|
|
34
73
|
progressTitle: document.getElementById("progress-title"),
|
|
74
|
+
progressFlowLabel: document.getElementById("progress-flow-label"),
|
|
35
75
|
progress: document.getElementById("progress-text"),
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
gitRefresh: document.getElementById("git-refresh-button"),
|
|
77
|
+
gitSummary: document.getElementById("git-summary"),
|
|
78
|
+
gitBranchInput: document.getElementById("git-branch-input"),
|
|
79
|
+
gitCreateBranch: document.getElementById("git-create-branch-button"),
|
|
80
|
+
gitCheckoutSelect: document.getElementById("git-checkout-select"),
|
|
81
|
+
gitCheckout: document.getElementById("git-checkout-button"),
|
|
82
|
+
gitFetch: document.getElementById("git-fetch-button"),
|
|
83
|
+
gitPull: document.getElementById("git-pull-button"),
|
|
84
|
+
gitFiles: document.getElementById("git-files"),
|
|
85
|
+
gitCommitMessage: document.getElementById("git-commit-message"),
|
|
86
|
+
gitStage: document.getElementById("git-stage-button"),
|
|
87
|
+
gitUnstage: document.getElementById("git-unstage-button"),
|
|
88
|
+
gitCommit: document.getElementById("git-commit-button"),
|
|
89
|
+
gitPush: document.getElementById("git-push-button"),
|
|
90
|
+
gitFeedback: document.getElementById("git-feedback"),
|
|
91
|
+
gitDiffDrawer: document.getElementById("git-diff-drawer"),
|
|
92
|
+
gitDiffClose: document.getElementById("git-diff-close-button"),
|
|
93
|
+
gitDiffTitle: document.getElementById("git-diff-title"),
|
|
94
|
+
gitDiffMeta: document.getElementById("git-diff-meta"),
|
|
95
|
+
gitDiffStatus: document.getElementById("git-diff-status"),
|
|
96
|
+
gitDiffFileList: document.getElementById("git-diff-file-list"),
|
|
97
|
+
gitDiffSelectedTitle: document.getElementById("git-diff-selected-title"),
|
|
98
|
+
gitDiffSelectedMeta: document.getElementById("git-diff-selected-meta"),
|
|
99
|
+
gitDiffModeControls: document.getElementById("git-diff-mode-controls"),
|
|
100
|
+
gitDiffBody: document.getElementById("git-diff-body"),
|
|
38
101
|
logTitle: document.getElementById("log-title"),
|
|
39
102
|
log: document.getElementById("log-text"),
|
|
103
|
+
logAutoscroll: document.getElementById("log-autoscroll-toggle"),
|
|
40
104
|
clearLog: document.getElementById("clear-log-button"),
|
|
41
105
|
artifactOpen: document.getElementById("artifact-open-button"),
|
|
42
106
|
artifactDrawer: document.getElementById("artifact-drawer"),
|
|
@@ -67,10 +131,411 @@
|
|
|
67
131
|
return fallback;
|
|
68
132
|
}
|
|
69
133
|
|
|
134
|
+
function normalizeTheme(value) {
|
|
135
|
+
return value === "dark" ? "dark" : "light";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isTheme(value) {
|
|
139
|
+
return value === "dark" || value === "light";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function persistThemePreference(theme) {
|
|
143
|
+
api.updateSettings({ theme: normalizeTheme(theme) });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function applyTheme(theme) {
|
|
147
|
+
var nextTheme = normalizeTheme(theme);
|
|
148
|
+
var root = document.documentElement || document.body;
|
|
149
|
+
state.theme = nextTheme;
|
|
150
|
+
if (root) {
|
|
151
|
+
root.setAttribute("data-theme", nextTheme);
|
|
152
|
+
}
|
|
153
|
+
if (elements.themeToggle) {
|
|
154
|
+
var dark = nextTheme === "dark";
|
|
155
|
+
elements.themeToggle.setAttribute("aria-pressed", dark ? "true" : "false");
|
|
156
|
+
elements.themeToggle.setAttribute("aria-label", dark ? "Switch to light theme" : "Switch to dark theme");
|
|
157
|
+
elements.themeToggle.title = dark ? "Switch to light theme" : "Switch to dark theme";
|
|
158
|
+
}
|
|
159
|
+
if (elements.themeToggleLabel) {
|
|
160
|
+
elements.themeToggleLabel.textContent = nextTheme === "dark" ? "Dark" : "Light";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function toggleTheme() {
|
|
165
|
+
var nextTheme = state.theme === "dark" ? "light" : "dark";
|
|
166
|
+
applyTheme(nextTheme);
|
|
167
|
+
persistThemePreference(nextTheme);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function persistAutoFlowHeight(height) {
|
|
171
|
+
var clamped = clampAutoFlowHeight(height);
|
|
172
|
+
api.updateSettings({ autoFlowHeight: clamped });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function autoFlowHeightBounds() {
|
|
176
|
+
var max = AUTO_FLOW_HEIGHT_MAX;
|
|
177
|
+
var detailsPane = elements && elements.autoFlowEditor ? elements.autoFlowEditor.parentNode : null;
|
|
178
|
+
if (detailsPane) {
|
|
179
|
+
var rectHeight = typeof detailsPane.getBoundingClientRect === "function"
|
|
180
|
+
? detailsPane.getBoundingClientRect().height
|
|
181
|
+
: 0;
|
|
182
|
+
if (Number.isFinite(rectHeight) && rectHeight > AUTO_FLOW_HEIGHT_MIN + AUTO_FLOW_LOWER_PANELS_MIN) {
|
|
183
|
+
max = Math.min(max, rectHeight - AUTO_FLOW_LOWER_PANELS_MIN);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
min: AUTO_FLOW_HEIGHT_MIN,
|
|
188
|
+
max: Math.max(AUTO_FLOW_HEIGHT_MIN, max),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function clampAutoFlowHeight(value) {
|
|
193
|
+
var numeric = Number(value);
|
|
194
|
+
if (!Number.isFinite(numeric)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
var bounds = autoFlowHeightBounds();
|
|
198
|
+
return Math.min(bounds.max, Math.max(bounds.min, Math.round(numeric)));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getAutoFlowEditorHeight() {
|
|
202
|
+
if (!elements.autoFlowEditor) {
|
|
203
|
+
return state.autoFlowHeight || AUTO_FLOW_HEIGHT_DEFAULT;
|
|
204
|
+
}
|
|
205
|
+
if (typeof elements.autoFlowEditor.getBoundingClientRect === "function") {
|
|
206
|
+
var rect = elements.autoFlowEditor.getBoundingClientRect();
|
|
207
|
+
if (rect && Number.isFinite(rect.height) && rect.height > 0) {
|
|
208
|
+
return rect.height;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
var inlineHeight = parseInt(elements.autoFlowEditor.style.height || "", 10);
|
|
212
|
+
return Number.isFinite(inlineHeight) ? inlineHeight : (state.autoFlowHeight || AUTO_FLOW_HEIGHT_DEFAULT);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function applyAutoFlowHeight(height) {
|
|
216
|
+
if (!elements.autoFlowEditor) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
var clamped = clampAutoFlowHeight(height);
|
|
220
|
+
if (clamped === null) {
|
|
221
|
+
state.autoFlowHeight = null;
|
|
222
|
+
elements.autoFlowEditor.style.height = "";
|
|
223
|
+
updateAutoFlowResizerState(null);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
state.autoFlowHeight = clamped;
|
|
227
|
+
elements.autoFlowEditor.style.height = clamped + "px";
|
|
228
|
+
updateAutoFlowResizerState(clamped);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function updateAutoFlowResizerState(currentHeight) {
|
|
232
|
+
if (!elements.autoFlowResizer) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
var bounds = autoFlowHeightBounds();
|
|
236
|
+
elements.autoFlowResizer.setAttribute("aria-valuemin", String(bounds.min));
|
|
237
|
+
elements.autoFlowResizer.setAttribute("aria-valuemax", String(bounds.max));
|
|
238
|
+
elements.autoFlowResizer.setAttribute("aria-valuenow", String(Math.round(currentHeight || getAutoFlowEditorHeight())));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function beginAutoFlowResize(event) {
|
|
242
|
+
if (!elements.autoFlowEditor || elements.autoFlowEditor.hidden) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (event.button !== undefined && event.button !== 0) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (event.preventDefault) {
|
|
249
|
+
event.preventDefault();
|
|
250
|
+
}
|
|
251
|
+
autoFlowResizeDrag = {
|
|
252
|
+
startY: Number.isFinite(Number(event.clientY)) ? Number(event.clientY) : 0,
|
|
253
|
+
startHeight: getAutoFlowEditorHeight(),
|
|
254
|
+
};
|
|
255
|
+
document.body.classList.add("auto-flow-resizing");
|
|
256
|
+
elements.autoFlowResizer.classList.add("resizing");
|
|
257
|
+
if (event.currentTarget && typeof event.currentTarget.setPointerCapture === "function" && event.pointerId !== undefined) {
|
|
258
|
+
try {
|
|
259
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
260
|
+
} catch {
|
|
261
|
+
// Pointer capture is a progressive enhancement for smoother dragging.
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
window.addEventListener("pointermove", updateAutoFlowResize);
|
|
265
|
+
window.addEventListener("pointerup", finishAutoFlowResize);
|
|
266
|
+
window.addEventListener("pointercancel", finishAutoFlowResize);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function updateAutoFlowResize(event) {
|
|
270
|
+
if (!autoFlowResizeDrag) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (event.preventDefault) {
|
|
274
|
+
event.preventDefault();
|
|
275
|
+
}
|
|
276
|
+
var clientY = Number(event.clientY);
|
|
277
|
+
if (!Number.isFinite(clientY)) {
|
|
278
|
+
clientY = autoFlowResizeDrag.startY;
|
|
279
|
+
}
|
|
280
|
+
applyAutoFlowHeight(autoFlowResizeDrag.startHeight + clientY - autoFlowResizeDrag.startY);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function finishAutoFlowResize() {
|
|
284
|
+
if (!autoFlowResizeDrag) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
autoFlowResizeDrag = null;
|
|
288
|
+
document.body.classList.remove("auto-flow-resizing");
|
|
289
|
+
elements.autoFlowResizer.classList.remove("resizing");
|
|
290
|
+
persistAutoFlowHeight(state.autoFlowHeight);
|
|
291
|
+
window.removeEventListener("pointermove", updateAutoFlowResize);
|
|
292
|
+
window.removeEventListener("pointerup", finishAutoFlowResize);
|
|
293
|
+
window.removeEventListener("pointercancel", finishAutoFlowResize);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resetAutoFlowHeight() {
|
|
297
|
+
applyAutoFlowHeight(null);
|
|
298
|
+
persistAutoFlowHeight(null);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function handleAutoFlowResizerKeydown(event) {
|
|
302
|
+
var step = event.shiftKey ? 48 : 20;
|
|
303
|
+
var nextHeight = getAutoFlowEditorHeight();
|
|
304
|
+
if (event.key === "ArrowUp") {
|
|
305
|
+
nextHeight -= step;
|
|
306
|
+
} else if (event.key === "ArrowDown") {
|
|
307
|
+
nextHeight += step;
|
|
308
|
+
} else if (event.key === "Home") {
|
|
309
|
+
nextHeight = AUTO_FLOW_HEIGHT_MIN;
|
|
310
|
+
} else if (event.key === "End") {
|
|
311
|
+
nextHeight = AUTO_FLOW_HEIGHT_MAX;
|
|
312
|
+
} else {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
applyAutoFlowHeight(nextHeight);
|
|
317
|
+
persistAutoFlowHeight(state.autoFlowHeight);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function persistWorkspaceSplit(split) {
|
|
321
|
+
var nextSplit = Number.isFinite(split) ? clampWorkspaceSplit(split) : WORKSPACE_SPLIT_DEFAULT;
|
|
322
|
+
api.updateSettings({ workspaceSplit: nextSplit });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function clampWorkspaceSplit(value) {
|
|
326
|
+
var numeric = Number(value);
|
|
327
|
+
if (!Number.isFinite(numeric)) {
|
|
328
|
+
return WORKSPACE_SPLIT_DEFAULT;
|
|
329
|
+
}
|
|
330
|
+
return Math.min(WORKSPACE_SPLIT_MAX, Math.max(WORKSPACE_SPLIT_MIN, Math.round(numeric)));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function applyWorkspaceSplit(split) {
|
|
334
|
+
if (!elements.splitPanels) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
var clamped = clampWorkspaceSplit(split);
|
|
338
|
+
state.workspaceSplit = clamped;
|
|
339
|
+
if (elements.splitPanels.style && typeof elements.splitPanels.style.setProperty === "function") {
|
|
340
|
+
elements.splitPanels.style.setProperty("--aw-work-panel-width", clamped + "%");
|
|
341
|
+
} else if (elements.splitPanels.style) {
|
|
342
|
+
elements.splitPanels.style["--aw-work-panel-width"] = clamped + "%";
|
|
343
|
+
}
|
|
344
|
+
updateWorkspaceResizerState(clamped);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function workspacePanelWidth() {
|
|
348
|
+
if (!elements.splitPanels || typeof elements.splitPanels.getBoundingClientRect !== "function") {
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
var rect = elements.splitPanels.getBoundingClientRect();
|
|
352
|
+
return rect && Number.isFinite(rect.width) ? rect.width : 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function updateWorkspaceResizerState(split) {
|
|
356
|
+
if (!elements.workspaceResizer) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
elements.workspaceResizer.setAttribute("aria-valuemin", String(WORKSPACE_SPLIT_MIN));
|
|
360
|
+
elements.workspaceResizer.setAttribute("aria-valuemax", String(WORKSPACE_SPLIT_MAX));
|
|
361
|
+
elements.workspaceResizer.setAttribute("aria-valuenow", String(clampWorkspaceSplit(split)));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function beginWorkspaceResize(event) {
|
|
365
|
+
if (event.button !== undefined && event.button !== 0) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
var width = workspacePanelWidth();
|
|
369
|
+
if (width <= 0) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (event.preventDefault) {
|
|
373
|
+
event.preventDefault();
|
|
374
|
+
}
|
|
375
|
+
workspaceResizeDrag = {
|
|
376
|
+
startX: Number.isFinite(Number(event.clientX)) ? Number(event.clientX) : 0,
|
|
377
|
+
startSplit: state.workspaceSplit,
|
|
378
|
+
width: width,
|
|
379
|
+
};
|
|
380
|
+
document.body.classList.add("workspace-split-resizing");
|
|
381
|
+
elements.workspaceResizer.classList.add("resizing");
|
|
382
|
+
if (event.currentTarget && typeof event.currentTarget.setPointerCapture === "function" && event.pointerId !== undefined) {
|
|
383
|
+
try {
|
|
384
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
385
|
+
} catch {
|
|
386
|
+
// Pointer capture is a progressive enhancement for smoother dragging.
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
window.addEventListener("pointermove", updateWorkspaceResize);
|
|
390
|
+
window.addEventListener("pointerup", finishWorkspaceResize);
|
|
391
|
+
window.addEventListener("pointercancel", finishWorkspaceResize);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function updateWorkspaceResize(event) {
|
|
395
|
+
if (!workspaceResizeDrag) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (event.preventDefault) {
|
|
399
|
+
event.preventDefault();
|
|
400
|
+
}
|
|
401
|
+
var clientX = Number(event.clientX);
|
|
402
|
+
if (!Number.isFinite(clientX)) {
|
|
403
|
+
clientX = workspaceResizeDrag.startX;
|
|
404
|
+
}
|
|
405
|
+
var nextSplit = workspaceResizeDrag.startSplit + ((clientX - workspaceResizeDrag.startX) / workspaceResizeDrag.width) * 100;
|
|
406
|
+
applyWorkspaceSplit(nextSplit);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function finishWorkspaceResize() {
|
|
410
|
+
if (!workspaceResizeDrag) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
workspaceResizeDrag = null;
|
|
414
|
+
document.body.classList.remove("workspace-split-resizing");
|
|
415
|
+
elements.workspaceResizer.classList.remove("resizing");
|
|
416
|
+
persistWorkspaceSplit(state.workspaceSplit);
|
|
417
|
+
window.removeEventListener("pointermove", updateWorkspaceResize);
|
|
418
|
+
window.removeEventListener("pointerup", finishWorkspaceResize);
|
|
419
|
+
window.removeEventListener("pointercancel", finishWorkspaceResize);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function resetWorkspaceSplit() {
|
|
423
|
+
applyWorkspaceSplit(WORKSPACE_SPLIT_DEFAULT);
|
|
424
|
+
persistWorkspaceSplit(null);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function handleWorkspaceResizerKeydown(event) {
|
|
428
|
+
var step = event.shiftKey ? 6 : 2;
|
|
429
|
+
var nextSplit = state.workspaceSplit;
|
|
430
|
+
if (event.key === "ArrowLeft") {
|
|
431
|
+
nextSplit -= step;
|
|
432
|
+
} else if (event.key === "ArrowRight") {
|
|
433
|
+
nextSplit += step;
|
|
434
|
+
} else if (event.key === "Home") {
|
|
435
|
+
nextSplit = WORKSPACE_SPLIT_MIN;
|
|
436
|
+
} else if (event.key === "End") {
|
|
437
|
+
nextSplit = WORKSPACE_SPLIT_MAX;
|
|
438
|
+
} else {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
applyWorkspaceSplit(nextSplit);
|
|
443
|
+
persistWorkspaceSplit(state.workspaceSplit);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function persistLogAutoscroll(enabled) {
|
|
447
|
+
api.updateSettings({ logAutoscroll: Boolean(enabled) });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function applyLogAutoscroll(enabled) {
|
|
451
|
+
state.logAutoscroll = Boolean(enabled);
|
|
452
|
+
if (elements.logAutoscroll) {
|
|
453
|
+
elements.logAutoscroll.checked = state.logAutoscroll;
|
|
454
|
+
}
|
|
455
|
+
if (state.logAutoscroll) {
|
|
456
|
+
scrollLogToBottom();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function applyWebUiSettings(settings) {
|
|
461
|
+
if (!settings || typeof settings !== "object") {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (isTheme(settings.theme)) {
|
|
465
|
+
applyTheme(settings.theme);
|
|
466
|
+
}
|
|
467
|
+
if ("autoFlowHeight" in settings) {
|
|
468
|
+
applyAutoFlowHeight(settings.autoFlowHeight);
|
|
469
|
+
}
|
|
470
|
+
if (Number.isFinite(Number(settings.workspaceSplit))) {
|
|
471
|
+
applyWorkspaceSplit(settings.workspaceSplit);
|
|
472
|
+
}
|
|
473
|
+
if (typeof settings.logAutoscroll === "boolean") {
|
|
474
|
+
applyLogAutoscroll(settings.logAutoscroll);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function scrollLogToBottom() {
|
|
479
|
+
if (!elements.log) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
elements.log.scrollTop = elements.log.scrollHeight;
|
|
483
|
+
rememberScrollOffset("log", elements.log.scrollTop);
|
|
484
|
+
}
|
|
485
|
+
|
|
70
486
|
function actionId() {
|
|
71
487
|
return "web-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
|
|
72
488
|
}
|
|
73
489
|
|
|
490
|
+
function controlId(prefix) {
|
|
491
|
+
var parts = Array.prototype.slice.call(arguments, 1).map(function (part) {
|
|
492
|
+
return String(part || "")
|
|
493
|
+
.trim()
|
|
494
|
+
.replace(/[^A-Za-z0-9_-]+/g, "-")
|
|
495
|
+
.replace(/^-+|-+$/g, "") || "value";
|
|
496
|
+
});
|
|
497
|
+
return [prefix].concat(parts).join("-");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function roundedScrollTop(element) {
|
|
501
|
+
return Math.round(Number(element.scrollTop) || 0);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function rememberScrollOffset(pane, offset) {
|
|
505
|
+
if (state.scrollSync.sentOffsets[pane] !== undefined) {
|
|
506
|
+
state.scrollSync.sentOffsets[pane] = Math.round(Number(offset) || 0);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function rememberCurrentScrollOffsets() {
|
|
511
|
+
rememberScrollOffset("progress", elements.progress ? elements.progress.scrollTop : 0);
|
|
512
|
+
rememberScrollOffset("log", elements.log ? elements.log.scrollTop : 0);
|
|
513
|
+
rememberScrollOffset("help", elements.helpText ? elements.helpText.scrollTop : 0);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function releaseScrollSuppressionSoon() {
|
|
517
|
+
if (state.scrollSync.releaseTimer) {
|
|
518
|
+
clearTimeout(state.scrollSync.releaseTimer);
|
|
519
|
+
}
|
|
520
|
+
state.scrollSync.releaseTimer = setTimeout(function () {
|
|
521
|
+
state.scrollSync.suppress = false;
|
|
522
|
+
state.scrollSync.releaseTimer = null;
|
|
523
|
+
rememberCurrentScrollOffsets();
|
|
524
|
+
}, 0);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function sendScrollPane(pane, element) {
|
|
528
|
+
if (state.scrollSync.suppress) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
var offset = roundedScrollTop(element);
|
|
532
|
+
if (state.scrollSync.sentOffsets[pane] === offset) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
rememberScrollOffset(pane, offset);
|
|
536
|
+
api.scrollPane(pane, offset);
|
|
537
|
+
}
|
|
538
|
+
|
|
74
539
|
function selectedFlow() {
|
|
75
540
|
var vm = state.viewModel;
|
|
76
541
|
if (!vm || !Array.isArray(vm.flowItems)) {
|
|
@@ -172,6 +637,60 @@
|
|
|
172
637
|
scrollPane: function (pane, offset) {
|
|
173
638
|
api.send({ type: "scroll", pane: pane, offset: offset });
|
|
174
639
|
},
|
|
640
|
+
refreshGit: function () {
|
|
641
|
+
api.send({ type: "git.refresh" });
|
|
642
|
+
},
|
|
643
|
+
createGitBranch: function () {
|
|
644
|
+
api.send({ type: "git.createBranch", branchName: elements.gitBranchInput.value });
|
|
645
|
+
},
|
|
646
|
+
checkoutGitBranch: function () {
|
|
647
|
+
api.send({ type: "git.checkout", branchName: elements.gitCheckoutSelect.value });
|
|
648
|
+
},
|
|
649
|
+
fetchGit: function () {
|
|
650
|
+
api.send({ type: "git.fetch" });
|
|
651
|
+
},
|
|
652
|
+
pullGit: function () {
|
|
653
|
+
api.send({ type: "git.pullFfOnly" });
|
|
654
|
+
},
|
|
655
|
+
stageGit: function () {
|
|
656
|
+
api.send({ type: "git.stage", paths: selectedGitPaths() });
|
|
657
|
+
},
|
|
658
|
+
unstageGit: function () {
|
|
659
|
+
api.send({ type: "git.unstage", paths: selectedGitPaths() });
|
|
660
|
+
},
|
|
661
|
+
commitGit: function () {
|
|
662
|
+
api.send({ type: "git.commit", paths: selectedGitPaths(), message: elements.gitCommitMessage.value });
|
|
663
|
+
},
|
|
664
|
+
pushGit: function () {
|
|
665
|
+
api.send({ type: "git.push" });
|
|
666
|
+
},
|
|
667
|
+
updateGitCommitMessage: function () {
|
|
668
|
+
api.send({ type: "git.updateCommitMessage", message: elements.gitCommitMessage.value });
|
|
669
|
+
},
|
|
670
|
+
updateSettings: function (settings) {
|
|
671
|
+
api.send({ type: "settings.update", settings: settings });
|
|
672
|
+
},
|
|
673
|
+
selectAutoFlowPreset: function (preset) {
|
|
674
|
+
api.send({ type: "autoFlow.selectPreset", preset: preset });
|
|
675
|
+
},
|
|
676
|
+
saveAutoFlow: function () {
|
|
677
|
+
api.send({ type: "autoFlow.save" });
|
|
678
|
+
},
|
|
679
|
+
resetAutoFlow: function () {
|
|
680
|
+
api.send({ type: "autoFlow.reset" });
|
|
681
|
+
},
|
|
682
|
+
toggleAutoFlowBlock: function (slotId, blockId, enabled) {
|
|
683
|
+
api.send({ type: "autoFlow.toggleBlock", slotId: slotId, blockId: blockId, enabled: enabled });
|
|
684
|
+
},
|
|
685
|
+
updateAutoFlowParam: function (slotId, blockId, paramName, value) {
|
|
686
|
+
api.send({ type: "autoFlow.updateParam", slotId: slotId, blockId: blockId, paramName: paramName, value: value });
|
|
687
|
+
},
|
|
688
|
+
insertAutoFlowBlock: function (slotId, blockId) {
|
|
689
|
+
api.send({ type: "autoFlow.insertBlock", slotId: slotId, blockId: blockId });
|
|
690
|
+
},
|
|
691
|
+
removeAutoFlowBlock: function (slotId, blockId) {
|
|
692
|
+
api.send({ type: "autoFlow.removeBlock", slotId: slotId, blockId: blockId });
|
|
693
|
+
},
|
|
175
694
|
};
|
|
176
695
|
|
|
177
696
|
function setConnection(nextState) {
|
|
@@ -190,11 +709,18 @@
|
|
|
190
709
|
}
|
|
191
710
|
|
|
192
711
|
if (message.type === "snapshot") {
|
|
712
|
+
applyWebUiSettings(message.settings);
|
|
193
713
|
var uiState = captureUiState();
|
|
194
714
|
state.viewModel = message.viewModel || {};
|
|
195
715
|
state.formValues = state.viewModel.form ? Object.assign({}, state.viewModel.form.values || {}) : {};
|
|
716
|
+
state.gitSelectedPaths = state.viewModel.gitWorkspace && Array.isArray(state.viewModel.gitWorkspace.selectedPaths)
|
|
717
|
+
? state.viewModel.gitWorkspace.selectedPaths.slice()
|
|
718
|
+
: [];
|
|
719
|
+
state.scrollSync.suppress = true;
|
|
196
720
|
render();
|
|
197
721
|
restoreUiState(uiState);
|
|
722
|
+
rememberCurrentScrollOffsets();
|
|
723
|
+
releaseScrollSuppressionSoon();
|
|
198
724
|
return;
|
|
199
725
|
}
|
|
200
726
|
if (message.type === "log.append") {
|
|
@@ -215,8 +741,8 @@
|
|
|
215
741
|
if (!lines) return;
|
|
216
742
|
var wasPinned = elements.log.scrollTop + elements.log.clientHeight >= elements.log.scrollHeight - 8;
|
|
217
743
|
elements.log.textContent += (elements.log.textContent ? "\n" : "") + lines;
|
|
218
|
-
if (wasPinned) {
|
|
219
|
-
|
|
744
|
+
if (state.logAutoscroll || wasPinned) {
|
|
745
|
+
scrollLogToBottom();
|
|
220
746
|
}
|
|
221
747
|
}
|
|
222
748
|
|
|
@@ -226,30 +752,967 @@
|
|
|
226
752
|
elements.header.textContent = text(vm.header, "Local operator console");
|
|
227
753
|
elements.status.textContent = text(vm.statusText, "Idle");
|
|
228
754
|
elements.flowsTitle.textContent = text(vm.flowListTitle, "Flows");
|
|
229
|
-
|
|
755
|
+
renderAutoFlowEditor(vm);
|
|
230
756
|
elements.progressTitle.textContent = text(vm.progressTitle, "Progress");
|
|
231
|
-
|
|
232
|
-
elements.summaryTitle.textContent = text(vm.summaryTitle, "Task Summary");
|
|
233
|
-
setTextPreservingScroll(elements.summary, vm.summaryVisible === false ? "Summary is hidden." : text(vm.summaryText, "No task summary yet."));
|
|
757
|
+
renderProgress(vm);
|
|
234
758
|
elements.logTitle.textContent = text(vm.logTitle, "Activity");
|
|
235
|
-
setTextPreservingScroll(elements.log, text(vm.logText, ""));
|
|
759
|
+
setTextPreservingScroll(elements.log, text(vm.logText, ""), state.logAutoscroll);
|
|
236
760
|
elements.helpText.textContent = text(vm.helpText, "No help is available.");
|
|
237
761
|
elements.helpPanel.hidden = !vm.helpVisible;
|
|
238
762
|
elements.help.setAttribute("aria-pressed", vm.helpVisible ? "true" : "false");
|
|
239
763
|
|
|
240
764
|
renderFlows(vm);
|
|
765
|
+
renderGitWorkspace(vm);
|
|
766
|
+
renderGitDiffDrawer(vm);
|
|
241
767
|
renderModal(vm);
|
|
242
768
|
renderArtifactExplorer(vm);
|
|
243
769
|
}
|
|
244
770
|
|
|
245
|
-
function
|
|
771
|
+
function gitWorkspace(vm) {
|
|
772
|
+
var workspace = vm && vm.gitWorkspace;
|
|
773
|
+
if (!workspace || typeof workspace !== "object") {
|
|
774
|
+
return {
|
|
775
|
+
available: false,
|
|
776
|
+
changedFiles: [],
|
|
777
|
+
branches: [],
|
|
778
|
+
remotes: [],
|
|
779
|
+
selectedPaths: [],
|
|
780
|
+
commitMessage: "",
|
|
781
|
+
operation: { status: "idle" },
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
return workspace;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function renderGitWorkspace(vm) {
|
|
788
|
+
var git = gitWorkspace(vm);
|
|
789
|
+
var blocked = hasBlockingInput(vm);
|
|
790
|
+
var changedFiles = Array.isArray(git.changedFiles) ? git.changedFiles : [];
|
|
791
|
+
var branches = Array.isArray(git.branches) ? git.branches : [];
|
|
792
|
+
var operation = git.operation || { status: "idle" };
|
|
793
|
+
var isRunning = operation.status === "running";
|
|
794
|
+
|
|
795
|
+
if (typeof git.commitMessage === "string" && document.activeElement !== elements.gitCommitMessage) {
|
|
796
|
+
state.gitCommitMessage = git.commitMessage;
|
|
797
|
+
elements.gitCommitMessage.value = git.commitMessage;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
elements.gitSummary.innerHTML = "";
|
|
801
|
+
var summary = document.createElement("span");
|
|
802
|
+
if (!git.available) {
|
|
803
|
+
summary.textContent = git.error || "Git workspace is unavailable.";
|
|
804
|
+
} else {
|
|
805
|
+
summary.append(
|
|
806
|
+
document.createTextNode("Branch "),
|
|
807
|
+
strong(git.detachedHead ? "detached HEAD" : (git.branch || "-")),
|
|
808
|
+
document.createTextNode(git.clean ? " | clean" : " | dirty"),
|
|
809
|
+
document.createTextNode(" | upstream " + (git.upstream || "-")),
|
|
810
|
+
document.createTextNode(" | ahead " + (git.ahead || 0) + " behind " + (git.behind || 0)),
|
|
811
|
+
);
|
|
812
|
+
if (git.lastCommit && git.lastCommit.shortHash) {
|
|
813
|
+
summary.append(document.createTextNode(" | last " + git.lastCommit.shortHash + " " + (git.lastCommit.subject || "")));
|
|
814
|
+
}
|
|
815
|
+
if (git.refreshedAt) {
|
|
816
|
+
summary.append(document.createTextNode(" | refreshed " + git.refreshedAt));
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
elements.gitSummary.append(summary);
|
|
820
|
+
|
|
821
|
+
renderGitBranches(branches, git.branch);
|
|
822
|
+
renderGitFiles(changedFiles);
|
|
823
|
+
|
|
824
|
+
elements.gitCreateBranch.disabled = blocked || isRunning || !git.available;
|
|
825
|
+
elements.gitCheckout.disabled = blocked || isRunning || !git.available || !elements.gitCheckoutSelect.value;
|
|
826
|
+
elements.gitFetch.disabled = blocked || isRunning || !git.available;
|
|
827
|
+
elements.gitPull.disabled = blocked || isRunning || !git.available;
|
|
828
|
+
elements.gitStage.disabled = blocked || isRunning || !git.available || selectedStageableGitPaths(git).length === 0;
|
|
829
|
+
elements.gitUnstage.disabled = blocked || isRunning || !git.available || selectedUnstageableGitPaths(git).length === 0;
|
|
830
|
+
elements.gitCommit.disabled = blocked || isRunning || !git.available || elements.gitCommitMessage.value.trim().length === 0;
|
|
831
|
+
elements.gitPush.disabled = blocked || isRunning || !git.available || !git.canPush;
|
|
832
|
+
elements.gitPush.title = git.canPush ? "Push current branch" : (git.pushDisabledReason || "Push is not available.");
|
|
833
|
+
|
|
834
|
+
elements.gitFeedback.className = "git-feedback";
|
|
835
|
+
if (operation.status === "error") {
|
|
836
|
+
elements.gitFeedback.classList.add("error");
|
|
837
|
+
} else if (operation.status === "success") {
|
|
838
|
+
elements.gitFeedback.classList.add("success");
|
|
839
|
+
}
|
|
840
|
+
var feedback = operation.message || git.pushDisabledReason || "";
|
|
841
|
+
var warnings = Array.isArray(git.warnings) && git.warnings.length > 0 ? " " + git.warnings.join(" ") : "";
|
|
842
|
+
elements.gitFeedback.textContent = feedback + warnings;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function strong(value) {
|
|
846
|
+
var element = document.createElement("strong");
|
|
847
|
+
element.textContent = value;
|
|
848
|
+
return element;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function renderAutoFlowEditor(vm) {
|
|
852
|
+
var model = vm && vm.autoFlow;
|
|
853
|
+
if (!elements.autoFlowEditor) return;
|
|
854
|
+
elements.autoFlowEditor.innerHTML = "";
|
|
855
|
+
if (!model || !Array.isArray(model.slots)) {
|
|
856
|
+
elements.autoFlowEditor.hidden = true;
|
|
857
|
+
if (elements.autoFlowResizer) {
|
|
858
|
+
elements.autoFlowResizer.hidden = true;
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
elements.autoFlowEditor.hidden = false;
|
|
863
|
+
if (elements.autoFlowResizer) {
|
|
864
|
+
elements.autoFlowResizer.hidden = false;
|
|
865
|
+
}
|
|
866
|
+
applyAutoFlowHeight(state.autoFlowHeight);
|
|
867
|
+
var blocked = hasBlockingInput(vm);
|
|
868
|
+
|
|
869
|
+
var toolbar = document.createElement("div");
|
|
870
|
+
toolbar.className = "auto-flow-toolbar";
|
|
871
|
+
var simple = document.createElement("button");
|
|
872
|
+
simple.type = "button";
|
|
873
|
+
simple.textContent = "Simple";
|
|
874
|
+
simple.disabled = blocked;
|
|
875
|
+
simple.className = model.basePreset === "simple" ? "primary" : "";
|
|
876
|
+
simple.addEventListener("click", function () {
|
|
877
|
+
api.selectAutoFlowPreset("simple");
|
|
878
|
+
});
|
|
879
|
+
var standard = document.createElement("button");
|
|
880
|
+
standard.type = "button";
|
|
881
|
+
standard.textContent = "Standard";
|
|
882
|
+
standard.disabled = blocked;
|
|
883
|
+
standard.className = model.basePreset === "standard" ? "primary" : "";
|
|
884
|
+
standard.addEventListener("click", function () {
|
|
885
|
+
api.selectAutoFlowPreset("standard");
|
|
886
|
+
});
|
|
887
|
+
var save = document.createElement("button");
|
|
888
|
+
save.type = "button";
|
|
889
|
+
save.textContent = "Save";
|
|
890
|
+
save.disabled = blocked || !model.status || !model.status.canSave;
|
|
891
|
+
save.addEventListener("click", api.saveAutoFlow);
|
|
892
|
+
var reset = document.createElement("button");
|
|
893
|
+
reset.type = "button";
|
|
894
|
+
reset.textContent = "Reset";
|
|
895
|
+
reset.disabled = blocked || !model.status || !model.status.canReset;
|
|
896
|
+
reset.title = "Discard unsaved auto-flow edits";
|
|
897
|
+
reset.addEventListener("click", api.resetAutoFlow);
|
|
898
|
+
var status = document.createElement("span");
|
|
899
|
+
status.className = "auto-flow-status " + (model.status && model.status.valid ? "valid" : "invalid");
|
|
900
|
+
status.textContent = (model.status && model.status.valid ? "valid" : "invalid") + " | " + text(model.status && model.status.sourceLabel, model.configName || "auto-flow");
|
|
901
|
+
toolbar.append(simple, standard, save, reset, status);
|
|
902
|
+
elements.autoFlowEditor.append(toolbar);
|
|
903
|
+
|
|
904
|
+
if (model.status && model.status.lastMessage) {
|
|
905
|
+
var message = document.createElement("div");
|
|
906
|
+
message.className = "auto-flow-message";
|
|
907
|
+
message.textContent = model.status.lastMessage;
|
|
908
|
+
elements.autoFlowEditor.append(message);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (Array.isArray(model.diagnostics) && model.diagnostics.length > 0) {
|
|
912
|
+
var diagnostics = document.createElement("div");
|
|
913
|
+
diagnostics.className = "auto-flow-diagnostics";
|
|
914
|
+
model.diagnostics.forEach(function (diagnostic) {
|
|
915
|
+
var item = document.createElement("div");
|
|
916
|
+
item.textContent = diagnostic.message || "Invalid auto-flow configuration.";
|
|
917
|
+
diagnostics.append(item);
|
|
918
|
+
});
|
|
919
|
+
elements.autoFlowEditor.append(diagnostics);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
var slots = document.createElement("div");
|
|
923
|
+
slots.className = "auto-flow-slots";
|
|
924
|
+
model.slots.forEach(function (slot) {
|
|
925
|
+
var slotRow = document.createElement("section");
|
|
926
|
+
slotRow.className = "auto-flow-slot status-" + slot.status;
|
|
927
|
+
slotRow.dataset.slotId = slot.slotId;
|
|
928
|
+
var title = document.createElement("div");
|
|
929
|
+
title.className = "auto-flow-slot-title";
|
|
930
|
+
title.append(strong(text(slot.title, slot.slotId)), document.createTextNode(" "), statusPill(slot.status));
|
|
931
|
+
var reason = document.createElement("div");
|
|
932
|
+
reason.className = "auto-flow-reason";
|
|
933
|
+
reason.textContent = text(slot.reason, "");
|
|
934
|
+
slotRow.append(title, reason);
|
|
935
|
+
|
|
936
|
+
var blockList = document.createElement("div");
|
|
937
|
+
blockList.className = "auto-flow-blocks";
|
|
938
|
+
if (!Array.isArray(slot.blocks) || slot.blocks.length === 0) {
|
|
939
|
+
var empty = document.createElement("div");
|
|
940
|
+
empty.className = "auto-flow-empty";
|
|
941
|
+
empty.textContent = "Empty slot.";
|
|
942
|
+
blockList.append(empty);
|
|
943
|
+
} else {
|
|
944
|
+
slot.blocks.forEach(function (block) {
|
|
945
|
+
blockList.append(renderAutoFlowBlock(block, blocked));
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
slotRow.append(blockList);
|
|
949
|
+
var insert = renderAutoFlowInsert(slot, model.availableBlocks || [], blocked);
|
|
950
|
+
if (insert) {
|
|
951
|
+
slotRow.append(insert);
|
|
952
|
+
}
|
|
953
|
+
slots.append(slotRow);
|
|
954
|
+
});
|
|
955
|
+
elements.autoFlowEditor.append(slots);
|
|
956
|
+
updateAutoFlowResizerState(state.autoFlowHeight);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function statusPill(status) {
|
|
960
|
+
var pill = document.createElement("span");
|
|
961
|
+
pill.className = "auto-flow-pill status-" + status;
|
|
962
|
+
pill.textContent = status || "pending";
|
|
963
|
+
return pill;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function renderAutoFlowBlock(block, blocked) {
|
|
967
|
+
var row = document.createElement("div");
|
|
968
|
+
row.className = "auto-flow-block status-" + block.status;
|
|
969
|
+
row.dataset.blockId = block.blockId;
|
|
970
|
+
row.dataset.slotId = block.slotId;
|
|
971
|
+
var enabled = document.createElement("input");
|
|
972
|
+
enabled.type = "checkbox";
|
|
973
|
+
enabled.id = controlId("auto-flow-enabled", block.slotId, block.blockId);
|
|
974
|
+
enabled.name = enabled.id;
|
|
975
|
+
enabled.checked = block.enabled !== false;
|
|
976
|
+
enabled.disabled = blocked || block.locked || !(block.actions && (block.actions.canEnable || block.actions.canDisable));
|
|
977
|
+
enabled.title = block.locked ? "Locked core block" : "Enable or disable block";
|
|
978
|
+
enabled.addEventListener("change", function () {
|
|
979
|
+
api.toggleAutoFlowBlock(block.slotId, block.blockId, enabled.checked);
|
|
980
|
+
});
|
|
981
|
+
var main = document.createElement("div");
|
|
982
|
+
main.className = "auto-flow-block-main";
|
|
983
|
+
var label = document.createElement("div");
|
|
984
|
+
label.className = "auto-flow-block-label";
|
|
985
|
+
label.append(strong(text(block.title, block.blockId)), document.createTextNode(" "), statusPill(block.status));
|
|
986
|
+
if (block.locked) {
|
|
987
|
+
var locked = document.createElement("span");
|
|
988
|
+
locked.className = "auto-flow-locked";
|
|
989
|
+
locked.textContent = "locked";
|
|
990
|
+
label.append(document.createTextNode(" "), locked);
|
|
991
|
+
}
|
|
992
|
+
var reason = document.createElement("div");
|
|
993
|
+
reason.className = "auto-flow-reason";
|
|
994
|
+
reason.textContent = text(block.reason, "");
|
|
995
|
+
main.append(label, reason);
|
|
996
|
+
if (Array.isArray(block.params) && block.params.length > 0) {
|
|
997
|
+
var params = document.createElement("div");
|
|
998
|
+
params.className = "auto-flow-params";
|
|
999
|
+
block.params.forEach(function (param) {
|
|
1000
|
+
var field = document.createElement("label");
|
|
1001
|
+
field.textContent = param.label + " ";
|
|
1002
|
+
var input = document.createElement("input");
|
|
1003
|
+
input.type = "number";
|
|
1004
|
+
input.id = controlId("auto-flow-param", block.slotId, block.blockId, param.name);
|
|
1005
|
+
input.name = input.id;
|
|
1006
|
+
input.min = String(param.min);
|
|
1007
|
+
input.max = String(param.max);
|
|
1008
|
+
input.step = "1";
|
|
1009
|
+
input.value = param.value === null || param.value === undefined ? "" : String(param.value);
|
|
1010
|
+
input.disabled = blocked || !(block.actions && block.actions.canEditParams);
|
|
1011
|
+
input.addEventListener("change", function () {
|
|
1012
|
+
var next = Number(input.value);
|
|
1013
|
+
if (Number.isInteger(next)) {
|
|
1014
|
+
api.updateAutoFlowParam(block.slotId, block.blockId, param.name, next);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
field.append(input);
|
|
1018
|
+
params.append(field);
|
|
1019
|
+
});
|
|
1020
|
+
main.append(params);
|
|
1021
|
+
}
|
|
1022
|
+
row.append(enabled, main);
|
|
1023
|
+
if (block.actions && block.actions.canRemove) {
|
|
1024
|
+
var remove = document.createElement("button");
|
|
1025
|
+
remove.type = "button";
|
|
1026
|
+
remove.className = "danger";
|
|
1027
|
+
remove.textContent = "Delete";
|
|
1028
|
+
remove.disabled = blocked;
|
|
1029
|
+
remove.title = "Remove block from this slot";
|
|
1030
|
+
remove.addEventListener("click", function (event) {
|
|
1031
|
+
if (event && typeof event.preventDefault === "function") {
|
|
1032
|
+
event.preventDefault();
|
|
1033
|
+
}
|
|
1034
|
+
api.removeAutoFlowBlock(block.slotId, block.blockId);
|
|
1035
|
+
});
|
|
1036
|
+
row.append(remove);
|
|
1037
|
+
}
|
|
1038
|
+
return row;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function renderAutoFlowInsert(slot, availableBlocks, blocked) {
|
|
1042
|
+
var configured = new Set((Array.isArray(slot.blocks) ? slot.blocks : []).map(function (block) {
|
|
1043
|
+
return block.blockId;
|
|
1044
|
+
}));
|
|
1045
|
+
var candidates = availableBlocks.filter(function (block) {
|
|
1046
|
+
return Array.isArray(block.allowedSlots)
|
|
1047
|
+
&& block.allowedSlots.indexOf(slot.slotId) !== -1
|
|
1048
|
+
&& !configured.has(block.blockId);
|
|
1049
|
+
});
|
|
1050
|
+
if (candidates.length === 0) {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
var container = document.createElement("div");
|
|
1054
|
+
container.className = "auto-flow-insert";
|
|
1055
|
+
var select = document.createElement("select");
|
|
1056
|
+
select.id = controlId("auto-flow-insert", slot.slotId);
|
|
1057
|
+
select.name = select.id;
|
|
1058
|
+
select.value = "";
|
|
1059
|
+
var placeholder = document.createElement("option");
|
|
1060
|
+
placeholder.value = "";
|
|
1061
|
+
placeholder.textContent = "Add block...";
|
|
1062
|
+
placeholder.disabled = true;
|
|
1063
|
+
placeholder.selected = true;
|
|
1064
|
+
select.append(placeholder);
|
|
1065
|
+
candidates.forEach(function (block) {
|
|
1066
|
+
var option = document.createElement("option");
|
|
1067
|
+
option.value = block.blockId;
|
|
1068
|
+
option.textContent = block.title || block.blockId;
|
|
1069
|
+
select.append(option);
|
|
1070
|
+
});
|
|
1071
|
+
var button = document.createElement("button");
|
|
1072
|
+
button.type = "button";
|
|
1073
|
+
button.textContent = "Insert";
|
|
1074
|
+
button.disabled = blocked || !select.value;
|
|
1075
|
+
select.addEventListener("change", function () {
|
|
1076
|
+
button.disabled = blocked || !select.value;
|
|
1077
|
+
});
|
|
1078
|
+
button.addEventListener("click", function (event) {
|
|
1079
|
+
if (event && typeof event.preventDefault === "function") {
|
|
1080
|
+
event.preventDefault();
|
|
1081
|
+
}
|
|
1082
|
+
if (!select.value) {
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
api.insertAutoFlowBlock(slot.slotId, select.value);
|
|
1086
|
+
});
|
|
1087
|
+
container.append(select, button);
|
|
1088
|
+
return container;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function renderGitBranches(branches, currentBranch) {
|
|
1092
|
+
elements.gitCheckoutSelect.innerHTML = "";
|
|
1093
|
+
branches.forEach(function (branch) {
|
|
1094
|
+
var option = document.createElement("option");
|
|
1095
|
+
option.value = branch.name;
|
|
1096
|
+
option.textContent = branch.current ? branch.name + " (current)" : branch.name;
|
|
1097
|
+
option.selected = branch.name === currentBranch;
|
|
1098
|
+
elements.gitCheckoutSelect.append(option);
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
function renderGitFiles(files) {
|
|
1103
|
+
elements.gitFiles.innerHTML = "";
|
|
1104
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
1105
|
+
var empty = document.createElement("div");
|
|
1106
|
+
empty.className = "git-empty";
|
|
1107
|
+
empty.textContent = "No changed files.";
|
|
1108
|
+
elements.gitFiles.append(empty);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
var tree = buildGitFileTree(files);
|
|
1113
|
+
elements.gitFiles.setAttribute("role", "tree");
|
|
1114
|
+
tree.forEach(function (root) {
|
|
1115
|
+
elements.gitFiles.append(renderGitTreeNode(root, 0));
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function buildGitFileTree(files) {
|
|
1120
|
+
var roots = [
|
|
1121
|
+
createGitTreeNode("group", "modified", "modified", "modified"),
|
|
1122
|
+
createGitTreeNode("group", "untracked", "untracked", "untracked"),
|
|
1123
|
+
];
|
|
1124
|
+
var rootByKey = { modified: roots[0], untracked: roots[1] };
|
|
1125
|
+
files.forEach(function (file) {
|
|
1126
|
+
var filePath = file.path || file.file || "";
|
|
1127
|
+
if (!filePath) return;
|
|
1128
|
+
var rootKey = gitRootKey(file);
|
|
1129
|
+
var parent = rootByKey[rootKey] || rootByKey.modified;
|
|
1130
|
+
var parts = filePath.split("/").filter(Boolean);
|
|
1131
|
+
var leafName = parts.pop() || filePath;
|
|
1132
|
+
parts.forEach(function (part) {
|
|
1133
|
+
parent = findOrCreateGitDirectory(parent, part);
|
|
1134
|
+
});
|
|
1135
|
+
var leaf = createGitTreeNode("file", leafName, filePath, rootKey);
|
|
1136
|
+
leaf.file = file;
|
|
1137
|
+
leaf.path = filePath;
|
|
1138
|
+
parent.children.push(leaf);
|
|
1139
|
+
});
|
|
1140
|
+
roots.forEach(sortGitTreeNode);
|
|
1141
|
+
return roots;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function gitRootKey(file) {
|
|
1145
|
+
return file && (file.type === "untracked" || file.xy === "??") ? "untracked" : "modified";
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function createGitTreeNode(kind, name, label, rootKey) {
|
|
1149
|
+
return {
|
|
1150
|
+
kind: kind,
|
|
1151
|
+
name: name,
|
|
1152
|
+
label: label,
|
|
1153
|
+
rootKey: rootKey,
|
|
1154
|
+
children: [],
|
|
1155
|
+
file: null,
|
|
1156
|
+
path: null,
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function findOrCreateGitDirectory(parent, name) {
|
|
1161
|
+
var existing = parent.children.find(function (child) {
|
|
1162
|
+
return child.kind === "dir" && child.name === name;
|
|
1163
|
+
});
|
|
1164
|
+
if (existing) return existing;
|
|
1165
|
+
var directory = createGitTreeNode("dir", name, name, parent.rootKey);
|
|
1166
|
+
parent.children.push(directory);
|
|
1167
|
+
return directory;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function sortGitTreeNode(node) {
|
|
1171
|
+
node.children.sort(function (left, right) {
|
|
1172
|
+
if (left.kind === "dir" && right.kind !== "dir") return -1;
|
|
1173
|
+
if (left.kind !== "dir" && right.kind === "dir") return 1;
|
|
1174
|
+
return left.name.localeCompare(right.name);
|
|
1175
|
+
});
|
|
1176
|
+
node.children.forEach(sortGitTreeNode);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function renderGitTreeNode(node, depth) {
|
|
1180
|
+
var paths = gitTreePaths(node);
|
|
1181
|
+
var selectedCount = paths.filter(function (filePath) {
|
|
1182
|
+
return state.gitSelectedPaths.indexOf(filePath) !== -1;
|
|
1183
|
+
}).length;
|
|
1184
|
+
var checked = paths.length > 0 && selectedCount === paths.length;
|
|
1185
|
+
var indeterminate = selectedCount > 0 && selectedCount < paths.length;
|
|
1186
|
+
var row = document.createElement("label");
|
|
1187
|
+
row.className = node.kind === "file" ? "git-file-row git-tree-row" : "git-tree-row git-tree-" + node.kind;
|
|
1188
|
+
row.style.paddingLeft = String(8 + depth * 18) + "px";
|
|
1189
|
+
row.setAttribute("role", "treeitem");
|
|
1190
|
+
row.setAttribute("aria-level", String(depth + 1));
|
|
1191
|
+
row.setAttribute("aria-checked", indeterminate ? "mixed" : String(checked));
|
|
1192
|
+
if (node.kind === "file" && node.path) {
|
|
1193
|
+
row.dataset.path = node.path;
|
|
1194
|
+
} else if (node.kind === "group") {
|
|
1195
|
+
row.dataset.gitRoot = node.rootKey;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
var checkbox = document.createElement("input");
|
|
1199
|
+
checkbox.type = "checkbox";
|
|
1200
|
+
checkbox.id = controlId("git-select", node.kind, node.rootKey, node.path || node.label || node.name);
|
|
1201
|
+
checkbox.name = checkbox.id;
|
|
1202
|
+
checkbox.checked = checked;
|
|
1203
|
+
checkbox.indeterminate = indeterminate;
|
|
1204
|
+
checkbox.disabled = paths.length === 0;
|
|
1205
|
+
checkbox.addEventListener("change", function () {
|
|
1206
|
+
setGitSelection(paths, checkbox.checked);
|
|
1207
|
+
renderGitWorkspace(state.viewModel || {});
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
var typeLabel = gitTreeTypeLabel(node);
|
|
1211
|
+
var type = null;
|
|
1212
|
+
if (typeLabel) {
|
|
1213
|
+
type = document.createElement("span");
|
|
1214
|
+
type.className = "git-file-type" + (typeLabel === "staged" ? " staged" : "");
|
|
1215
|
+
type.textContent = typeLabel;
|
|
1216
|
+
} else {
|
|
1217
|
+
row.classList.add("without-type");
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
var pathText = document.createElement("span");
|
|
1221
|
+
pathText.className = "git-file-path";
|
|
1222
|
+
pathText.textContent = gitTreeDisplayLabel(node);
|
|
1223
|
+
|
|
1224
|
+
var meta = document.createElement("span");
|
|
1225
|
+
meta.className = "git-file-meta";
|
|
1226
|
+
meta.textContent = node.kind === "file" ? gitFileMeta(node.file) : paths.length + " file" + (paths.length === 1 ? "" : "s");
|
|
1227
|
+
|
|
1228
|
+
row.append(checkbox);
|
|
1229
|
+
if (type) row.append(type);
|
|
1230
|
+
row.append(pathText, meta);
|
|
1231
|
+
if (node.kind === "file" && node.path) {
|
|
1232
|
+
var diffButton = document.createElement("button");
|
|
1233
|
+
diffButton.type = "button";
|
|
1234
|
+
diffButton.className = "git-diff-open";
|
|
1235
|
+
diffButton.textContent = "Diff";
|
|
1236
|
+
diffButton.setAttribute("aria-label", "Open diff for " + node.path);
|
|
1237
|
+
diffButton.addEventListener("click", function (event) {
|
|
1238
|
+
if (event && typeof event.preventDefault === "function") event.preventDefault();
|
|
1239
|
+
if (event && typeof event.stopPropagation === "function") event.stopPropagation();
|
|
1240
|
+
openGitDiff(node.path);
|
|
1241
|
+
});
|
|
1242
|
+
row.append(diffButton);
|
|
1243
|
+
}
|
|
1244
|
+
if (node.kind === "file") {
|
|
1245
|
+
return row;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
var container = document.createElement("div");
|
|
1249
|
+
container.className = "git-tree-node";
|
|
1250
|
+
container.append(row);
|
|
1251
|
+
node.children.forEach(function (child) {
|
|
1252
|
+
container.append(renderGitTreeNode(child, depth + 1));
|
|
1253
|
+
});
|
|
1254
|
+
return container;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function gitTreeTypeLabel(node) {
|
|
1258
|
+
if (node.kind === "group") return node.rootKey;
|
|
1259
|
+
if (node.kind === "dir") return "";
|
|
1260
|
+
if (node.file && isGitFileStaged(node.file) && !needsGitFileStage(node.file)) return "staged";
|
|
1261
|
+
return (node.file && (node.file.type || node.file.xy)) || "changed";
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function gitTreeDisplayLabel(node) {
|
|
1265
|
+
if (node.kind !== "file") return node.label;
|
|
1266
|
+
var file = node.file || {};
|
|
1267
|
+
return file.originalPath || file.originalFile
|
|
1268
|
+
? (file.originalPath || file.originalFile) + " -> " + (node.path || "")
|
|
1269
|
+
: (node.path || node.label);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
function gitFileMeta(file) {
|
|
1273
|
+
file = file || {};
|
|
1274
|
+
if (file.type === "untracked" || file.xy === "??") return "untracked";
|
|
1275
|
+
if (isGitFileStaged(file) && needsGitFileStage(file)) return "staged+unstaged";
|
|
1276
|
+
if (isGitFileStaged(file)) return "staged";
|
|
1277
|
+
if (needsGitFileStage(file)) return "unstaged";
|
|
1278
|
+
return (file.xy || "").trim() || "changed";
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function gitTreePaths(node) {
|
|
1282
|
+
if (node.kind === "file") return node.path ? [node.path] : [];
|
|
1283
|
+
return node.children.reduce(function (paths, child) {
|
|
1284
|
+
return paths.concat(gitTreePaths(child));
|
|
1285
|
+
}, []);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function setGitSelection(paths, selected) {
|
|
1289
|
+
if (selected) {
|
|
1290
|
+
paths.forEach(function (filePath) {
|
|
1291
|
+
if (state.gitSelectedPaths.indexOf(filePath) === -1) {
|
|
1292
|
+
state.gitSelectedPaths.push(filePath);
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
state.gitSelectedPaths = state.gitSelectedPaths.filter(function (filePath) {
|
|
1298
|
+
return paths.indexOf(filePath) === -1;
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
function selectedGitPaths() {
|
|
1303
|
+
return state.gitSelectedPaths.slice();
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function gitChangedFiles() {
|
|
1307
|
+
var git = gitWorkspace(state.viewModel || {});
|
|
1308
|
+
return Array.isArray(git.changedFiles) ? git.changedFiles : [];
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function gitFilePath(file) {
|
|
1312
|
+
return file && (file.path || file.file) || "";
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function findGitChangedFile(filePath) {
|
|
1316
|
+
return gitChangedFiles().find(function (file) {
|
|
1317
|
+
return gitFilePath(file) === filePath || file.file === filePath;
|
|
1318
|
+
}) || null;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function openGitDiff(filePath) {
|
|
1322
|
+
state.gitDiff.open = true;
|
|
1323
|
+
state.gitDiff.selectedPath = filePath;
|
|
1324
|
+
state.gitDiff.error = null;
|
|
1325
|
+
state.gitDiff.diff = null;
|
|
1326
|
+
fetchGitDiff();
|
|
1327
|
+
renderGitDiffDrawer(state.viewModel || {});
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function closeGitDiff() {
|
|
1331
|
+
state.gitDiff.open = false;
|
|
1332
|
+
state.gitDiff.requestId += 1;
|
|
1333
|
+
state.gitDiff.loading = false;
|
|
1334
|
+
renderGitDiffDrawer(state.viewModel || {});
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
function setGitDiffMode(mode) {
|
|
1338
|
+
if (state.gitDiff.mode === mode) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
state.gitDiff.mode = mode;
|
|
1342
|
+
state.gitDiff.error = null;
|
|
1343
|
+
state.gitDiff.diff = null;
|
|
1344
|
+
if (state.gitDiff.open && state.gitDiff.selectedPath) {
|
|
1345
|
+
fetchGitDiff();
|
|
1346
|
+
}
|
|
1347
|
+
renderGitDiffDrawer(state.viewModel || {});
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
function gitDiffApiUrl(filePath, mode) {
|
|
1351
|
+
var params = new URLSearchParams();
|
|
1352
|
+
params.set("path", filePath);
|
|
1353
|
+
params.set("mode", mode);
|
|
1354
|
+
return "/__agentweaver/api/git/diff?" + params.toString();
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
function fetchGitDiff() {
|
|
1358
|
+
var filePath = state.gitDiff.selectedPath;
|
|
1359
|
+
if (!filePath) {
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
var requestId = state.gitDiff.requestId + 1;
|
|
1363
|
+
state.gitDiff.requestId = requestId;
|
|
1364
|
+
state.gitDiff.loading = true;
|
|
1365
|
+
state.gitDiff.error = null;
|
|
1366
|
+
state.gitDiff.diff = null;
|
|
1367
|
+
fetch(gitDiffApiUrl(filePath, state.gitDiff.mode))
|
|
1368
|
+
.then(function (response) {
|
|
1369
|
+
return response.json().then(function (body) {
|
|
1370
|
+
if (!response.ok) {
|
|
1371
|
+
throw new Error(body && body.message ? body.message : "Git diff request failed.");
|
|
1372
|
+
}
|
|
1373
|
+
return body;
|
|
1374
|
+
});
|
|
1375
|
+
})
|
|
1376
|
+
.then(function (diff) {
|
|
1377
|
+
if (requestId !== state.gitDiff.requestId) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
state.gitDiff.loading = false;
|
|
1381
|
+
state.gitDiff.diff = diff;
|
|
1382
|
+
state.gitDiff.error = null;
|
|
1383
|
+
renderGitDiffDrawer(state.viewModel || {});
|
|
1384
|
+
})
|
|
1385
|
+
.catch(function (error) {
|
|
1386
|
+
if (requestId !== state.gitDiff.requestId) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
state.gitDiff.loading = false;
|
|
1390
|
+
state.gitDiff.diff = null;
|
|
1391
|
+
state.gitDiff.error = error.message || "Git diff request failed.";
|
|
1392
|
+
renderGitDiffDrawer(state.viewModel || {});
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
function renderGitDiffDrawer(vm) {
|
|
1397
|
+
var files = gitChangedFiles();
|
|
1398
|
+
var selectedPath = state.gitDiff.selectedPath;
|
|
1399
|
+
var selectedPathChanged = false;
|
|
1400
|
+
if (selectedPath && !findGitChangedFile(selectedPath)) {
|
|
1401
|
+
selectedPath = files[0] ? gitFilePath(files[0]) : null;
|
|
1402
|
+
state.gitDiff.selectedPath = selectedPath;
|
|
1403
|
+
state.gitDiff.diff = null;
|
|
1404
|
+
state.gitDiff.error = null;
|
|
1405
|
+
state.gitDiff.loading = false;
|
|
1406
|
+
selectedPathChanged = Boolean(selectedPath);
|
|
1407
|
+
}
|
|
1408
|
+
elements.gitDiffDrawer.hidden = !state.gitDiff.open;
|
|
1409
|
+
elements.gitDiffTitle.textContent = "Git Diff Viewer";
|
|
1410
|
+
elements.gitDiffMeta.textContent = files.length === 0
|
|
1411
|
+
? "No changed files are available."
|
|
1412
|
+
: String(files.length) + " changed file" + (files.length === 1 ? "" : "s") + ".";
|
|
1413
|
+
if (!state.gitDiff.open) {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (selectedPathChanged) {
|
|
1417
|
+
fetchGitDiff();
|
|
1418
|
+
}
|
|
1419
|
+
renderGitDiffFileList(files);
|
|
1420
|
+
renderGitDiffModeControls();
|
|
1421
|
+
renderGitDiffPreview(vm);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function renderGitDiffFileList(files) {
|
|
1425
|
+
elements.gitDiffFileList.innerHTML = "";
|
|
1426
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
1427
|
+
var empty = document.createElement("div");
|
|
1428
|
+
empty.className = "git-diff-empty";
|
|
1429
|
+
empty.textContent = "No changed files.";
|
|
1430
|
+
elements.gitDiffFileList.append(empty);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
files.forEach(function (file) {
|
|
1434
|
+
var filePath = gitFilePath(file);
|
|
1435
|
+
var button = document.createElement("button");
|
|
1436
|
+
button.type = "button";
|
|
1437
|
+
button.className = "git-diff-file" + (filePath === state.gitDiff.selectedPath ? " selected" : "");
|
|
1438
|
+
button.dataset.path = filePath;
|
|
1439
|
+
button.setAttribute("aria-pressed", filePath === state.gitDiff.selectedPath ? "true" : "false");
|
|
1440
|
+
button.addEventListener("click", function () {
|
|
1441
|
+
if (state.gitDiff.selectedPath !== filePath) {
|
|
1442
|
+
openGitDiff(filePath);
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
var title = document.createElement("strong");
|
|
1446
|
+
title.textContent = file.originalPath || file.originalFile
|
|
1447
|
+
? (file.originalPath || file.originalFile) + " -> " + filePath
|
|
1448
|
+
: filePath;
|
|
1449
|
+
var meta = document.createElement("span");
|
|
1450
|
+
meta.textContent = gitFileMeta(file);
|
|
1451
|
+
button.append(title, meta);
|
|
1452
|
+
elements.gitDiffFileList.append(button);
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function renderGitDiffModeControls() {
|
|
1457
|
+
ensureGitDiffModeButtons();
|
|
1458
|
+
Array.prototype.slice.call(elements.gitDiffModeControls.querySelectorAll("button")).forEach(function (button) {
|
|
1459
|
+
var mode = button.dataset.gitDiffMode || "head";
|
|
1460
|
+
var active = state.gitDiff.mode === mode;
|
|
1461
|
+
button.className = active ? "primary" : "";
|
|
1462
|
+
button.setAttribute("aria-pressed", active ? "true" : "false");
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function ensureGitDiffModeButtons() {
|
|
1467
|
+
if (elements.gitDiffModeControls.querySelectorAll("button").length > 0) {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
[
|
|
1471
|
+
["head", "All"],
|
|
1472
|
+
["staged", "Staged"],
|
|
1473
|
+
["worktree", "Unstaged"],
|
|
1474
|
+
].forEach(function (entry) {
|
|
1475
|
+
var button = document.createElement("button");
|
|
1476
|
+
button.type = "button";
|
|
1477
|
+
button.dataset.gitDiffMode = entry[0];
|
|
1478
|
+
button.textContent = entry[1];
|
|
1479
|
+
elements.gitDiffModeControls.append(button);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function renderGitDiffPreview(vm) {
|
|
1484
|
+
var selected = state.gitDiff.selectedPath ? findGitChangedFile(state.gitDiff.selectedPath) : null;
|
|
1485
|
+
if (!selected) {
|
|
1486
|
+
elements.gitDiffSelectedTitle.textContent = "Diff";
|
|
1487
|
+
elements.gitDiffSelectedMeta.textContent = "No file selected.";
|
|
1488
|
+
elements.gitDiffStatus.textContent = "";
|
|
1489
|
+
renderGitDiffMessage("Select a changed file to inspect its diff.", false);
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
var displayPath = selected.originalPath || selected.originalFile
|
|
1493
|
+
? (selected.originalPath || selected.originalFile) + " -> " + gitFilePath(selected)
|
|
1494
|
+
: gitFilePath(selected);
|
|
1495
|
+
elements.gitDiffSelectedTitle.textContent = displayPath;
|
|
1496
|
+
elements.gitDiffSelectedMeta.textContent = [gitFileMeta(selected), gitModeLabel(state.gitDiff.mode)].join(" | ");
|
|
1497
|
+
if (state.gitDiff.loading) {
|
|
1498
|
+
elements.gitDiffStatus.textContent = "Loading diff...";
|
|
1499
|
+
renderGitDiffMessage("Loading diff...", false);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
if (state.gitDiff.error) {
|
|
1503
|
+
elements.gitDiffStatus.textContent = state.gitDiff.error;
|
|
1504
|
+
renderGitDiffMessage(state.gitDiff.error, true);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
var diff = state.gitDiff.diff;
|
|
1508
|
+
if (!diff) {
|
|
1509
|
+
elements.gitDiffStatus.textContent = "Diff has not been loaded yet.";
|
|
1510
|
+
renderGitDiffMessage("Diff has not been loaded yet.", false);
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
elements.gitDiffStatus.textContent = diff.message || "";
|
|
1514
|
+
renderGitDiffContent(diff);
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
function gitModeLabel(mode) {
|
|
1518
|
+
if (mode === "staged") return "Staged";
|
|
1519
|
+
if (mode === "worktree") return "Unstaged";
|
|
1520
|
+
return "All";
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function renderGitDiffMessage(message, failed) {
|
|
1524
|
+
elements.gitDiffBody.textContent = "";
|
|
1525
|
+
elements.gitDiffBody.className = "git-diff-body" + (failed ? " error-text" : "");
|
|
1526
|
+
elements.gitDiffBody.textContent = message;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
function renderGitDiffContent(diff) {
|
|
1530
|
+
elements.gitDiffBody.textContent = "";
|
|
1531
|
+
elements.gitDiffBody.className = "git-diff-body";
|
|
1532
|
+
if (diff.binary) {
|
|
1533
|
+
renderGitDiffMessage(diff.message || "Binary file diff is not displayed.", false);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (diff.tooLarge) {
|
|
1537
|
+
renderGitDiffMessage(diff.message || "Diff is too large to display.", false);
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
if (diff.empty || !Array.isArray(diff.hunks) || diff.hunks.length === 0) {
|
|
1541
|
+
renderGitDiffMessage(diff.message || "No diff is available for this mode.", false);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
var table = document.createElement("div");
|
|
1545
|
+
table.className = "git-diff-table";
|
|
1546
|
+
diff.hunks.forEach(function (hunk) {
|
|
1547
|
+
var hunkRow = document.createElement("div");
|
|
1548
|
+
hunkRow.className = "git-diff-hunk";
|
|
1549
|
+
hunkRow.textContent = hunk.header || "@@";
|
|
1550
|
+
table.append(hunkRow);
|
|
1551
|
+
(Array.isArray(hunk.rows) ? hunk.rows : []).forEach(function (row) {
|
|
1552
|
+
table.append(renderGitDiffRow(row));
|
|
1553
|
+
});
|
|
1554
|
+
});
|
|
1555
|
+
elements.gitDiffBody.append(table);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function renderGitDiffRow(row) {
|
|
1559
|
+
var element = document.createElement("div");
|
|
1560
|
+
var kind = row && row.kind ? row.kind : "context";
|
|
1561
|
+
element.className = "git-diff-row row-" + kind;
|
|
1562
|
+
var leftNumber = document.createElement("span");
|
|
1563
|
+
leftNumber.className = "git-diff-line-number";
|
|
1564
|
+
leftNumber.textContent = row.leftLineNumber === null || row.leftLineNumber === undefined ? "" : String(row.leftLineNumber);
|
|
1565
|
+
var leftText = document.createElement("code");
|
|
1566
|
+
leftText.className = "git-diff-code left";
|
|
1567
|
+
leftText.textContent = row.leftText || "";
|
|
1568
|
+
var rightNumber = document.createElement("span");
|
|
1569
|
+
rightNumber.className = "git-diff-line-number";
|
|
1570
|
+
rightNumber.textContent = row.rightLineNumber === null || row.rightLineNumber === undefined ? "" : String(row.rightLineNumber);
|
|
1571
|
+
var rightText = document.createElement("code");
|
|
1572
|
+
rightText.className = "git-diff-code right";
|
|
1573
|
+
rightText.textContent = row.rightText || "";
|
|
1574
|
+
element.append(leftNumber, leftText, rightNumber, rightText);
|
|
1575
|
+
return element;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
function selectedStageableGitPaths(git) {
|
|
1579
|
+
return selectedGitPathsFor(git, needsGitFileStage);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
function selectedUnstageableGitPaths(git) {
|
|
1583
|
+
return selectedGitPathsFor(git, isGitFileStaged);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
function selectedGitPathsFor(git, predicate) {
|
|
1587
|
+
git = git || gitWorkspace(state.viewModel || {});
|
|
1588
|
+
var selected = selectedGitPaths();
|
|
1589
|
+
var selectedSet = new Set(selected);
|
|
1590
|
+
var files = Array.isArray(git.changedFiles) ? git.changedFiles : [];
|
|
1591
|
+
return files.filter(function (file) {
|
|
1592
|
+
var filePath = file.path || file.file || "";
|
|
1593
|
+
return selectedSet.has(filePath) && predicate(file);
|
|
1594
|
+
}).map(function (file) {
|
|
1595
|
+
return file.path || file.file || "";
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function needsGitFileStage(file) {
|
|
1600
|
+
if (!file) return false;
|
|
1601
|
+
if (file.type === "untracked" || file.xy === "??") return true;
|
|
1602
|
+
var workTreeStatus = typeof file.workTreeStatus === "string" ? file.workTreeStatus : (file.xy || " ")[1];
|
|
1603
|
+
return workTreeStatus !== " " && workTreeStatus !== undefined;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
function isGitFileStaged(file) {
|
|
1607
|
+
if (!file) return false;
|
|
1608
|
+
var indexStatus = typeof file.indexStatus === "string" ? file.indexStatus : (file.xy || " ")[0];
|
|
1609
|
+
return indexStatus !== " " && indexStatus !== "?";
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function renderProgress(vm) {
|
|
1613
|
+
var progress = vm && vm.progress;
|
|
1614
|
+
var items = progress && Array.isArray(progress.items) ? progress.items.filter(isProgressItem) : [];
|
|
1615
|
+
renderProgressFlowLabel(progress && progress.flow ? text(progress.flow.label, progress.flow.id || "") : "");
|
|
1616
|
+
if (!progress || !progress.flow || items.length === 0) {
|
|
1617
|
+
var fallbackText = text(vm && vm.progressText, "No progress yet.");
|
|
1618
|
+
elements.progress.className = "progress-tree fallback";
|
|
1619
|
+
if (elements.progress.children.length > 0) {
|
|
1620
|
+
var previousFallbackScrollTop = elements.progress.scrollTop;
|
|
1621
|
+
var fallbackWasPinned = previousFallbackScrollTop + elements.progress.clientHeight >= elements.progress.scrollHeight - 8;
|
|
1622
|
+
elements.progress.textContent = fallbackText;
|
|
1623
|
+
elements.progress.scrollTop = fallbackWasPinned ? elements.progress.scrollHeight : previousFallbackScrollTop;
|
|
1624
|
+
} else {
|
|
1625
|
+
setTextPreservingScroll(elements.progress, fallbackText);
|
|
1626
|
+
}
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
var previousScrollTop = elements.progress.scrollTop;
|
|
1631
|
+
var wasPinned = previousScrollTop + elements.progress.clientHeight >= elements.progress.scrollHeight - 8;
|
|
1632
|
+
elements.progress.className = "progress-tree";
|
|
1633
|
+
elements.progress.innerHTML = "";
|
|
1634
|
+
|
|
1635
|
+
items.forEach(function (item, index) {
|
|
1636
|
+
elements.progress.append(renderProgressRow(item, index));
|
|
1637
|
+
});
|
|
1638
|
+
elements.progress.scrollTop = wasPinned ? elements.progress.scrollHeight : previousScrollTop;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
function renderProgressFlowLabel(label) {
|
|
1642
|
+
if (!elements.progressFlowLabel) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
var value = text(label, "");
|
|
1646
|
+
elements.progressFlowLabel.textContent = value;
|
|
1647
|
+
elements.progressFlowLabel.hidden = value.length === 0;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function isProgressItem(item) {
|
|
1651
|
+
if (!item || typeof item !== "object") return false;
|
|
1652
|
+
if (["group", "phase", "step", "slot", "block", "termination"].indexOf(item.kind) === -1) return false;
|
|
1653
|
+
if (["pending", "running", "done", "success", "failed", "stopped", "skipped", "waiting-user", "disabled", "blocked", "invalid", "empty"].indexOf(item.status) === -1) return false;
|
|
1654
|
+
return typeof item.label === "string" && Number.isFinite(item.depth);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
function renderProgressRow(item, index) {
|
|
1658
|
+
var depth = Math.max(0, Math.min(12, Math.floor(item.depth)));
|
|
1659
|
+
var row = document.createElement("div");
|
|
1660
|
+
row.className = "progress-row kind-" + item.kind + " status-" + item.status;
|
|
1661
|
+
row.dataset.kind = item.kind;
|
|
1662
|
+
row.dataset.status = item.status;
|
|
1663
|
+
row.dataset.depth = String(depth);
|
|
1664
|
+
row.setAttribute("role", "treeitem");
|
|
1665
|
+
row.setAttribute("aria-level", String(depth + 1));
|
|
1666
|
+
row.setAttribute("aria-current", item.status === "running" ? "step" : "false");
|
|
1667
|
+
row.style.paddingLeft = String(8 + depth * 18) + "px";
|
|
1668
|
+
row.title = item.kind + ": " + item.status;
|
|
1669
|
+
|
|
1670
|
+
var marker = document.createElement("span");
|
|
1671
|
+
marker.className = "progress-marker";
|
|
1672
|
+
marker.setAttribute("aria-hidden", "true");
|
|
1673
|
+
marker.textContent = progressMarker(item.status);
|
|
1674
|
+
|
|
1675
|
+
var body = document.createElement("span");
|
|
1676
|
+
body.className = "progress-row-body";
|
|
1677
|
+
|
|
1678
|
+
var label = document.createElement("span");
|
|
1679
|
+
label.className = "progress-label";
|
|
1680
|
+
label.textContent = text(item.label, "Untitled progress item");
|
|
1681
|
+
body.append(label);
|
|
1682
|
+
|
|
1683
|
+
if (typeof item.detail === "string" && item.detail.length > 0) {
|
|
1684
|
+
var detail = document.createElement("span");
|
|
1685
|
+
detail.className = "progress-detail";
|
|
1686
|
+
detail.textContent = item.detail;
|
|
1687
|
+
body.append(detail);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
row.append(marker, body);
|
|
1691
|
+
row.dataset.index = String(index);
|
|
1692
|
+
return row;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
function progressMarker(status) {
|
|
1696
|
+
if (status === "done" || status === "success") return "✓";
|
|
1697
|
+
if (status === "failed" || status === "invalid") return "×";
|
|
1698
|
+
if (status === "stopped" || status === "blocked") return "■";
|
|
1699
|
+
if (status === "running" || status === "waiting-user") return "●";
|
|
1700
|
+
if (status === "skipped") return "↷";
|
|
1701
|
+
if (status === "disabled" || status === "empty") return "·";
|
|
1702
|
+
return "○";
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
function setTextPreservingScroll(element, value, forceBottom) {
|
|
246
1706
|
if (element.textContent === value) {
|
|
1707
|
+
if (forceBottom) {
|
|
1708
|
+
element.scrollTop = element.scrollHeight;
|
|
1709
|
+
}
|
|
247
1710
|
return;
|
|
248
1711
|
}
|
|
249
1712
|
var previousScrollTop = element.scrollTop;
|
|
250
1713
|
var wasPinned = previousScrollTop + element.clientHeight >= element.scrollHeight - 8;
|
|
251
1714
|
element.textContent = value;
|
|
252
|
-
element.scrollTop = wasPinned ? element.scrollHeight : previousScrollTop;
|
|
1715
|
+
element.scrollTop = forceBottom || wasPinned ? element.scrollHeight : previousScrollTop;
|
|
253
1716
|
}
|
|
254
1717
|
|
|
255
1718
|
function captureUiState() {
|
|
@@ -265,7 +1728,6 @@
|
|
|
265
1728
|
}
|
|
266
1729
|
return {
|
|
267
1730
|
progressScrollTop: elements.progress.scrollTop,
|
|
268
|
-
summaryScrollTop: elements.summary.scrollTop,
|
|
269
1731
|
logScrollTop: elements.log.scrollTop,
|
|
270
1732
|
helpScrollTop: elements.helpText.scrollTop,
|
|
271
1733
|
modalScrollTop: elements.modalRoot.scrollTop,
|
|
@@ -281,8 +1743,7 @@
|
|
|
281
1743
|
function restoreUiState(uiState) {
|
|
282
1744
|
if (!uiState) return;
|
|
283
1745
|
elements.progress.scrollTop = uiState.progressScrollTop;
|
|
284
|
-
elements.
|
|
285
|
-
elements.log.scrollTop = uiState.logScrollTop;
|
|
1746
|
+
elements.log.scrollTop = state.logAutoscroll ? elements.log.scrollHeight : uiState.logScrollTop;
|
|
286
1747
|
elements.helpText.scrollTop = uiState.helpScrollTop;
|
|
287
1748
|
elements.modalRoot.scrollTop = uiState.modalScrollTop;
|
|
288
1749
|
var modalBody = currentModalBody();
|
|
@@ -446,9 +1907,9 @@
|
|
|
446
1907
|
parts.push("Scope " + explorer.scopeKey);
|
|
447
1908
|
}
|
|
448
1909
|
if (Array.isArray(explorer.runIds) && explorer.runIds.length > 1) {
|
|
449
|
-
parts.push("
|
|
1910
|
+
parts.push("Current runs " + explorer.runIds.join(", "));
|
|
450
1911
|
} else if (explorer.runId) {
|
|
451
|
-
parts.push("
|
|
1912
|
+
parts.push("Current run " + explorer.runId);
|
|
452
1913
|
}
|
|
453
1914
|
return parts.join(" | ");
|
|
454
1915
|
}
|
|
@@ -457,7 +1918,7 @@
|
|
|
457
1918
|
if (typeof count !== "number") {
|
|
458
1919
|
return "Artifacts are available.";
|
|
459
1920
|
}
|
|
460
|
-
return String(count) + " artifact" + (count === 1 ? "" : "s") + "
|
|
1921
|
+
return String(count) + " artifact" + (count === 1 ? "" : "s") + " in scope.";
|
|
461
1922
|
}
|
|
462
1923
|
|
|
463
1924
|
function artifactApiUrl(explorer, suffix) {
|
|
@@ -466,13 +1927,6 @@
|
|
|
466
1927
|
if (explorer.scopeKey) {
|
|
467
1928
|
params.set("scope", explorer.scopeKey);
|
|
468
1929
|
}
|
|
469
|
-
if (Array.isArray(explorer.runIds) && explorer.runIds.length > 0) {
|
|
470
|
-
explorer.runIds.forEach(function (runId) {
|
|
471
|
-
params.append("runId", runId);
|
|
472
|
-
});
|
|
473
|
-
} else if (explorer.runId) {
|
|
474
|
-
params.set("runId", explorer.runId);
|
|
475
|
-
}
|
|
476
1930
|
var query = params.toString();
|
|
477
1931
|
return query ? base + "?" + query : base;
|
|
478
1932
|
}
|
|
@@ -483,6 +1937,7 @@
|
|
|
483
1937
|
return {
|
|
484
1938
|
title: group.title || group.phaseId || "Artifacts",
|
|
485
1939
|
items: Array.isArray(group.items) ? group.items : [],
|
|
1940
|
+
groups: Array.isArray(group.groups) ? artifactGroups({ groups: group.groups }) : [],
|
|
486
1941
|
};
|
|
487
1942
|
});
|
|
488
1943
|
}
|
|
@@ -493,8 +1948,23 @@
|
|
|
493
1948
|
}
|
|
494
1949
|
|
|
495
1950
|
function flattenArtifacts(catalog) {
|
|
1951
|
+
function flattenGroup(group) {
|
|
1952
|
+
var nested = Array.isArray(group.groups) ? group.groups.reduce(function (items, child) {
|
|
1953
|
+
return items.concat(flattenGroup(child));
|
|
1954
|
+
}, []) : [];
|
|
1955
|
+
var ownItems = Array.isArray(group.items) ? group.items : [];
|
|
1956
|
+
if (nested.length > 0) {
|
|
1957
|
+
var nestedIds = new Set(nested.map(function (item) {
|
|
1958
|
+
return item && item.id;
|
|
1959
|
+
}));
|
|
1960
|
+
ownItems = ownItems.filter(function (item) {
|
|
1961
|
+
return item && !nestedIds.has(item.id);
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
return nested.concat(ownItems);
|
|
1965
|
+
}
|
|
496
1966
|
return artifactGroups(catalog).reduce(function (items, group) {
|
|
497
|
-
return items.concat(group
|
|
1967
|
+
return items.concat(flattenGroup(group));
|
|
498
1968
|
}, []);
|
|
499
1969
|
}
|
|
500
1970
|
|
|
@@ -535,14 +2005,23 @@
|
|
|
535
2005
|
return item && item.kind ? String(item.kind) : "unknown";
|
|
536
2006
|
}
|
|
537
2007
|
|
|
538
|
-
function
|
|
2008
|
+
function currentArtifactRunIds(explorer) {
|
|
2009
|
+
if (explorer && Array.isArray(explorer.runIds) && explorer.runIds.length > 0) {
|
|
2010
|
+
return explorer.runIds.filter(Boolean);
|
|
2011
|
+
}
|
|
2012
|
+
return explorer && explorer.runId ? [explorer.runId] : [];
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
function chooseDefaultArtifact(items, explorer) {
|
|
539
2016
|
if (!Array.isArray(items) || items.length === 0) {
|
|
540
2017
|
return null;
|
|
541
2018
|
}
|
|
2019
|
+
var currentRunIds = new Set(currentArtifactRunIds(explorer));
|
|
542
2020
|
var best = null;
|
|
543
2021
|
var bestScore = Infinity;
|
|
544
2022
|
items.forEach(function (item, index) {
|
|
545
|
-
var
|
|
2023
|
+
var runScore = currentRunIds.size > 0 && item && currentRunIds.has(item.runId) ? 0 : 1;
|
|
2024
|
+
var score = runScore * 100000 + artifactUsefulnessScore(item) * 1000 + index;
|
|
546
2025
|
if (score < bestScore) {
|
|
547
2026
|
best = item;
|
|
548
2027
|
bestScore = score;
|
|
@@ -603,7 +2082,7 @@
|
|
|
603
2082
|
return item && item.id === state.artifacts.selectedId;
|
|
604
2083
|
});
|
|
605
2084
|
if (!selectedStillExists) {
|
|
606
|
-
var defaultItem = chooseDefaultArtifact(items);
|
|
2085
|
+
var defaultItem = chooseDefaultArtifact(items, explorer);
|
|
607
2086
|
state.artifacts.selectedId = defaultItem ? defaultItem.id : null;
|
|
608
2087
|
state.artifacts.preview = null;
|
|
609
2088
|
if (defaultItem) {
|
|
@@ -640,26 +2119,48 @@
|
|
|
640
2119
|
var groups = artifactGroups(catalog);
|
|
641
2120
|
var rendered = 0;
|
|
642
2121
|
groups.forEach(function (group) {
|
|
643
|
-
var
|
|
644
|
-
|
|
645
|
-
|
|
2122
|
+
var renderedGroup = renderArtifactGroup(explorer, group, 0);
|
|
2123
|
+
rendered += renderedGroup.count;
|
|
2124
|
+
if (renderedGroup.element) {
|
|
2125
|
+
elements.artifactList.append(renderedGroup.element);
|
|
646
2126
|
}
|
|
647
|
-
var section = document.createElement("section");
|
|
648
|
-
section.className = "artifact-group";
|
|
649
|
-
var title = document.createElement("h3");
|
|
650
|
-
title.textContent = group.title || "Artifacts";
|
|
651
|
-
section.append(title);
|
|
652
|
-
items.forEach(function (item) {
|
|
653
|
-
rendered += 1;
|
|
654
|
-
section.append(renderArtifactRow(explorer, item));
|
|
655
|
-
});
|
|
656
|
-
elements.artifactList.append(section);
|
|
657
2127
|
});
|
|
658
2128
|
if (rendered === 0) {
|
|
659
|
-
elements.artifactList.append(artifactEmpty("No artifacts were found for the current scope
|
|
2129
|
+
elements.artifactList.append(artifactEmpty("No artifacts were found for the current scope."));
|
|
660
2130
|
}
|
|
661
2131
|
}
|
|
662
2132
|
|
|
2133
|
+
function renderArtifactGroup(explorer, group, depth) {
|
|
2134
|
+
var childGroups = Array.isArray(group.groups) ? group.groups : [];
|
|
2135
|
+
var nestedIds = new Set();
|
|
2136
|
+
childGroups.forEach(function (child) {
|
|
2137
|
+
(Array.isArray(child.items) ? child.items : []).forEach(function (item) {
|
|
2138
|
+
if (item && item.id) nestedIds.add(item.id);
|
|
2139
|
+
});
|
|
2140
|
+
});
|
|
2141
|
+
var items = (Array.isArray(group.items) ? group.items : []).filter(function (item) {
|
|
2142
|
+
return item && !nestedIds.has(item.id);
|
|
2143
|
+
});
|
|
2144
|
+
var section = document.createElement("section");
|
|
2145
|
+
section.className = depth > 0 ? "artifact-group artifact-subgroup" : "artifact-group";
|
|
2146
|
+
var title = document.createElement(depth > 0 ? "h4" : "h3");
|
|
2147
|
+
title.textContent = group.title || "Artifacts";
|
|
2148
|
+
section.append(title);
|
|
2149
|
+
var count = 0;
|
|
2150
|
+
childGroups.forEach(function (child) {
|
|
2151
|
+
var renderedChild = renderArtifactGroup(explorer, child, depth + 1);
|
|
2152
|
+
count += renderedChild.count;
|
|
2153
|
+
if (renderedChild.element) {
|
|
2154
|
+
section.append(renderedChild.element);
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
items.forEach(function (item) {
|
|
2158
|
+
count += 1;
|
|
2159
|
+
section.append(renderArtifactRow(explorer, item));
|
|
2160
|
+
});
|
|
2161
|
+
return { element: count > 0 ? section : null, count: count };
|
|
2162
|
+
}
|
|
2163
|
+
|
|
663
2164
|
function renderArtifactRow(explorer, item) {
|
|
664
2165
|
var row = document.createElement("article");
|
|
665
2166
|
row.className = "artifact-row" + (state.artifacts.selectedId === item.id ? " selected" : "");
|
|
@@ -1433,6 +2934,8 @@
|
|
|
1433
2934
|
checkRow.className = "check-row";
|
|
1434
2935
|
var checkbox = document.createElement("input");
|
|
1435
2936
|
checkbox.type = "checkbox";
|
|
2937
|
+
checkbox.id = "field-" + field.id;
|
|
2938
|
+
checkbox.name = field.id;
|
|
1436
2939
|
checkbox.dataset.fieldId = field.id;
|
|
1437
2940
|
checkbox.dataset.fieldType = field.type;
|
|
1438
2941
|
checkbox.checked = currentValue(field) === true;
|
|
@@ -1450,6 +2953,7 @@
|
|
|
1450
2953
|
var input = document.createElement(field.multiline ? "textarea" : "input");
|
|
1451
2954
|
input.id = "field-" + field.id;
|
|
1452
2955
|
if (!field.multiline) input.type = "text";
|
|
2956
|
+
input.name = field.id;
|
|
1453
2957
|
if (field.placeholder) input.placeholder = field.placeholder;
|
|
1454
2958
|
if (field.rows) input.rows = field.rows;
|
|
1455
2959
|
input.dataset.fieldId = field.id;
|
|
@@ -1485,6 +2989,7 @@
|
|
|
1485
2989
|
var input = document.createElement("input");
|
|
1486
2990
|
input.type = "radio";
|
|
1487
2991
|
input.name = "field-" + field.id;
|
|
2992
|
+
input.id = controlId("field", field.id, option.value);
|
|
1488
2993
|
input.value = option.value;
|
|
1489
2994
|
input.dataset.fieldId = field.id;
|
|
1490
2995
|
input.dataset.fieldType = field.type;
|
|
@@ -1515,6 +3020,8 @@
|
|
|
1515
3020
|
label.className = "field-option";
|
|
1516
3021
|
var input = document.createElement("input");
|
|
1517
3022
|
input.type = "checkbox";
|
|
3023
|
+
input.id = controlId("field", field.id, option.value);
|
|
3024
|
+
input.name = field.id;
|
|
1518
3025
|
input.value = option.value;
|
|
1519
3026
|
input.dataset.fieldId = field.id;
|
|
1520
3027
|
input.dataset.fieldType = field.type;
|
|
@@ -1551,6 +3058,36 @@
|
|
|
1551
3058
|
return Object.assign({}, state.formValues);
|
|
1552
3059
|
}
|
|
1553
3060
|
|
|
3061
|
+
applyTheme(state.theme);
|
|
3062
|
+
if (elements.themeToggle) {
|
|
3063
|
+
elements.themeToggle.addEventListener("click", toggleTheme);
|
|
3064
|
+
}
|
|
3065
|
+
if (elements.autoFlowResizer) {
|
|
3066
|
+
elements.autoFlowResizer.setAttribute("role", "separator");
|
|
3067
|
+
elements.autoFlowResizer.setAttribute("aria-orientation", "horizontal");
|
|
3068
|
+
elements.autoFlowResizer.setAttribute("aria-label", "Resize flow editor");
|
|
3069
|
+
elements.autoFlowResizer.setAttribute("tabindex", "0");
|
|
3070
|
+
elements.autoFlowResizer.addEventListener("pointerdown", beginAutoFlowResize);
|
|
3071
|
+
elements.autoFlowResizer.addEventListener("dblclick", resetAutoFlowHeight);
|
|
3072
|
+
elements.autoFlowResizer.addEventListener("keydown", handleAutoFlowResizerKeydown);
|
|
3073
|
+
}
|
|
3074
|
+
applyWorkspaceSplit(state.workspaceSplit);
|
|
3075
|
+
if (elements.workspaceResizer) {
|
|
3076
|
+
elements.workspaceResizer.setAttribute("role", "separator");
|
|
3077
|
+
elements.workspaceResizer.setAttribute("aria-orientation", "vertical");
|
|
3078
|
+
elements.workspaceResizer.setAttribute("aria-label", "Resize workspace panels");
|
|
3079
|
+
elements.workspaceResizer.setAttribute("tabindex", "0");
|
|
3080
|
+
elements.workspaceResizer.addEventListener("pointerdown", beginWorkspaceResize);
|
|
3081
|
+
elements.workspaceResizer.addEventListener("dblclick", resetWorkspaceSplit);
|
|
3082
|
+
elements.workspaceResizer.addEventListener("keydown", handleWorkspaceResizerKeydown);
|
|
3083
|
+
}
|
|
3084
|
+
applyLogAutoscroll(state.logAutoscroll);
|
|
3085
|
+
if (elements.logAutoscroll) {
|
|
3086
|
+
elements.logAutoscroll.addEventListener("change", function () {
|
|
3087
|
+
applyLogAutoscroll(elements.logAutoscroll.checked);
|
|
3088
|
+
persistLogAutoscroll(state.logAutoscroll);
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
1554
3091
|
elements.run.addEventListener("click", api.openRunConfirm);
|
|
1555
3092
|
elements.interrupt.addEventListener("click", api.openInterruptConfirm);
|
|
1556
3093
|
elements.artifactOpen.addEventListener("click", api.openArtifactExplorer);
|
|
@@ -1558,22 +3095,39 @@
|
|
|
1558
3095
|
elements.artifactToolbarClose.addEventListener("click", api.closeArtifactExplorer);
|
|
1559
3096
|
elements.artifactCopyContent.addEventListener("click", copySelectedArtifactContent);
|
|
1560
3097
|
elements.artifactCopyReference.addEventListener("click", copySelectedArtifactReference);
|
|
3098
|
+
ensureGitDiffModeButtons();
|
|
3099
|
+
elements.gitDiffClose.addEventListener("click", closeGitDiff);
|
|
3100
|
+
Array.prototype.slice.call(elements.gitDiffModeControls.querySelectorAll("button")).forEach(function (button) {
|
|
3101
|
+
button.addEventListener("click", function () {
|
|
3102
|
+
setGitDiffMode(button.dataset.gitDiffMode || "head");
|
|
3103
|
+
});
|
|
3104
|
+
});
|
|
3105
|
+
elements.gitRefresh.addEventListener("click", api.refreshGit);
|
|
3106
|
+
elements.gitCreateBranch.addEventListener("click", api.createGitBranch);
|
|
3107
|
+
elements.gitCheckout.addEventListener("click", api.checkoutGitBranch);
|
|
3108
|
+
elements.gitFetch.addEventListener("click", api.fetchGit);
|
|
3109
|
+
elements.gitPull.addEventListener("click", api.pullGit);
|
|
3110
|
+
elements.gitStage.addEventListener("click", api.stageGit);
|
|
3111
|
+
elements.gitUnstage.addEventListener("click", api.unstageGit);
|
|
3112
|
+
elements.gitCommit.addEventListener("click", api.commitGit);
|
|
3113
|
+
elements.gitPush.addEventListener("click", api.pushGit);
|
|
3114
|
+
elements.gitCommitMessage.addEventListener("input", function () {
|
|
3115
|
+
state.gitCommitMessage = elements.gitCommitMessage.value;
|
|
3116
|
+
});
|
|
3117
|
+
elements.gitCommitMessage.addEventListener("change", api.updateGitCommitMessage);
|
|
1561
3118
|
elements.help.addEventListener("click", api.toggleHelp);
|
|
1562
3119
|
elements.closeHelp.addEventListener("click", function () {
|
|
1563
3120
|
api.showHelp(false);
|
|
1564
3121
|
});
|
|
1565
3122
|
elements.clearLog.addEventListener("click", api.clearLog);
|
|
1566
3123
|
elements.progress.addEventListener("scroll", function () {
|
|
1567
|
-
|
|
1568
|
-
});
|
|
1569
|
-
elements.summary.addEventListener("scroll", function () {
|
|
1570
|
-
api.scrollPane("summary", Math.round(elements.summary.scrollTop));
|
|
3124
|
+
sendScrollPane("progress", elements.progress);
|
|
1571
3125
|
});
|
|
1572
3126
|
elements.log.addEventListener("scroll", function () {
|
|
1573
|
-
|
|
3127
|
+
sendScrollPane("log", elements.log);
|
|
1574
3128
|
});
|
|
1575
3129
|
elements.helpText.addEventListener("scroll", function () {
|
|
1576
|
-
|
|
3130
|
+
sendScrollPane("help", elements.helpText);
|
|
1577
3131
|
});
|
|
1578
3132
|
|
|
1579
3133
|
api.connect();
|