Compare commits
1 Commits
feature/de
...
actions_ed
| Author | SHA1 | Date | |
|---|---|---|---|
| 57702bd82d |
@@ -8,6 +8,10 @@ import {renderAnsi} from '../render/ansi.ts';
|
||||
import {POST, DELETE} from '../modules/fetch.ts';
|
||||
import type {IntervalId} from '../types.ts';
|
||||
import {toggleFullScreen} from '../utils.ts';
|
||||
import {createMonaco} from '../features/codeeditor.ts';
|
||||
import type MonacoNamespace from 'monaco-editor';
|
||||
|
||||
type IStandaloneCodeEditor = MonacoNamespace.editor.IStandaloneCodeEditor;
|
||||
|
||||
// see "models/actions/status.go", if it needs to be used somewhere else, move it to a shared file like "types/actions.ts"
|
||||
type RunStatus = 'unknown' | 'waiting' | 'running' | 'success' | 'failure' | 'cancelled' | 'skipped' | 'blocked';
|
||||
@@ -123,6 +127,7 @@ export default defineComponent({
|
||||
isRunningWorkflow: false,
|
||||
hasDraftContent: false,
|
||||
draftTimestamp: null as Date | null,
|
||||
monacoEditor: null as IStandaloneCodeEditor | null,
|
||||
|
||||
commitMessage: '',
|
||||
isCommittingWorkflow: false,
|
||||
@@ -548,9 +553,85 @@ export default defineComponent({
|
||||
} finally {
|
||||
this.isLoadingWorkflow = false;
|
||||
}
|
||||
|
||||
// Initialize Monaco editor if the editor is still open
|
||||
if (this.showWorkflowEditor) {
|
||||
// Use a small timeout to allow Vue to re-render the v-else block
|
||||
setTimeout(() => {
|
||||
this.initMonacoEditor();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
async initMonacoEditor() {
|
||||
// 1. Dispose existing editor if any
|
||||
if (this.monacoEditor) {
|
||||
this.monacoEditor.dispose();
|
||||
this.monacoEditor = null;
|
||||
}
|
||||
|
||||
// 2. Poll for textarea element (max 20 attempts, 50ms interval)
|
||||
let textarea: HTMLTextAreaElement | null = null;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
textarea = document.querySelector('#workflow-content-textarea') as HTMLTextAreaElement;
|
||||
if (textarea) break;
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
}
|
||||
|
||||
if (!textarea) {
|
||||
console.error('Failed to find workflow textarea for Monaco initialization');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. Create Monaco Editor
|
||||
const {editor} = await createMonaco(textarea, this.workflowPath || 'workflow.yml', {
|
||||
language: 'yaml',
|
||||
wordWrap: 'on',
|
||||
automaticLayout: true,
|
||||
minimap: {enabled: false},
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
});
|
||||
|
||||
this.monacoEditor = editor;
|
||||
|
||||
// Hide the original textarea so only Monaco is visible
|
||||
textarea.style.display = 'none';
|
||||
|
||||
// Force a layout update to ensure editor fills the container
|
||||
// This fixes the "10 lines then blank" issue
|
||||
requestAnimationFrame(() => {
|
||||
editor.layout();
|
||||
});
|
||||
|
||||
// 4. Set up bidirectional sync and auto-save
|
||||
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
editor.onDidChangeModelContent(() => {
|
||||
// Sync content back to Vue data
|
||||
this.workflowContent = editor.getValue();
|
||||
|
||||
// Debounce saveDraft
|
||||
if (saveTimeout) clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(() => {
|
||||
this.saveDraft();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('Failed to initialize Monaco editor:', e);
|
||||
alert('Failed to load code editor: ' + e.message + '. Using simple text area instead.');
|
||||
// Fallback: make sure textarea is visible if Monaco failed
|
||||
textarea.style.display = 'block';
|
||||
}
|
||||
},
|
||||
|
||||
closeWorkflowEditor() {
|
||||
// Dispose Monaco editor
|
||||
if (this.monacoEditor) {
|
||||
this.monacoEditor.dispose();
|
||||
this.monacoEditor = null;
|
||||
}
|
||||
this.showWorkflowEditor = false;
|
||||
this.workflowContent = '';
|
||||
this.workflowPath = '';
|
||||
@@ -817,12 +898,18 @@ export default defineComponent({
|
||||
{{ locale.changes_auto_saved }}
|
||||
</span>
|
||||
</label>
|
||||
<textarea
|
||||
v-model="workflowContent"
|
||||
@input="saveDraft()"
|
||||
rows="15"
|
||||
style="font-family: monospace; font-size: 12px;">
|
||||
</textarea>
|
||||
<div class="editor-wrapper" style="position: relative; height: 600px; border: 1px solid var(--color-secondary); border-radius: var(--border-radius); overflow: hidden;">
|
||||
<div v-if="!monacoEditor" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background: var(--color-body); z-index: 10; pointer-events: none;">
|
||||
<span class="ui active large text loader">Loading Editor...</span>
|
||||
</div>
|
||||
<textarea
|
||||
id="workflow-content-textarea"
|
||||
v-model="workflowContent"
|
||||
@input="saveDraft()"
|
||||
rows="15"
|
||||
style="font-family: monospace; font-size: 12px; width: 100%; height: 100%; border: none; padding: 1em; box-sizing: border-box; resize: none;">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -860,6 +947,13 @@ export default defineComponent({
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
/* Monaco Editor Container */
|
||||
:deep(.monaco-editor-container) {
|
||||
height: 100% !important; /* Force full height of the wrapper */
|
||||
width: 100% !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.action-view-body {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
|
||||
Reference in New Issue
Block a user