@grifhinz/logics-manager 2.1.2 → 2.3.0
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 +106 -4
- package/VERSION +1 -1
- package/clients/README.md +9 -0
- package/clients/shared-web/media/css/board.css +658 -0
- package/clients/shared-web/media/css/details.css +457 -0
- package/clients/shared-web/media/css/layout.css +123 -0
- package/clients/shared-web/media/css/toolbar.css +576 -0
- package/clients/shared-web/media/harnessApi.js +324 -0
- package/clients/shared-web/media/hostApi.js +213 -0
- package/clients/shared-web/media/hostApiContract.js +55 -0
- package/clients/shared-web/media/icon.png +0 -0
- package/clients/shared-web/media/layoutController.js +246 -0
- package/clients/shared-web/media/logics.svg +7 -0
- package/clients/shared-web/media/logicsModel.js +910 -0
- package/clients/shared-web/media/main.css +112 -0
- package/clients/shared-web/media/main.js +3 -0
- package/clients/shared-web/media/mainApp.js +1005 -0
- package/clients/shared-web/media/mainCore.js +604 -0
- package/clients/shared-web/media/mainInteractionHandlers.js +324 -0
- package/clients/shared-web/media/mainInteractions.js +378 -0
- package/clients/shared-web/media/renderBoard.js +3 -0
- package/clients/shared-web/media/renderBoardApp.js +1339 -0
- package/clients/shared-web/media/renderDetails.js +685 -0
- package/clients/shared-web/media/renderMarkdown.js +449 -0
- package/clients/shared-web/media/toolsPanelLayout.js +172 -0
- package/clients/shared-web/media/uiStatus.js +54 -0
- package/clients/shared-web/media/webviewChrome.js +405 -0
- package/clients/shared-web/media/webviewPersistence.js +116 -0
- package/clients/shared-web/media/webviewSelectors.js +491 -0
- package/clients/viewer/README.md +5 -0
- package/clients/viewer/browser-host.js +847 -0
- package/clients/viewer/index.html +237 -0
- package/clients/viewer/viewer.css +433 -0
- package/logics_manager/assist.py +94 -63
- package/logics_manager/assist_handoff.py +132 -0
- package/logics_manager/assist_surface.py +38 -0
- package/logics_manager/cli.py +152 -12
- package/logics_manager/cli_output.py +18 -0
- package/logics_manager/flow.py +1360 -84
- package/logics_manager/flow_evidence.py +63 -0
- package/logics_manager/index.py +3 -7
- package/logics_manager/insights.py +418 -0
- package/logics_manager/mcp.py +50 -0
- package/logics_manager/path_utils.py +31 -0
- package/logics_manager/sync.py +24 -12
- package/logics_manager/update_check.py +138 -0
- package/logics_manager/viewer.py +533 -0
- package/package.json +12 -6
- package/pyproject.toml +1 -1
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
window.createCdxLogicsWebviewChrome = function createCdxLogicsWebviewChrome(options) {
|
|
3
|
+
const {
|
|
4
|
+
activityPanel,
|
|
5
|
+
activityToggle,
|
|
6
|
+
attentionToggle,
|
|
7
|
+
bootstrapLogicsButton,
|
|
8
|
+
repairLogicsKitButton,
|
|
9
|
+
assistPublishReleaseButton,
|
|
10
|
+
filterPanel,
|
|
11
|
+
filterToggle,
|
|
12
|
+
groupBySelect,
|
|
13
|
+
helpBanner,
|
|
14
|
+
helpBannerCopy,
|
|
15
|
+
hideCompleteToggle,
|
|
16
|
+
hideEmptyColumnsToggle,
|
|
17
|
+
hideProcessedRequestsToggle,
|
|
18
|
+
hideSpecToggle,
|
|
19
|
+
markDoneButton,
|
|
20
|
+
markObsoleteButton,
|
|
21
|
+
changeStatusButton,
|
|
22
|
+
openButton,
|
|
23
|
+
promoteButton,
|
|
24
|
+
readButton,
|
|
25
|
+
resetProjectRootButton,
|
|
26
|
+
searchInput,
|
|
27
|
+
showCompanionDocsToggle,
|
|
28
|
+
sortBySelect,
|
|
29
|
+
toolsPanel,
|
|
30
|
+
toolsToggle,
|
|
31
|
+
viewModeToggleButton,
|
|
32
|
+
defaultFilterState,
|
|
33
|
+
canPromote,
|
|
34
|
+
getActivityEntries,
|
|
35
|
+
getAttentionOnly,
|
|
36
|
+
getCanBootstrapLogics,
|
|
37
|
+
getBootstrapLogicsTitle,
|
|
38
|
+
getCanResetProjectRoot,
|
|
39
|
+
getCanRepairLogicsKit,
|
|
40
|
+
getRepairLogicsKitTitle,
|
|
41
|
+
getCanPublishRelease,
|
|
42
|
+
getPublishReleaseTitle,
|
|
43
|
+
getShouldRecommendCheckEnvironment,
|
|
44
|
+
getEffectiveViewMode,
|
|
45
|
+
getGroupMode,
|
|
46
|
+
getHelpBannerMessage,
|
|
47
|
+
getHideCompleted,
|
|
48
|
+
getHideEmptyColumns,
|
|
49
|
+
getHideProcessedRequests,
|
|
50
|
+
getHideSpec,
|
|
51
|
+
getIsListMode,
|
|
52
|
+
getSearchQuery,
|
|
53
|
+
getSecondaryToolbarOpen,
|
|
54
|
+
getShowCompanionDocs,
|
|
55
|
+
getSortMode,
|
|
56
|
+
getStageLabel,
|
|
57
|
+
getToolsPanelOpen,
|
|
58
|
+
getSelectedItem,
|
|
59
|
+
isCompactListForced,
|
|
60
|
+
readItemAndRender,
|
|
61
|
+
selectItemAndRender
|
|
62
|
+
} = options;
|
|
63
|
+
const toolsPanelLayoutFactory = window.createCdxLogicsToolsPanelLayoutApi;
|
|
64
|
+
const toolsPanelLayout =
|
|
65
|
+
typeof toolsPanelLayoutFactory === "function"
|
|
66
|
+
? toolsPanelLayoutFactory({
|
|
67
|
+
toolsPanel,
|
|
68
|
+
getCanBootstrapLogics,
|
|
69
|
+
getBootstrapLogicsTitle,
|
|
70
|
+
getShouldRecommendCheckEnvironment
|
|
71
|
+
})
|
|
72
|
+
: null;
|
|
73
|
+
|
|
74
|
+
function setButtonIcon(button, svgMarkup) {
|
|
75
|
+
if (!button) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
button.innerHTML = svgMarkup;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function formatToolsViewLabel(viewName) {
|
|
82
|
+
if (!viewName) {
|
|
83
|
+
return "Tools";
|
|
84
|
+
}
|
|
85
|
+
return String(viewName).replace(/^[a-z]/, (value) => value.toUpperCase());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function listModeIcon() {
|
|
89
|
+
return `
|
|
90
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
91
|
+
<path d="M8 7h12M8 12h12M8 17h12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
|
92
|
+
<circle cx="5" cy="7" r="1.5" fill="currentColor" />
|
|
93
|
+
<circle cx="5" cy="12" r="1.5" fill="currentColor" />
|
|
94
|
+
<circle cx="5" cy="17" r="1.5" fill="currentColor" />
|
|
95
|
+
</svg>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function boardModeIcon() {
|
|
100
|
+
return `
|
|
101
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
102
|
+
<rect x="4" y="5" width="6" height="6" rx="1.5" fill="none" stroke="currentColor" stroke-width="2" />
|
|
103
|
+
<rect x="14" y="5" width="6" height="6" rx="1.5" fill="none" stroke="currentColor" stroke-width="2" />
|
|
104
|
+
<rect x="4" y="13" width="6" height="6" rx="1.5" fill="none" stroke="currentColor" stroke-width="2" />
|
|
105
|
+
<rect x="14" y="13" width="6" height="6" rx="1.5" fill="none" stroke="currentColor" stroke-width="2" />
|
|
106
|
+
</svg>
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function updateViewModeToggle() {
|
|
111
|
+
if (!viewModeToggleButton) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const currentMode = getEffectiveViewMode();
|
|
115
|
+
if (isCompactListForced()) {
|
|
116
|
+
setButtonIcon(viewModeToggleButton, listModeIcon());
|
|
117
|
+
viewModeToggleButton.dataset.currentMode = currentMode;
|
|
118
|
+
viewModeToggleButton.setAttribute("aria-pressed", "true");
|
|
119
|
+
viewModeToggleButton.setAttribute("aria-label", "Current mode: list. List mode is required below 500px");
|
|
120
|
+
viewModeToggleButton.title = "Current mode: list. List mode is required below 500px";
|
|
121
|
+
viewModeToggleButton.disabled = true;
|
|
122
|
+
viewModeToggleButton.hidden = true;
|
|
123
|
+
viewModeToggleButton.style.display = "none";
|
|
124
|
+
viewModeToggleButton.setAttribute("aria-hidden", "true");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const switchToList = currentMode !== "list";
|
|
128
|
+
setButtonIcon(viewModeToggleButton, switchToList ? listModeIcon() : boardModeIcon());
|
|
129
|
+
viewModeToggleButton.dataset.currentMode = currentMode;
|
|
130
|
+
viewModeToggleButton.setAttribute("aria-pressed", String(currentMode === "list"));
|
|
131
|
+
viewModeToggleButton.setAttribute(
|
|
132
|
+
"aria-label",
|
|
133
|
+
switchToList
|
|
134
|
+
? `Current mode: ${currentMode}. Switch to list mode`
|
|
135
|
+
: `Current mode: ${currentMode}. Switch to board mode`
|
|
136
|
+
);
|
|
137
|
+
viewModeToggleButton.title = switchToList
|
|
138
|
+
? `Current mode: ${currentMode}. Switch to list mode`
|
|
139
|
+
: `Current mode: ${currentMode}. Switch to board mode`;
|
|
140
|
+
viewModeToggleButton.disabled = false;
|
|
141
|
+
viewModeToggleButton.hidden = false;
|
|
142
|
+
viewModeToggleButton.style.display = "";
|
|
143
|
+
viewModeToggleButton.removeAttribute("aria-hidden");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderActivityPanel() {
|
|
147
|
+
if (!activityPanel) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const isOpen = options.getActivityPanelOpen();
|
|
151
|
+
activityPanel.hidden = !isOpen;
|
|
152
|
+
if (!isOpen) {
|
|
153
|
+
activityPanel.innerHTML = "";
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const entries = getActivityEntries();
|
|
158
|
+
activityPanel.innerHTML = "";
|
|
159
|
+
|
|
160
|
+
const header = document.createElement("div");
|
|
161
|
+
header.className = "activity-panel__header";
|
|
162
|
+
header.textContent = "Recent activity";
|
|
163
|
+
activityPanel.appendChild(header);
|
|
164
|
+
|
|
165
|
+
const list = document.createElement("div");
|
|
166
|
+
list.className = "activity-panel__list";
|
|
167
|
+
|
|
168
|
+
if (!entries.length) {
|
|
169
|
+
const empty = document.createElement("div");
|
|
170
|
+
empty.className = "state-message";
|
|
171
|
+
empty.textContent = "No recent activity is available yet.";
|
|
172
|
+
list.appendChild(empty);
|
|
173
|
+
} else {
|
|
174
|
+
entries.forEach((entry) => {
|
|
175
|
+
const button = document.createElement("button");
|
|
176
|
+
button.type = "button";
|
|
177
|
+
button.className = "activity-panel__entry";
|
|
178
|
+
button.dataset.id = entry.id;
|
|
179
|
+
|
|
180
|
+
const title = document.createElement("div");
|
|
181
|
+
title.className = "activity-panel__title";
|
|
182
|
+
title.textContent = entry.title;
|
|
183
|
+
button.appendChild(title);
|
|
184
|
+
|
|
185
|
+
const meta = document.createElement("div");
|
|
186
|
+
meta.className = "activity-panel__meta";
|
|
187
|
+
meta.textContent = `${entry.label} • ${getStageLabel(entry.stage)} • ${entry.id}`;
|
|
188
|
+
button.appendChild(meta);
|
|
189
|
+
|
|
190
|
+
const updated = document.createElement("div");
|
|
191
|
+
updated.className = "activity-panel__updated";
|
|
192
|
+
updated.textContent =
|
|
193
|
+
toolsPanelLayout && typeof toolsPanelLayout.formatActivityUpdated === "function"
|
|
194
|
+
? toolsPanelLayout.formatActivityUpdated(entry.updatedAt)
|
|
195
|
+
: "Updated: Unknown";
|
|
196
|
+
button.appendChild(updated);
|
|
197
|
+
|
|
198
|
+
button.addEventListener("click", () => {
|
|
199
|
+
selectItemAndRender(entry.id);
|
|
200
|
+
});
|
|
201
|
+
button.addEventListener("dblclick", () => {
|
|
202
|
+
readItemAndRender(entry.id);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
list.appendChild(button);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
activityPanel.appendChild(list);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function renderHelpBanner() {
|
|
213
|
+
if (!helpBanner || !helpBannerCopy) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const message = options.getHelpDismissed() ? "" : getHelpBannerMessage();
|
|
217
|
+
helpBanner.hidden = !message;
|
|
218
|
+
helpBannerCopy.textContent = message;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function updateButtons() {
|
|
222
|
+
const item = getSelectedItem();
|
|
223
|
+
openButton.disabled = !item;
|
|
224
|
+
promoteButton.disabled = !canPromote(item);
|
|
225
|
+
if (markDoneButton) {
|
|
226
|
+
markDoneButton.disabled = !item;
|
|
227
|
+
markDoneButton.title = item ? "Mark selected item as done" : "Select an item first";
|
|
228
|
+
}
|
|
229
|
+
if (markObsoleteButton) {
|
|
230
|
+
markObsoleteButton.disabled = !item;
|
|
231
|
+
markObsoleteButton.title = item ? "Mark selected item as obsolete" : "Select an item first";
|
|
232
|
+
}
|
|
233
|
+
if (changeStatusButton) {
|
|
234
|
+
changeStatusButton.disabled = !item;
|
|
235
|
+
changeStatusButton.title = item ? "Change status of selected item" : "Select an item first";
|
|
236
|
+
}
|
|
237
|
+
openButton.title = item ? "Edit selected item" : "Select an item to edit";
|
|
238
|
+
promoteButton.title = canPromote(item)
|
|
239
|
+
? "Promote selected item"
|
|
240
|
+
: "Select a request/backlog item that can be promoted";
|
|
241
|
+
promoteButton.classList.toggle("btn--contextual-active", canPromote(item));
|
|
242
|
+
if (readButton) {
|
|
243
|
+
readButton.disabled = !item;
|
|
244
|
+
readButton.title = item ? "Read selected item" : "Select an item to read";
|
|
245
|
+
}
|
|
246
|
+
if (resetProjectRootButton) {
|
|
247
|
+
resetProjectRootButton.disabled = !getCanResetProjectRoot();
|
|
248
|
+
resetProjectRootButton.title = getCanResetProjectRoot() ? "Use workspace root" : "Already using workspace root";
|
|
249
|
+
}
|
|
250
|
+
if (bootstrapLogicsButton) {
|
|
251
|
+
bootstrapLogicsButton.disabled = !getCanBootstrapLogics();
|
|
252
|
+
bootstrapLogicsButton.title = getBootstrapLogicsTitle();
|
|
253
|
+
}
|
|
254
|
+
if (repairLogicsKitButton) {
|
|
255
|
+
repairLogicsKitButton.disabled = !getCanRepairLogicsKit();
|
|
256
|
+
repairLogicsKitButton.title = getRepairLogicsKitTitle();
|
|
257
|
+
}
|
|
258
|
+
if (assistPublishReleaseButton) {
|
|
259
|
+
assistPublishReleaseButton.disabled = !getCanPublishRelease();
|
|
260
|
+
assistPublishReleaseButton.title = getPublishReleaseTitle();
|
|
261
|
+
}
|
|
262
|
+
if (toolsPanelLayout && typeof toolsPanelLayout.renderToolsPanelStructure === "function") {
|
|
263
|
+
toolsPanelLayout.renderToolsPanelStructure();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getActiveToolsView() {
|
|
268
|
+
if (toolsPanelLayout && typeof toolsPanelLayout.getActiveToolsView === "function") {
|
|
269
|
+
return toolsPanelLayout.getActiveToolsView();
|
|
270
|
+
}
|
|
271
|
+
return "workflow";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function hasNonDefaultSecondaryControls() {
|
|
275
|
+
return (
|
|
276
|
+
getHideCompleted() !== defaultFilterState.hideCompleted ||
|
|
277
|
+
getHideProcessedRequests() !== defaultFilterState.hideProcessedRequests ||
|
|
278
|
+
getHideSpec() !== defaultFilterState.hideSpec ||
|
|
279
|
+
getShowCompanionDocs() !== defaultFilterState.showCompanionDocs ||
|
|
280
|
+
getHideEmptyColumns() !== defaultFilterState.hideEmptyColumns ||
|
|
281
|
+
options.normalizeSearchValue(getSearchQuery()) !== "" ||
|
|
282
|
+
getGroupMode() !== "stage" ||
|
|
283
|
+
(getSortMode() !== "updated-desc" && getSortMode() !== "default")
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function updateFilterState() {
|
|
288
|
+
if (filterPanel) {
|
|
289
|
+
filterPanel.hidden = !getSecondaryToolbarOpen();
|
|
290
|
+
filterPanel.setAttribute("aria-hidden", String(!getSecondaryToolbarOpen()));
|
|
291
|
+
}
|
|
292
|
+
if (!filterToggle) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const hasNonDefaultControls = hasNonDefaultSecondaryControls();
|
|
296
|
+
filterToggle.classList.toggle("toolbar__filter--open", getSecondaryToolbarOpen());
|
|
297
|
+
filterToggle.classList.toggle("toolbar__filter--active", !getSecondaryToolbarOpen() && hasNonDefaultControls);
|
|
298
|
+
filterToggle.setAttribute("aria-expanded", String(getSecondaryToolbarOpen()));
|
|
299
|
+
filterToggle.setAttribute("data-has-active-controls", String(hasNonDefaultControls));
|
|
300
|
+
const label = getSecondaryToolbarOpen()
|
|
301
|
+
? "Hide view controls"
|
|
302
|
+
: hasNonDefaultControls
|
|
303
|
+
? "Show view controls (non-default controls active)"
|
|
304
|
+
: "Show view controls";
|
|
305
|
+
filterToggle.setAttribute("aria-label", label);
|
|
306
|
+
filterToggle.title = label;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function syncInputs() {
|
|
310
|
+
if (hideCompleteToggle) {
|
|
311
|
+
hideCompleteToggle.checked = getHideCompleted();
|
|
312
|
+
}
|
|
313
|
+
if (hideProcessedRequestsToggle) {
|
|
314
|
+
hideProcessedRequestsToggle.checked = getHideProcessedRequests();
|
|
315
|
+
}
|
|
316
|
+
if (hideSpecToggle) {
|
|
317
|
+
hideSpecToggle.checked = getHideSpec();
|
|
318
|
+
}
|
|
319
|
+
if (showCompanionDocsToggle) {
|
|
320
|
+
showCompanionDocsToggle.checked = getShowCompanionDocs();
|
|
321
|
+
}
|
|
322
|
+
if (hideEmptyColumnsToggle) {
|
|
323
|
+
hideEmptyColumnsToggle.checked = getHideEmptyColumns();
|
|
324
|
+
}
|
|
325
|
+
if (searchInput) {
|
|
326
|
+
searchInput.value = getSearchQuery();
|
|
327
|
+
}
|
|
328
|
+
if (groupBySelect) {
|
|
329
|
+
groupBySelect.value = getGroupMode();
|
|
330
|
+
groupBySelect.disabled = !getIsListMode();
|
|
331
|
+
groupBySelect.title = getIsListMode() ? "Group visible list items" : "Grouping modes apply in list mode";
|
|
332
|
+
}
|
|
333
|
+
if (sortBySelect) {
|
|
334
|
+
sortBySelect.value = getSortMode();
|
|
335
|
+
sortBySelect.title = "Sort visible items";
|
|
336
|
+
}
|
|
337
|
+
if (attentionToggle) {
|
|
338
|
+
attentionToggle.classList.toggle("btn--active", getAttentionOnly());
|
|
339
|
+
attentionToggle.setAttribute("aria-pressed", String(getAttentionOnly()));
|
|
340
|
+
attentionToggle.setAttribute(
|
|
341
|
+
"aria-label",
|
|
342
|
+
getAttentionOnly()
|
|
343
|
+
? "Showing blocked, orphaned, unprocessed, or inconsistent items"
|
|
344
|
+
: "Show blocked, orphaned, unprocessed, or inconsistent items"
|
|
345
|
+
);
|
|
346
|
+
attentionToggle.title = getAttentionOnly()
|
|
347
|
+
? "Showing blocked, orphaned, unprocessed, or inconsistent items"
|
|
348
|
+
: "Show blocked, orphaned, unprocessed, or inconsistent items";
|
|
349
|
+
}
|
|
350
|
+
if (activityToggle) {
|
|
351
|
+
activityToggle.classList.toggle("btn--active", options.getActivityPanelOpen());
|
|
352
|
+
activityToggle.setAttribute("aria-pressed", String(options.getActivityPanelOpen()));
|
|
353
|
+
activityToggle.setAttribute(
|
|
354
|
+
"aria-label",
|
|
355
|
+
options.getActivityPanelOpen() ? "Hide recent activity" : "Show recent activity"
|
|
356
|
+
);
|
|
357
|
+
activityToggle.title = options.getActivityPanelOpen() ? "Hide recent activity" : "Show recent activity";
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function setToolsPanelOpen(viewName, isOpen) {
|
|
362
|
+
if (toolsPanel) {
|
|
363
|
+
toolsPanel.classList.toggle("tools-panel--open", isOpen);
|
|
364
|
+
toolsPanel.setAttribute("aria-hidden", String(!isOpen));
|
|
365
|
+
}
|
|
366
|
+
if (viewName && toolsPanelLayout && typeof toolsPanelLayout.setActiveToolsView === "function") {
|
|
367
|
+
toolsPanelLayout.setActiveToolsView(viewName);
|
|
368
|
+
}
|
|
369
|
+
const activeToolsView = getActiveToolsView();
|
|
370
|
+
if (toolsToggle) {
|
|
371
|
+
toolsToggle.classList.toggle("toolbar__filter--active", isOpen);
|
|
372
|
+
toolsToggle.setAttribute("aria-expanded", String(isOpen));
|
|
373
|
+
toolsToggle.setAttribute(
|
|
374
|
+
"aria-label",
|
|
375
|
+
isOpen ? `Close tools menu (${formatToolsViewLabel(activeToolsView)})` : `Open tools menu (${formatToolsViewLabel(activeToolsView)})`
|
|
376
|
+
);
|
|
377
|
+
toolsToggle.title = isOpen
|
|
378
|
+
? `Close tools menu (${formatToolsViewLabel(activeToolsView)})`
|
|
379
|
+
: `Open tools menu (${formatToolsViewLabel(activeToolsView)})`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function setControlDescription(element, label) {
|
|
384
|
+
if (!element || !label) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (!element.getAttribute("aria-label")) {
|
|
388
|
+
element.setAttribute("aria-label", label);
|
|
389
|
+
}
|
|
390
|
+
element.title = label;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
updateViewModeToggle,
|
|
395
|
+
renderActivityPanel,
|
|
396
|
+
renderHelpBanner,
|
|
397
|
+
updateButtons,
|
|
398
|
+
hasNonDefaultSecondaryControls,
|
|
399
|
+
updateFilterState,
|
|
400
|
+
syncInputs,
|
|
401
|
+
setToolsPanelOpen,
|
|
402
|
+
setControlDescription
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
})();
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
window.createCdxLogicsWebviewPersistence = function createCdxLogicsWebviewPersistence(options) {
|
|
3
|
+
const {
|
|
4
|
+
vscode,
|
|
5
|
+
board,
|
|
6
|
+
detailsBody,
|
|
7
|
+
defaultFilterState,
|
|
8
|
+
getUiState,
|
|
9
|
+
getSnapshot,
|
|
10
|
+
applyResetState,
|
|
11
|
+
applyPersistedState
|
|
12
|
+
} = options;
|
|
13
|
+
|
|
14
|
+
const scrollState = {
|
|
15
|
+
boardLeft: 0,
|
|
16
|
+
boardTop: 0,
|
|
17
|
+
detailsTop: 0
|
|
18
|
+
};
|
|
19
|
+
let persistStateTimer = null;
|
|
20
|
+
|
|
21
|
+
function captureScrollState() {
|
|
22
|
+
if (board) {
|
|
23
|
+
scrollState.boardLeft = board.scrollLeft;
|
|
24
|
+
scrollState.boardTop = board.scrollTop;
|
|
25
|
+
}
|
|
26
|
+
if (detailsBody) {
|
|
27
|
+
scrollState.detailsTop = detailsBody.scrollTop;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function clampScrollPosition(target, horizontal = false) {
|
|
32
|
+
if (!target) {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
const current = horizontal ? target.scrollLeft : target.scrollTop;
|
|
36
|
+
const size = Number(horizontal ? target.scrollWidth : target.scrollHeight);
|
|
37
|
+
const viewport = Number(horizontal ? target.clientWidth : target.clientHeight);
|
|
38
|
+
if (!Number.isFinite(size) || !Number.isFinite(viewport) || size <= 0 || viewport <= 0) {
|
|
39
|
+
return Number(current || 0);
|
|
40
|
+
}
|
|
41
|
+
const maxScroll = Math.max(0, size - viewport);
|
|
42
|
+
const nextValue = Math.min(Math.max(Number(current || 0), 0), maxScroll);
|
|
43
|
+
if (horizontal) {
|
|
44
|
+
target.scrollLeft = nextValue;
|
|
45
|
+
} else {
|
|
46
|
+
target.scrollTop = nextValue;
|
|
47
|
+
}
|
|
48
|
+
return nextValue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function restoreScrollState() {
|
|
52
|
+
if (board) {
|
|
53
|
+
board.scrollLeft = scrollState.boardLeft;
|
|
54
|
+
board.scrollTop = scrollState.boardTop;
|
|
55
|
+
clampScrollPosition(board, true);
|
|
56
|
+
clampScrollPosition(board, false);
|
|
57
|
+
}
|
|
58
|
+
if (detailsBody) {
|
|
59
|
+
detailsBody.scrollTop = scrollState.detailsTop;
|
|
60
|
+
clampScrollPosition(detailsBody, false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function persistState() {
|
|
65
|
+
captureScrollState();
|
|
66
|
+
vscode.setState(getSnapshot({ ...scrollState }));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function schedulePersistState() {
|
|
70
|
+
if (persistStateTimer) {
|
|
71
|
+
clearTimeout(persistStateTimer);
|
|
72
|
+
}
|
|
73
|
+
persistStateTimer = setTimeout(() => {
|
|
74
|
+
persistStateTimer = null;
|
|
75
|
+
persistState();
|
|
76
|
+
}, 80);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resetPersistedUiState() {
|
|
80
|
+
applyResetState({
|
|
81
|
+
defaultFilterState,
|
|
82
|
+
uiDefaults: {
|
|
83
|
+
detailsCollapsed: false,
|
|
84
|
+
viewMode: "board",
|
|
85
|
+
splitRatio: 0.6
|
|
86
|
+
},
|
|
87
|
+
scrollState: {
|
|
88
|
+
boardLeft: 0,
|
|
89
|
+
boardTop: 0,
|
|
90
|
+
detailsTop: 0
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
Object.assign(scrollState, {
|
|
94
|
+
boardLeft: 0,
|
|
95
|
+
boardTop: 0,
|
|
96
|
+
detailsTop: 0
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function hydrate(previousState) {
|
|
101
|
+
applyPersistedState(previousState, scrollState, getUiState());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
captureScrollState,
|
|
106
|
+
restoreScrollState,
|
|
107
|
+
schedulePersistState,
|
|
108
|
+
resetPersistedUiState,
|
|
109
|
+
persistState,
|
|
110
|
+
hydrate,
|
|
111
|
+
getScrollState() {
|
|
112
|
+
return { ...scrollState };
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
})();
|