windmill-components 1.511.1 → 1.522.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/package/components/AppConnectInner.svelte.d.ts +1 -1
- package/package/components/ArgInput.svelte +42 -14
- package/package/components/ArgInput.svelte.d.ts +2 -10
- package/package/components/AssignableTagsInner.svelte +5 -0
- package/package/components/AuthSettings.svelte +4 -2
- package/package/components/AuthSettings.svelte.d.ts +1 -0
- package/package/components/DBManagerDrawer.svelte +154 -151
- package/package/components/DBManagerDrawer.svelte.d.ts +2 -2
- package/package/components/DBTable.svelte +3 -3
- package/package/components/DBTable.svelte.d.ts +1 -0
- package/package/components/DBTableEditor.svelte +7 -7
- package/package/components/DBTableEditor.svelte.d.ts +1 -1
- package/package/components/DeployWorkspace.svelte +1 -1
- package/package/components/DisplayResult.svelte +34 -8
- package/package/components/DisplayResult.svelte.d.ts +4 -1
- package/package/components/DynSelect.svelte +58 -34
- package/package/components/DynSelect.svelte.d.ts +3 -11
- package/package/components/EditableSchemaForm.svelte +126 -6
- package/package/components/EditableSchemaForm.svelte.d.ts +5 -1
- package/package/components/Editor.svelte +1 -1
- package/package/components/EditorBar.svelte +82 -4
- package/package/components/ErrorOrRecoveryHandler.svelte +76 -8
- package/package/components/ErrorOrRecoveryHandler.svelte.d.ts +2 -1
- package/package/components/ExploreAssetButton.svelte +14 -4
- package/package/components/ExploreAssetButton.svelte.d.ts +1 -0
- package/package/components/FlowJobResult.svelte +3 -3
- package/package/components/FlowJobResult.svelte.d.ts +1 -0
- package/package/components/FlowPreviewContent.svelte +9 -1
- package/package/components/FlowPreviewResult.svelte +4 -1
- package/package/components/FlowPreviewResult.svelte.d.ts +1 -0
- package/package/components/FlowStatusViewerInner.svelte +21 -3
- package/package/components/FlowStatusViewerInner.svelte.d.ts +7 -1
- package/package/components/FolderEditor.svelte +1 -1
- package/package/components/GitDiffPreview.svelte +14 -18
- package/package/components/GitDiffPreview.svelte.d.ts +2 -8
- package/package/components/GitHubAppIntegration.svelte +3 -1
- package/package/components/IdEditorInput.svelte +25 -22
- package/package/components/IdEditorInput.svelte.d.ts +11 -23
- package/package/components/InstanceSetting.svelte +7 -2
- package/package/components/InstanceSettings.svelte +1 -0
- package/package/components/JobLoader.svelte +48 -5
- package/package/components/JobLoader.svelte.d.ts +7 -2
- package/package/components/Login.svelte +8 -2
- package/package/components/MemoryFootprintViewer.svelte +1 -1
- package/package/components/ModulePreviewResultViewer.svelte +2 -2
- package/package/components/MoveDrawer.svelte.d.ts +2 -2
- package/package/components/NextcloudSetting.svelte +84 -0
- package/package/components/NextcloudSetting.svelte.d.ts +7 -0
- package/package/components/ObjectResourceInput.svelte +3 -2
- package/package/components/ObjectResourceInput.svelte.d.ts +1 -0
- package/package/components/ParqetCsvTableRenderer.svelte +1 -1
- package/package/components/ResourceEditor.svelte +1 -1
- package/package/components/ResourcePicker.svelte +8 -1
- package/package/components/ResourcePicker.svelte.d.ts +1 -0
- package/package/components/ResultStreamDisplay.svelte +5 -0
- package/package/components/ResultStreamDisplay.svelte.d.ts +5 -0
- package/package/components/RunForm.svelte +9 -1
- package/package/components/SchemaForm.svelte +2 -2
- package/package/components/SchemaForm.svelte.d.ts +2 -10
- package/package/components/ScriptBuilder.svelte +13 -8
- package/package/components/ScriptBuilder.svelte.d.ts +1 -1
- package/package/components/ScriptEditor.svelte.d.ts +1 -1
- package/package/components/ScriptWrapper.svelte +1 -1
- package/package/components/ShareModal.svelte.d.ts +1 -1
- package/package/components/SimpleAgTable.svelte +2 -0
- package/package/components/SimpleAgTable.svelte.d.ts +2 -0
- package/package/components/SqlRepl.svelte +21 -7
- package/package/components/SqlRepl.svelte.d.ts +2 -2
- package/package/components/StringTypeNarrowing.svelte.d.ts +1 -1
- package/package/components/WorkerTagSelect.svelte +70 -1
- package/package/components/apps/components/display/AppDisplayComponent.svelte +13 -1
- package/package/components/apps/components/display/AppText.svelte +2 -2
- package/package/components/apps/components/display/dbtable/AppDbExplorer.svelte +8 -1
- package/package/components/apps/components/display/dbtable/InsertRow.svelte +5 -4
- package/package/components/apps/components/display/dbtable/queries/count.js +11 -1
- package/package/components/apps/components/display/dbtable/queries/createTable.d.ts +1 -1
- package/package/components/apps/components/display/dbtable/queries/createTable.js +3 -3
- package/package/components/apps/components/display/dbtable/queries/delete.js +7 -0
- package/package/components/apps/components/display/dbtable/queries/insert.js +2 -0
- package/package/components/apps/components/display/dbtable/queries/select.js +14 -0
- package/package/components/apps/components/display/dbtable/queries/update.js +7 -0
- package/package/components/apps/components/display/dbtable/utils.d.ts +6 -5
- package/package/components/apps/components/display/dbtable/utils.js +52 -28
- package/package/components/apps/components/display/table/AppAggridExplorerTable.svelte +1 -1
- package/package/components/apps/components/display/table/AppAggridInfiniteTable.svelte +1 -0
- package/package/components/apps/components/display/table/AppAggridInfiniteTable.svelte.d.ts +1 -0
- package/package/components/apps/components/display/table/AppAggridTable.svelte +5 -4
- package/package/components/apps/components/display/table/AppAggridTable.svelte.d.ts +1 -0
- package/package/components/apps/components/display/table/utils.js +7 -4
- package/package/components/apps/components/helpers/HiddenComponent.svelte +2 -2
- package/package/components/apps/components/helpers/RunnableComponent.svelte +4 -1
- package/package/components/apps/components/helpers/RunnableComponent.svelte.d.ts +2 -1
- package/package/components/apps/components/inputs/AppS3FileInput.svelte +2 -2
- package/package/components/apps/components/layout/AppDecisionTree.svelte +1 -1
- package/package/components/apps/components/layout/AppStepper.svelte +1 -1
- package/package/components/apps/components/layout/AppTabs.svelte +1 -1
- package/package/components/apps/editor/AppEditorHeader.svelte +13 -2
- package/package/components/apps/editor/GridViewer.svelte +1 -0
- package/package/components/apps/editor/RunnableJobPanelInner.svelte +2 -1
- package/package/components/apps/editor/contextPanel/components/IdEditor.svelte +7 -7
- package/package/components/apps/editor/contextPanel/components/IdEditor.svelte.d.ts +7 -19
- package/package/components/apps/editor/contextPanel/components/OutputHeader.svelte +8 -12
- package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte.d.ts +1 -1
- package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditorDrawer.svelte.d.ts +1 -1
- package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte.d.ts +1 -1
- package/package/components/apps/editor/settingsPanel/DecisionTreeGraphEditor.svelte +3 -3
- package/package/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte +1 -3
- package/package/components/assets/AssetsDropdownButton.svelte +1 -1
- package/package/components/assets/JobAssetsViewer.svelte +2 -2
- package/package/components/assets/lib.js +4 -0
- package/package/components/auditLogs/AuditLogsFilters.svelte +7 -9
- package/package/components/common/button/Button.svelte +4 -3
- package/package/components/common/button/Button.svelte.d.ts +1 -0
- package/package/components/common/confirmationModal/ConfirmationModal.svelte +6 -5
- package/package/components/common/confirmationModal/ConfirmationModal.svelte.d.ts +6 -11
- package/package/components/common/confirmationModal/asyncConfirmationModal.svelte.d.ts +26 -0
- package/package/components/common/confirmationModal/asyncConfirmationModal.svelte.js +50 -0
- package/package/components/common/modal/Modal.svelte +2 -5
- package/package/components/common/tabs/TabsV2.svelte +2 -1
- package/package/components/common/tabs/TabsV2.svelte.d.ts +1 -0
- package/package/components/copilot/chat/AIChatManager.svelte.js +61 -7
- package/package/components/copilot/chat/ContextTextarea.svelte +1 -1
- package/package/components/copilot/chat/script/core.js +28 -29
- package/package/components/copilot/chat/shared.d.ts +1 -1
- package/package/components/copilot/chat/shared.js +8 -2
- package/package/components/custom_ui.d.ts +2 -0
- package/package/components/dbOps.d.ts +20 -8
- package/package/components/dbOps.js +85 -40
- package/package/components/details/DetailPageHeader.svelte +0 -2
- package/package/components/flows/content/FlowInput.svelte +5 -0
- package/package/components/flows/content/FlowModuleScript.svelte +0 -1
- package/package/components/flows/idUtils.js +2 -1
- package/package/components/flows/map/FlowModuleSchemaItem.svelte +3 -3
- package/package/components/flows/map/FlowModuleSchemaMap.svelte +5 -0
- package/package/components/flows/map/InsertModuleButton.svelte +4 -1
- package/package/components/flows/propPicker/OutputBadge.svelte +5 -1
- package/package/components/flows/propPicker/OutputPickerInner.svelte +9 -5
- package/package/components/flows/propPicker/OutputPickerInner.svelte.d.ts +6 -2
- package/package/components/flows/propPicker/StepHistory.svelte +4 -1
- package/package/components/git_sync/DetectionFlow.svelte +202 -0
- package/package/components/git_sync/DetectionFlow.svelte.d.ts +6 -0
- package/package/components/git_sync/GitSyncContext.svelte.d.ts +82 -0
- package/package/components/git_sync/GitSyncContext.svelte.js +461 -0
- package/package/components/git_sync/GitSyncModalManager.svelte +99 -0
- package/package/components/git_sync/GitSyncModalManager.svelte.d.ts +18 -0
- package/package/components/git_sync/GitSyncRepositoryCard.svelte +339 -0
- package/package/components/git_sync/GitSyncRepositoryCard.svelte.d.ts +6 -0
- package/package/components/git_sync/GitSyncRepositoryList.svelte +17 -0
- package/package/components/git_sync/GitSyncRepositoryList.svelte.d.ts +18 -0
- package/package/components/git_sync/GitSyncSection.svelte +89 -0
- package/package/components/git_sync/GitSyncSection.svelte.d.ts +3 -0
- package/package/components/git_sync/GitSyncSuccessModal.svelte +58 -0
- package/package/components/git_sync/GitSyncSuccessModal.svelte.d.ts +7 -0
- package/package/components/git_sync/PullWorkspaceModal.svelte +575 -0
- package/package/components/git_sync/PullWorkspaceModal.svelte.d.ts +15 -0
- package/package/components/git_sync/PushWorkspaceModal.svelte +320 -0
- package/package/components/git_sync/PushWorkspaceModal.svelte.d.ts +12 -0
- package/package/components/graph/FlowGraphV2.svelte +5 -1
- package/package/components/graph/graphBuilder.svelte.js +1 -1
- package/package/components/graph/renderers/nodes/AssetNode.svelte +4 -4
- package/package/components/icons/AssetDucklakeIcon.svelte +28 -0
- package/package/components/icons/AssetDucklakeIcon.svelte.d.ts +9 -0
- package/package/components/icons/AssetGenericIcon.svelte +3 -0
- package/package/components/icons/DucklakeIcon.svelte +18 -0
- package/package/components/icons/DucklakeIcon.svelte.d.ts +6 -0
- package/package/components/instanceSettings.js +11 -3
- package/package/components/runs/JobPreview.svelte +2 -2
- package/package/components/runs/NoWorkerWithTagWarning.svelte +3 -3
- package/package/components/runs/RunsFilter.svelte.d.ts +1 -1
- package/package/components/schema/FlowPropertyEditor.svelte +3 -2
- package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
- package/package/components/schema/PropertyEditor.svelte +0 -2
- package/package/components/schema/PropertyEditor.svelte.d.ts +1 -1
- package/package/components/schema/SchemaFormDND.svelte +2 -1
- package/package/components/schema/SchemaFormDND.svelte.d.ts +2 -0
- package/package/components/scriptEditor/LogPanel.svelte +5 -3
- package/package/components/scriptEditor/LogPanel.svelte.d.ts +5 -1
- package/package/components/select/Select.svelte +7 -4
- package/package/components/select/Select.svelte.d.ts +5 -0
- package/package/components/select/SelectDropdown.svelte +2 -1
- package/package/components/select/SelectDropdown.svelte.d.ts +3 -0
- package/package/components/sidebar/changelogs.js +5 -0
- package/package/components/table/AutoDataTable.svelte +6 -4
- package/package/components/table/AutoDataTable.svelte.d.ts +1 -0
- package/package/components/table/DataTable.svelte +12 -10
- package/package/components/table/DataTable.svelte.d.ts +1 -0
- package/package/components/triggers/TriggerRetriesAndErrorHandler.svelte.d.ts +2 -2
- package/package/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte +1 -1
- package/package/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte.d.ts +2 -1
- package/package/components/triggers/gcp/GcpTriggerEditorInner.svelte +50 -11
- package/package/components/triggers/gcp/utils.js +1 -0
- package/package/components/triggers/http/utils.js +1 -1
- package/package/components/triggers/kafka/utils.js +1 -1
- package/package/components/triggers/mqtt/utils.js +1 -1
- package/package/components/triggers/nats/utils.js +1 -1
- package/package/components/triggers/postgres/utils.js +1 -1
- package/package/components/triggers/sqs/utils.js +1 -1
- package/package/components/triggers/utils.js +2 -1
- package/package/components/triggers/webhook/WebhooksConfigSection.svelte +24 -26
- package/package/components/triggers/webhook/WebhooksPanel.svelte +1 -15
- package/package/components/triggers/websocket/utils.js +1 -1
- package/package/components/workspaceSettings/AISettings.svelte +52 -36
- package/package/components/workspaceSettings/DucklakeSettings.svelte +321 -0
- package/package/components/workspaceSettings/DucklakeSettings.svelte.d.ts +23 -0
- package/package/components/workspaceSettings/GitSyncFilterSettings.svelte +122 -499
- package/package/components/workspaceSettings/GitSyncFilterSettings.svelte.d.ts +8 -10
- package/package/consts.js +2 -1
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +7 -6
- package/package/gen/schemas.gen.js +7 -6
- package/package/gen/services.gen.d.ts +19 -1
- package/package/gen/services.gen.js +38 -0
- package/package/gen/types.gen.d.ts +78 -3
- package/package/git-sync.d.ts +36 -0
- package/package/git-sync.js +1 -0
- package/package/hub.d.ts +1 -0
- package/package/hubPaths.json +5 -2
- package/package/infer.js +3 -2
- package/package/script_helpers.d.ts +2 -2
- package/package/script_helpers.js +29 -11
- package/package/services/JobManager.d.ts +28 -0
- package/package/services/JobManager.js +114 -0
- package/package/stores.d.ts +1 -1
- package/package/utils.d.ts +18 -1
- package/package/utils.js +55 -2
- package/package.json +5 -4
- package/package/components/InitGitRepoPopover.svelte +0 -410
- package/package/components/InitGitRepoPopover.svelte.d.ts +0 -13
- package/package/components/PullGitRepoPopover.svelte +0 -355
- package/package/components/PullGitRepoPopover.svelte.d.ts +0 -18
- package/package/inferArgSig.d.ts +0 -42
- package/package/inferArgSig.js +0 -198
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
<script lang="ts">import Modal from '../common/modal/Modal.svelte';
|
|
2
|
+
import { Button, Alert, Badge } from '../common';
|
|
3
|
+
import { Loader2, CheckCircle2, XCircle, Terminal, ChevronDown, ChevronUp, Save, Edit3 } from 'lucide-svelte';
|
|
4
|
+
import GitDiffPreview from '../GitDiffPreview.svelte';
|
|
5
|
+
import { JobService } from '../../gen';
|
|
6
|
+
import { workspaceStore } from '../../stores';
|
|
7
|
+
import { sendUserToast } from '../../toast';
|
|
8
|
+
import hubPaths from '../../hubPaths.json';
|
|
9
|
+
import { tryEvery } from '../../utils';
|
|
10
|
+
let { open = $bindable(false), gitRepoResourcePath, uiState, repoIndex, currentGitSyncSettings, onFilterUpdate, onSettingsSaved, onSuccess, settingsOnly = false } = $props();
|
|
11
|
+
// Job state
|
|
12
|
+
let previewJobId = $state(null);
|
|
13
|
+
let previewJobStatus = $state(undefined);
|
|
14
|
+
let isPreviewLoading = $state(false);
|
|
15
|
+
let previewError = $state('');
|
|
16
|
+
let applyJobId = $state(null);
|
|
17
|
+
let applyJobStatus = $state(undefined);
|
|
18
|
+
let isApplying = $state(false);
|
|
19
|
+
let applyError = $state('');
|
|
20
|
+
// UI state
|
|
21
|
+
let showCliInstructions = $state(false);
|
|
22
|
+
let previewResult = $state(null);
|
|
23
|
+
let settingsApplied = $state(false);
|
|
24
|
+
let previewAttempted = $state(false);
|
|
25
|
+
// Helper functions to reduce type casting repetition
|
|
26
|
+
const getSettingsChanges = (result) => {
|
|
27
|
+
if (!result)
|
|
28
|
+
return { hasChanges: false, data: null, diff: null };
|
|
29
|
+
if (settingsOnly) {
|
|
30
|
+
const settingsResponse = result;
|
|
31
|
+
return {
|
|
32
|
+
hasChanges: settingsResponse.hasChanges ?? false,
|
|
33
|
+
data: settingsResponse.local,
|
|
34
|
+
diff: settingsResponse
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const syncResponse = result;
|
|
39
|
+
return {
|
|
40
|
+
hasChanges: syncResponse.settingsDiffResult?.hasChanges ?? false,
|
|
41
|
+
data: syncResponse.settingsDiffResult?.local,
|
|
42
|
+
diff: syncResponse.settingsDiffResult
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const getWorkspaceChanges = (result) => {
|
|
47
|
+
if (!result || settingsOnly)
|
|
48
|
+
return { hasChanges: false, changes: [] };
|
|
49
|
+
const syncResponse = result;
|
|
50
|
+
return {
|
|
51
|
+
hasChanges: (syncResponse.changes?.length ?? 0) > 0,
|
|
52
|
+
changes: syncResponse.changes ?? []
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
// Auto-save settings after successful pull with settings updates
|
|
56
|
+
async function saveUpdatedSettings() {
|
|
57
|
+
if (!currentGitSyncSettings || repoIndex === undefined)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
// Save only the specific repository that was updated
|
|
61
|
+
await currentGitSyncSettings.saveRepository(repoIndex);
|
|
62
|
+
onSettingsSaved?.();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error('Failed to save settings:', error);
|
|
66
|
+
sendUserToast('Failed to save updated settings', true);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Reset state when modal opens/closes
|
|
70
|
+
$effect(() => {
|
|
71
|
+
if (!open) {
|
|
72
|
+
previewJobId = null;
|
|
73
|
+
previewJobStatus = undefined;
|
|
74
|
+
isPreviewLoading = false;
|
|
75
|
+
previewError = '';
|
|
76
|
+
applyJobId = null;
|
|
77
|
+
applyJobStatus = undefined;
|
|
78
|
+
isApplying = false;
|
|
79
|
+
applyError = '';
|
|
80
|
+
showCliInstructions = false;
|
|
81
|
+
previewResult = null;
|
|
82
|
+
settingsApplied = false;
|
|
83
|
+
previewAttempted = false;
|
|
84
|
+
}
|
|
85
|
+
else if (settingsOnly && !previewResult && !isPreviewLoading && !previewAttempted) {
|
|
86
|
+
// Auto-trigger settings preview when modal opens in settings-only mode (only once)
|
|
87
|
+
executeJob(true, true);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// Execute job with dry run or actual execution
|
|
91
|
+
async function executeJob(isDryRun, settingsOnly = false) {
|
|
92
|
+
const isPreview = isDryRun;
|
|
93
|
+
if (isPreview) {
|
|
94
|
+
isPreviewLoading = true;
|
|
95
|
+
previewError = '';
|
|
96
|
+
previewResult = null;
|
|
97
|
+
previewJobId = null;
|
|
98
|
+
previewJobStatus = undefined;
|
|
99
|
+
previewAttempted = true;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
isApplying = true;
|
|
103
|
+
applyError = '';
|
|
104
|
+
applyJobId = null;
|
|
105
|
+
applyJobStatus = undefined;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const workspace = $workspaceStore;
|
|
109
|
+
if (!workspace)
|
|
110
|
+
return;
|
|
111
|
+
const payload = {
|
|
112
|
+
workspace_id: workspace,
|
|
113
|
+
repo_url_resource_path: gitRepoResourcePath,
|
|
114
|
+
dry_run: isDryRun,
|
|
115
|
+
pull: true,
|
|
116
|
+
only_wmill_yaml: settingsOnly,
|
|
117
|
+
settings_json: JSON.stringify(uiState),
|
|
118
|
+
use_promotion_overrides: currentGitSyncSettings?.repositories?.[repoIndex]?.use_individual_branch === true
|
|
119
|
+
};
|
|
120
|
+
const jobId = await JobService.runScriptByPath({
|
|
121
|
+
workspace,
|
|
122
|
+
path: hubPaths.gitInitRepo,
|
|
123
|
+
requestBody: payload,
|
|
124
|
+
skipPreprocessor: true
|
|
125
|
+
});
|
|
126
|
+
if (isPreview) {
|
|
127
|
+
previewJobId = jobId;
|
|
128
|
+
previewJobStatus = 'running';
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
applyJobId = jobId;
|
|
132
|
+
applyJobStatus = 'running';
|
|
133
|
+
}
|
|
134
|
+
let jobSuccess = false;
|
|
135
|
+
let result = {};
|
|
136
|
+
await tryEvery({
|
|
137
|
+
tryCode: async () => {
|
|
138
|
+
const testResult = await JobService.getCompletedJob({ workspace, id: jobId });
|
|
139
|
+
jobSuccess = !!testResult.success;
|
|
140
|
+
if (jobSuccess) {
|
|
141
|
+
const jobResult = await JobService.getCompletedJobResult({ workspace, id: jobId });
|
|
142
|
+
result = jobResult;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
timeoutCode: async () => {
|
|
146
|
+
try {
|
|
147
|
+
await JobService.cancelQueuedJob({
|
|
148
|
+
workspace,
|
|
149
|
+
id: jobId,
|
|
150
|
+
requestBody: { reason: `${isPreview ? 'Preview' : 'Apply'} job timed out after 60s` }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (err) { }
|
|
154
|
+
},
|
|
155
|
+
interval: 500,
|
|
156
|
+
timeout: 60000
|
|
157
|
+
});
|
|
158
|
+
if (isPreview) {
|
|
159
|
+
previewJobStatus = jobSuccess ? 'success' : 'failure';
|
|
160
|
+
if (jobSuccess) {
|
|
161
|
+
previewResult = result;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
previewError = 'Preview failed';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
applyJobStatus = jobSuccess ? 'success' : 'failure';
|
|
169
|
+
if (jobSuccess) {
|
|
170
|
+
const settingsData = result?.local;
|
|
171
|
+
const hasSettingsChanges = settingsData && onFilterUpdate;
|
|
172
|
+
if (hasSettingsChanges) {
|
|
173
|
+
onFilterUpdate(settingsData);
|
|
174
|
+
await saveUpdatedSettings();
|
|
175
|
+
}
|
|
176
|
+
onSuccess?.();
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
applyError = 'Pull failed';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
const errorMsg = e?.message || 'Operation failed';
|
|
185
|
+
if (isPreview) {
|
|
186
|
+
previewJobStatus = 'failure';
|
|
187
|
+
previewError = errorMsg;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
applyJobStatus = 'failure';
|
|
191
|
+
applyError = errorMsg;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
if (isPreview) {
|
|
196
|
+
isPreviewLoading = false;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
isApplying = false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Apply settings only (no job needed - we have the data from preview)
|
|
204
|
+
async function applySettingsOnly() {
|
|
205
|
+
isApplying = true;
|
|
206
|
+
try {
|
|
207
|
+
const settingsChanges = getSettingsChanges(previewResult);
|
|
208
|
+
if (!settingsChanges.hasChanges) {
|
|
209
|
+
sendUserToast('No settings changes to apply', true);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (!settingsChanges.data) {
|
|
213
|
+
sendUserToast('Settings data not available', true);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// Update the UI state with the new settings
|
|
217
|
+
if (onFilterUpdate) {
|
|
218
|
+
onFilterUpdate(settingsChanges.data);
|
|
219
|
+
}
|
|
220
|
+
// Save the updated settings
|
|
221
|
+
await saveUpdatedSettings();
|
|
222
|
+
if (settingsOnly) {
|
|
223
|
+
// Settings-only mode - we're done, onSuccess will handle the toast
|
|
224
|
+
onSuccess?.();
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Two-step flow - transition to step 2
|
|
228
|
+
settingsApplied = true;
|
|
229
|
+
sendUserToast('Settings applied successfully. You can now review workspace changes.');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error('Failed to apply settings:', error);
|
|
234
|
+
sendUserToast('Failed to apply settings: ' + error.message, true);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
isApplying = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
</script>
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
<Modal bind:open title={settingsOnly ? "Pull Settings from Git Repository" : "Pull Workspace from Git Repository"} class="sm:max-w-4xl" cancelText={settingsOnly && !getSettingsChanges(previewResult).hasChanges ? "Close" : "Cancel"}>
|
|
244
|
+
<div class="flex flex-col gap-4">
|
|
245
|
+
<!-- Description -->
|
|
246
|
+
<p class="text-sm text-secondary">
|
|
247
|
+
{#if settingsOnly}
|
|
248
|
+
Pull and apply settings changes from the Git repository to your workspace. This will update your sync filter settings only.
|
|
249
|
+
{:else}
|
|
250
|
+
Pull and apply changes from the Git repository to your workspace. If settings changes are detected, you can choose to pull just the settings or everything.
|
|
251
|
+
{/if}
|
|
252
|
+
</p>
|
|
253
|
+
|
|
254
|
+
<!-- Warning about overwrites - only show for full pulls, not settings-only -->
|
|
255
|
+
{#if !settingsOnly}
|
|
256
|
+
<Alert type="warning" title="This will overwrite local changes">
|
|
257
|
+
Pulling from the repository will overwrite any local changes to files that exist in the repository.
|
|
258
|
+
Make sure to preview the changes before applying.
|
|
259
|
+
</Alert>
|
|
260
|
+
{/if}
|
|
261
|
+
|
|
262
|
+
<!-- Preview section -->
|
|
263
|
+
{#if !previewResult}
|
|
264
|
+
<div class="flex justify-start pt-4">
|
|
265
|
+
<Button
|
|
266
|
+
size="md"
|
|
267
|
+
color="dark"
|
|
268
|
+
onclick={() => {
|
|
269
|
+
previewAttempted = false // Allow manual retry
|
|
270
|
+
executeJob(true, settingsOnly)
|
|
271
|
+
}}
|
|
272
|
+
disabled={isPreviewLoading}
|
|
273
|
+
startIcon={{
|
|
274
|
+
icon: isPreviewLoading ? Loader2 : undefined,
|
|
275
|
+
classes: isPreviewLoading ? 'animate-spin' : ''
|
|
276
|
+
}}
|
|
277
|
+
>
|
|
278
|
+
{isPreviewLoading ? 'Previewing...' : 'Preview changes'}
|
|
279
|
+
</Button>
|
|
280
|
+
</div>
|
|
281
|
+
{/if}
|
|
282
|
+
|
|
283
|
+
<!-- Job status for preview -->
|
|
284
|
+
{#if previewJobId}
|
|
285
|
+
<div class="flex items-center gap-2 text-xs text-tertiary">
|
|
286
|
+
{#if previewJobStatus === 'running'}
|
|
287
|
+
<Loader2 class="animate-spin" size={14} />
|
|
288
|
+
{:else if previewJobStatus === 'success'}
|
|
289
|
+
<CheckCircle2 size={14} class="text-green-600" />
|
|
290
|
+
{:else if previewJobStatus === 'failure'}
|
|
291
|
+
<XCircle size={14} class="text-red-700" />
|
|
292
|
+
{/if}
|
|
293
|
+
Preview job:
|
|
294
|
+
<a
|
|
295
|
+
target="_blank"
|
|
296
|
+
class="underline"
|
|
297
|
+
href={`/run/${previewJobId}?workspace=${$workspaceStore}`}
|
|
298
|
+
>
|
|
299
|
+
{previewJobId}
|
|
300
|
+
</a>
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
|
|
304
|
+
<!-- Preview error -->
|
|
305
|
+
{#if previewError}
|
|
306
|
+
<Alert type="error" title="Preview failed">
|
|
307
|
+
{previewError}
|
|
308
|
+
</Alert>
|
|
309
|
+
{/if}
|
|
310
|
+
|
|
311
|
+
<!-- Preview results -->
|
|
312
|
+
{#if previewResult && !previewError}
|
|
313
|
+
{@const settingsChanges = getSettingsChanges(previewResult)}
|
|
314
|
+
{@const workspaceChanges = getWorkspaceChanges(previewResult)}
|
|
315
|
+
<div class="space-y-4">
|
|
316
|
+
<!-- Settings changes (always show first if present) -->
|
|
317
|
+
{#if settingsChanges.hasChanges && !settingsApplied}
|
|
318
|
+
<div>
|
|
319
|
+
<h4 class="text-sm font-semibold text-primary mb-2">
|
|
320
|
+
Filter Settings from Repository
|
|
321
|
+
<Badge color="blue" size="xs" class="ml-2">wmill.yaml</Badge>
|
|
322
|
+
</h4>
|
|
323
|
+
|
|
324
|
+
<div class="bg-surface-secondary rounded-lg p-4 space-y-1">
|
|
325
|
+
{#if settingsChanges.diff?.diff}
|
|
326
|
+
{#each Object.entries(settingsChanges.diff.diff) as [field, change]}
|
|
327
|
+
{@const fieldName = field.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
|
|
328
|
+
{@const typedChange = change as {from: any, to: any}}
|
|
329
|
+
<div class="flex items-center gap-2 text-xs">
|
|
330
|
+
<span class="text-tertiary min-w-0 flex-shrink-0">{fieldName}:</span>
|
|
331
|
+
{#if Array.isArray(typedChange.from) && Array.isArray(typedChange.to)}
|
|
332
|
+
<span class="text-red-600">{typedChange.from.length === 0 ? 'None' : typedChange.from.join(', ')}</span>
|
|
333
|
+
<span class="text-tertiary">→</span>
|
|
334
|
+
<span class="text-green-600">{typedChange.to.length === 0 ? 'None' : typedChange.to.join(', ')}</span>
|
|
335
|
+
{:else}
|
|
336
|
+
<span class="text-red-600">{typedChange.from}</span>
|
|
337
|
+
<span class="text-tertiary">→</span>
|
|
338
|
+
<span class="text-green-600">{typedChange.to}</span>
|
|
339
|
+
{/if}
|
|
340
|
+
</div>
|
|
341
|
+
{/each}
|
|
342
|
+
{:else}
|
|
343
|
+
<div class="text-xs text-tertiary">
|
|
344
|
+
Settings changes detected but no detailed diff available.
|
|
345
|
+
</div>
|
|
346
|
+
{/if}
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
{/if}
|
|
350
|
+
|
|
351
|
+
<!-- No settings changes detected (settings-only mode) -->
|
|
352
|
+
{#if settingsOnly && !settingsChanges.hasChanges}
|
|
353
|
+
<div class="bg-surface-secondary rounded-lg p-3">
|
|
354
|
+
<div class="text-sm text-tertiary">
|
|
355
|
+
No settings changes detected. Your local sync filter settings are already up to date with the repository.
|
|
356
|
+
See the CLI instructions below on how to edit wmill.yaml
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
<div class="flex justify-start">
|
|
360
|
+
<Button
|
|
361
|
+
size="sm"
|
|
362
|
+
color="dark"
|
|
363
|
+
onclick={() => {
|
|
364
|
+
previewAttempted = false
|
|
365
|
+
previewResult = null
|
|
366
|
+
executeJob(true, settingsOnly)
|
|
367
|
+
}}
|
|
368
|
+
disabled={isPreviewLoading}
|
|
369
|
+
startIcon={{
|
|
370
|
+
icon: isPreviewLoading ? Loader2 : undefined,
|
|
371
|
+
classes: isPreviewLoading ? 'animate-spin' : ''
|
|
372
|
+
}}
|
|
373
|
+
>
|
|
374
|
+
{isPreviewLoading ? 'Previewing...' : 'Preview again'}
|
|
375
|
+
</Button>
|
|
376
|
+
</div>
|
|
377
|
+
{/if}
|
|
378
|
+
|
|
379
|
+
<!-- Workspace changes (show when no settings changes and there are workspace changes) -->
|
|
380
|
+
{#if !settingsOnly && !settingsChanges.hasChanges && workspaceChanges.hasChanges}
|
|
381
|
+
<div class={settingsChanges.hasChanges && settingsApplied ? 'border-t pt-4' : ''}>
|
|
382
|
+
<h4 class="text-sm font-semibold text-primary mb-2">Workspace changes to pull</h4>
|
|
383
|
+
|
|
384
|
+
{#if workspaceChanges.hasChanges}
|
|
385
|
+
<GitDiffPreview previewResult={previewResult as SyncResponse} />
|
|
386
|
+
{:else}
|
|
387
|
+
<div class="bg-surface-secondary rounded-lg p-3">
|
|
388
|
+
<div class="text-sm text-tertiary">No changes to pull from the repository.</div>
|
|
389
|
+
</div>
|
|
390
|
+
{/if}
|
|
391
|
+
</div>
|
|
392
|
+
{/if}
|
|
393
|
+
</div>
|
|
394
|
+
{/if}
|
|
395
|
+
|
|
396
|
+
<!-- Apply section (shown after successful preview) -->
|
|
397
|
+
{#if previewResult && !previewError}
|
|
398
|
+
{@const settingsChanges = getSettingsChanges(previewResult)}
|
|
399
|
+
{@const workspaceChanges = getWorkspaceChanges(previewResult)}
|
|
400
|
+
|
|
401
|
+
{#if settingsChanges.hasChanges || workspaceChanges.hasChanges}
|
|
402
|
+
<div class="border-t pt-4 mt-4">
|
|
403
|
+
{#if settingsChanges.hasChanges && workspaceChanges.hasChanges && !settingsApplied}
|
|
404
|
+
<!-- Step 1: Settings changes first when both are present -->
|
|
405
|
+
<div class="flex flex-col gap-3">
|
|
406
|
+
<div class="text-sm font-medium text-primary">Step 1 of 2: Apply settings changes</div>
|
|
407
|
+
<div class="text-xs text-tertiary">Settings changes detected. Apply these first to ensure workspace content is pulled with the correct configuration.</div>
|
|
408
|
+
<div class="flex gap-2">
|
|
409
|
+
<Button
|
|
410
|
+
size="md"
|
|
411
|
+
onclick={applySettingsOnly}
|
|
412
|
+
disabled={isApplying}
|
|
413
|
+
startIcon={{
|
|
414
|
+
icon: isApplying ? Loader2 : Save,
|
|
415
|
+
classes: isApplying ? 'animate-spin' : ''
|
|
416
|
+
}}
|
|
417
|
+
>
|
|
418
|
+
{isApplying ? 'Applying...' : 'Apply settings'}
|
|
419
|
+
</Button>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
{:else if settingsChanges.hasChanges && !workspaceChanges.hasChanges && !settingsApplied}
|
|
423
|
+
<!-- Only settings changes -->
|
|
424
|
+
<div class="flex gap-2">
|
|
425
|
+
<Button
|
|
426
|
+
size="md"
|
|
427
|
+
onclick={applySettingsOnly}
|
|
428
|
+
disabled={isApplying}
|
|
429
|
+
startIcon={{
|
|
430
|
+
icon: isApplying ? Loader2 : Save,
|
|
431
|
+
classes: isApplying ? 'animate-spin' : ''
|
|
432
|
+
}}
|
|
433
|
+
>
|
|
434
|
+
{isApplying ? 'Applying...' : 'Apply settings'}
|
|
435
|
+
</Button>
|
|
436
|
+
</div>
|
|
437
|
+
{:else if workspaceChanges.hasChanges && (!settingsChanges.hasChanges || settingsApplied)}
|
|
438
|
+
<!-- Step 2: Workspace changes (either no settings changes, or settings already applied) -->
|
|
439
|
+
<div class="flex flex-col gap-3">
|
|
440
|
+
{#if settingsApplied}
|
|
441
|
+
<div class="text-sm font-medium text-primary">Step 2 of 2: Pull Workspace Changes</div>
|
|
442
|
+
<div class="text-xs text-green-600">✓ Settings applied successfully. Now you can pull the workspace changes.</div>
|
|
443
|
+
{/if}
|
|
444
|
+
<div class="flex gap-2">
|
|
445
|
+
<Button
|
|
446
|
+
size="md"
|
|
447
|
+
onclick={() => executeJob(false, false)}
|
|
448
|
+
disabled={isApplying}
|
|
449
|
+
startIcon={{
|
|
450
|
+
icon: isApplying ? Loader2 : Save,
|
|
451
|
+
classes: isApplying ? 'animate-spin' : ''
|
|
452
|
+
}}
|
|
453
|
+
>
|
|
454
|
+
{isApplying ? 'Pulling...' : 'Pull from repository'}
|
|
455
|
+
</Button>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
{:else}
|
|
459
|
+
<!-- No changes to pull -->
|
|
460
|
+
<div class="bg-surface-secondary rounded-lg p-3">
|
|
461
|
+
<div class="text-sm text-tertiary mb-3">No changes to pull from the repository.</div>
|
|
462
|
+
<Button
|
|
463
|
+
size="sm"
|
|
464
|
+
color="dark"
|
|
465
|
+
onclick={() => {
|
|
466
|
+
previewAttempted = false
|
|
467
|
+
previewResult = null
|
|
468
|
+
executeJob(true, settingsOnly)
|
|
469
|
+
}}
|
|
470
|
+
disabled={isPreviewLoading}
|
|
471
|
+
startIcon={{
|
|
472
|
+
icon: isPreviewLoading ? Loader2 : undefined,
|
|
473
|
+
classes: isPreviewLoading ? 'animate-spin' : ''
|
|
474
|
+
}}
|
|
475
|
+
>
|
|
476
|
+
{isPreviewLoading ? 'Previewing...' : 'Preview again'}
|
|
477
|
+
</Button>
|
|
478
|
+
</div>
|
|
479
|
+
{/if}
|
|
480
|
+
</div>
|
|
481
|
+
{/if}
|
|
482
|
+
{/if}
|
|
483
|
+
|
|
484
|
+
<!-- Job status for apply -->
|
|
485
|
+
{#if applyJobId}
|
|
486
|
+
<div class="flex items-center gap-2 text-xs text-tertiary">
|
|
487
|
+
{#if applyJobStatus === 'running'}
|
|
488
|
+
<Loader2 class="animate-spin" size={14} />
|
|
489
|
+
{:else if applyJobStatus === 'success'}
|
|
490
|
+
<CheckCircle2 size={14} class="text-green-600" />
|
|
491
|
+
{:else if applyJobStatus === 'failure'}
|
|
492
|
+
<XCircle size={14} class="text-red-700" />
|
|
493
|
+
{/if}
|
|
494
|
+
Pull job:
|
|
495
|
+
<a
|
|
496
|
+
target="_blank"
|
|
497
|
+
class="underline"
|
|
498
|
+
href={`/run/${applyJobId}?workspace=${$workspaceStore}`}
|
|
499
|
+
>
|
|
500
|
+
{applyJobId}
|
|
501
|
+
</a>
|
|
502
|
+
</div>
|
|
503
|
+
{/if}
|
|
504
|
+
|
|
505
|
+
<!-- Apply error -->
|
|
506
|
+
{#if applyError}
|
|
507
|
+
<Alert type="error" title="Pull failed">
|
|
508
|
+
{applyError}
|
|
509
|
+
</Alert>
|
|
510
|
+
{/if}
|
|
511
|
+
|
|
512
|
+
<!-- CLI Instructions (collapsible) -->
|
|
513
|
+
<div class="border-t pt-4 mt-4">
|
|
514
|
+
<button
|
|
515
|
+
class="flex items-center gap-2 text-sm text-secondary hover:text-primary transition-colors"
|
|
516
|
+
onclick={() => showCliInstructions = !showCliInstructions}
|
|
517
|
+
>
|
|
518
|
+
<Terminal size={16} />
|
|
519
|
+
{#if settingsOnly}
|
|
520
|
+
<span>Update settings with CLI</span>
|
|
521
|
+
{:else}
|
|
522
|
+
<span>Update workspace with CLI</span>
|
|
523
|
+
{/if}
|
|
524
|
+
<Edit3 size={14} class="text-tertiary" />
|
|
525
|
+
{#if showCliInstructions}
|
|
526
|
+
<ChevronUp size={16} />
|
|
527
|
+
{:else}
|
|
528
|
+
<ChevronDown size={16} />
|
|
529
|
+
{/if}
|
|
530
|
+
</button>
|
|
531
|
+
|
|
532
|
+
{#if showCliInstructions}
|
|
533
|
+
<div class="mt-3 bg-surface-secondary rounded-lg p-3">
|
|
534
|
+
{#if settingsOnly}
|
|
535
|
+
<div class="text-xs text-tertiary mb-2">
|
|
536
|
+
Filter settings are sourced from the <code class="bg-surface px-1 py-0.5 rounded">wmill.yaml</code> file in your git repository.
|
|
537
|
+
To modify them, edit the file in your repository, commit the changes, and sync using the commands below. Learn more about <a href="https://www.windmill.dev/docs/advanced/cli/sync#wmillyaml" target="_blank" rel="noopener noreferrer">the wmill.yaml format</a>.
|
|
538
|
+
</div>
|
|
539
|
+
{/if}
|
|
540
|
+
<pre class="text-xs bg-surface p-3 rounded overflow-x-auto whitespace-pre-wrap break-all">
|
|
541
|
+
# Make sure your repo is up to date
|
|
542
|
+
git pull
|
|
543
|
+
|
|
544
|
+
{#if !settingsOnly}# Push from git repository to workspace
|
|
545
|
+
wmill sync push --workspace {$workspaceStore} --repository {gitRepoResourcePath}
|
|
546
|
+
|
|
547
|
+
{:else}
|
|
548
|
+
# Edit wmill.yaml file
|
|
549
|
+
vim wmill.yaml
|
|
550
|
+
git add wmill.yaml
|
|
551
|
+
{/if}# Commit changes
|
|
552
|
+
git commit
|
|
553
|
+
git push
|
|
554
|
+
{#if settingsOnly}
|
|
555
|
+
# Push settings only from git repository or click the pull settings button above{#if currentGitSyncSettings?.repositories?.[repoIndex!]?.use_individual_branch}
|
|
556
|
+
wmill gitsync-settings push --workspace {$workspaceStore} --repository {gitRepoResourcePath} --promotion main{:else}
|
|
557
|
+
wmill gitsync-settings push --workspace {$workspaceStore} --repository {gitRepoResourcePath}{/if}{/if}</pre>
|
|
558
|
+
{#if currentGitSyncSettings?.repositories?.[repoIndex!]?.use_individual_branch && settingsOnly}
|
|
559
|
+
<div class="text-xs text-tertiary mt-3">
|
|
560
|
+
<div class="font-medium mb-1">Promotion Mode Configuration:</div>
|
|
561
|
+
<div class="flex items-center gap-2">
|
|
562
|
+
<span>You can add promotion-specific overrides in your <code class="bg-surface px-1 py-0.5 rounded">wmill.yaml</code> file:</span>
|
|
563
|
+
</div>
|
|
564
|
+
<pre class="text-xs bg-surface p-2 rounded mt-2 overflow-x-auto">git_branches:
|
|
565
|
+
main:
|
|
566
|
+
promotionOverrides:
|
|
567
|
+
# Add your promotion-specific settings here
|
|
568
|
+
</pre>
|
|
569
|
+
</div>
|
|
570
|
+
{/if}
|
|
571
|
+
</div>
|
|
572
|
+
{/if}
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</Modal>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SettingsObject } from '../../git-sync';
|
|
2
|
+
interface Props {
|
|
3
|
+
open: boolean;
|
|
4
|
+
gitRepoResourcePath: string;
|
|
5
|
+
uiState: SettingsObject;
|
|
6
|
+
repoIndex?: number;
|
|
7
|
+
currentGitSyncSettings?: any;
|
|
8
|
+
onFilterUpdate?: (filters: SettingsObject) => void;
|
|
9
|
+
onSettingsSaved?: () => void;
|
|
10
|
+
onSuccess?: () => void;
|
|
11
|
+
settingsOnly?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare const PullWorkspaceModal: import("svelte").Component<Props, {}, "open">;
|
|
14
|
+
type PullWorkspaceModal = ReturnType<typeof PullWorkspaceModal>;
|
|
15
|
+
export default PullWorkspaceModal;
|