v0.11.3
This commit is contained in:
+1
-1
@@ -693,7 +693,7 @@
|
||||
|
||||
<section id="flowEditor" class="flow-editor" hidden aria-label="Flow editor">
|
||||
<header class="flow-editor-bar">
|
||||
<div class="flow-editor-title"><button type="button" class="icon-button" data-action="close-flow-editor" aria-label="Back">←</button><div><span class="eyebrow" data-i18n="flow.editorEyebrow">Flow editor</span><input id="flowName" maxlength="100" data-i18n-placeholder="flow.namePlaceholder" data-i18n-title="flow.nameAliasHint" placeholder="My automation"></div></div>
|
||||
<div class="flow-editor-title"><button type="button" class="icon-button" data-action="close-flow-editor" aria-label="Back">←</button><div><span class="eyebrow" data-i18n="flow.editorEyebrow">Flow editor</span><div class="flow-name-wrap"><div id="flowNameView" class="flow-name-view" hidden><button id="flowNameText" class="flow-name-text" type="button" data-action="edit-flow-name" data-i18n-title="flow.editName" title="Edit Flow name"></button><button class="flow-name-edit" type="button" data-action="edit-flow-name" data-i18n-title="flow.editName" data-i18n-aria="flow.editName" title="Edit Flow name" aria-label="Edit Flow name">✎</button></div><input id="flowName" maxlength="100" data-i18n-placeholder="flow.namePlaceholder" data-i18n-title="flow.nameAliasHint" placeholder="My automation"></div></div></div>
|
||||
<div class="flow-editor-actions"><button class="secondary" type="button" data-action="flow-templates" data-i18n="flow.templates">Templates</button><button class="secondary" type="button" data-action="import-flow" data-i18n="flow.import">Import</button><button class="secondary" type="button" data-action="export-flow" data-i18n="flow.export">Export</button><button class="secondary" type="button" data-action="flow-dry-run" data-i18n="flow.dryRun">Dry-run</button><button class="secondary" type="button" data-action="flow-logs" data-i18n="flow.logs">Logs</button><label class="flow-enabled"><input type="checkbox" id="flowEnabled" checked><span data-i18n="common.enabled">Enabled</span></label><span id="flowCompileStatus" class="badge"></span><button class="primary" type="button" data-action="save-flow" data-i18n="actions.save">Save</button></div>
|
||||
</header>
|
||||
<div class="flow-editor-body">
|
||||
|
||||
+53
-7
@@ -111,12 +111,48 @@ function flowDraftFrom(flow) {
|
||||
};
|
||||
}
|
||||
|
||||
function flowCurrentName() {
|
||||
const input = $('#flowName');
|
||||
if (input && !input.hidden) return input.value.trim();
|
||||
return String(app.flowDraft?.name || '').trim();
|
||||
}
|
||||
|
||||
function renderFlowNameMode() {
|
||||
const input = $('#flowName'), view = $('#flowNameView'), text = $('#flowNameText');
|
||||
if (!input || !view || !text || !app.flowDraft) return;
|
||||
const name = String(app.flowDraft.name || '').trim();
|
||||
input.value = name; text.textContent = name || tr('flow.newDefaultName');
|
||||
const editing = app.flowNameEditing === true;
|
||||
input.hidden = !editing; view.hidden = editing;
|
||||
}
|
||||
|
||||
function editFlowName({ select = true } = {}) {
|
||||
if (!app.flowDraft) return;
|
||||
app.flowNameEditing = true; renderFlowNameMode();
|
||||
const input = $('#flowName');
|
||||
requestAnimationFrame(() => { input?.focus(); if (select) input?.select(); });
|
||||
}
|
||||
|
||||
function commitFlowName({ keepEditingOnEmpty = true } = {}) {
|
||||
if (!app.flowDraft) return false;
|
||||
const input = $('#flowName'); if (!input) return false;
|
||||
const name = input.value.trim();
|
||||
if (!name) {
|
||||
if (keepEditingOnEmpty) { app.flowNameEditing = true; input.focus(); }
|
||||
return false;
|
||||
}
|
||||
if (app.flowDraft.name !== name) { app.flowDraft.name = name; app.flowDirty = true; }
|
||||
app.flowNameEditing = false; renderFlowNameMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
function openFlowEditor(id = '', { push = true } = {}) {
|
||||
const flow = id ? app.flows.find(item => item.id === id) : null;
|
||||
if (id && !flow) return toast(tr('flow.notFound'), true);
|
||||
app.flowDraft = flowDraftFrom(flow);
|
||||
app.flowSelectedNodeId = null; app.flowSelectedNodeIds = []; app.flowConnectFrom = null; app.flowDirty = false;
|
||||
$('#flowName').value = app.flowDraft.name || '';
|
||||
app.flowNameEditing = !flow;
|
||||
renderFlowNameMode();
|
||||
$('#flowEnabled').checked = app.flowDraft.enabled !== false;
|
||||
$('#flowEditor').hidden = false;
|
||||
document.body.classList.add('flow-editor-open');
|
||||
@@ -129,7 +165,7 @@ function closeFlowEditor({ push = true, force = false } = {}) {
|
||||
if (!force && app.flowDirty && !confirm(tr('confirm.discardChanges'))) return false;
|
||||
stopFlowSharedInputValueRefresh();
|
||||
$('#flowEditor').hidden = true; document.body.classList.remove('flow-editor-open');
|
||||
app.flowDraft = null; app.flowSelectedNodeId = null; app.flowSelectedNodeIds = []; app.flowConnectFrom = null; app.flowDirty = false;
|
||||
app.flowDraft = null; app.flowSelectedNodeId = null; app.flowSelectedNodeIds = []; app.flowConnectFrom = null; app.flowDirty = false; app.flowNameEditing = false;
|
||||
if (push) updateBrowserUrl('/flows');
|
||||
return true;
|
||||
}
|
||||
@@ -324,7 +360,7 @@ function renderFlowEditor() {
|
||||
}).join('');
|
||||
$('#flowEmptyHint').hidden = draft.nodes.length > 0;
|
||||
const selectionCount = $('#flowSelectionCount'); if (selectionCount) selectionCount.textContent = (app.flowSelectedNodeIds || []).length ? tr('flow.selectedCount', { count:(app.flowSelectedNodeIds || []).length }) : '';
|
||||
renderFlowEdges(); renderFlowInspector(); renderFlowInterpretation(); renderFlowRuntimeInfo(); refreshFlowSharedInputCurrentValues();
|
||||
renderFlowNameMode(); renderFlowEdges(); renderFlowInspector(); renderFlowInterpretation(); renderFlowRuntimeInfo(); refreshFlowSharedInputCurrentValues();
|
||||
const status = $('#flowCompileStatus');
|
||||
status.textContent = draft.draft ? tr('flow.draftStatus') : tr('flow.compileCount', { schedules: draft.compiled_schedule_ids?.length || 0, automations: draft.compiled_automation_ids?.length || 0 });
|
||||
status.classList.toggle('flow-draft-badge', draft.draft === true);
|
||||
@@ -526,7 +562,7 @@ function removeFlowEdge(id) {
|
||||
function flowSourcePayload() {
|
||||
if (!app.flowDraft) return null;
|
||||
return {
|
||||
name: ($('#flowName')?.value || app.flowDraft.name || '').trim(),
|
||||
name: flowCurrentName(),
|
||||
enabled: $('#flowEnabled')?.checked !== false,
|
||||
draft: app.flowDraft.draft === true,
|
||||
description: app.flowDraft.description || '',
|
||||
@@ -808,7 +844,7 @@ function applyFlowTemplate(key) {
|
||||
const graph = materializeFlowPreset(preset);
|
||||
app.flowDraft.nodes = graph.nodes; app.flowDraft.edges = graph.edges;
|
||||
app.flowDraft.description = flowPresetText(preset.description); app.flowSelectedNodeId = null; app.flowSelectedNodeIds = []; app.flowDirty = true;
|
||||
if (!app.flowDraft.id || $('#flowName').value === tr('flow.newDefaultName')) $('#flowName').value = flowPresetText(preset.name) || preset.id;
|
||||
if (!app.flowDraft.id || flowCurrentName() === tr('flow.newDefaultName')) { app.flowDraft.name = flowPresetText(preset.name) || preset.id; if (app.flowNameEditing) $('#flowName').value = app.flowDraft.name; renderFlowNameMode(); }
|
||||
markFlowPresetRecent(preset.id);
|
||||
$('#flowTemplateDialog')?.close(); renderFlowEditor();
|
||||
}
|
||||
@@ -906,14 +942,15 @@ async function persistFlowDraft(body) {
|
||||
|
||||
async function applySavedFlow(saved, messageKey) {
|
||||
const index = app.flows.findIndex(flow => flow.id === saved.id); if (index >= 0) app.flows[index] = saved; else app.flows.push(saved);
|
||||
app.flowDraft = flowDraftFrom(saved); app.flowDirty = false;
|
||||
app.flowDraft = flowDraftFrom(saved); app.flowDirty = false; app.flowNameEditing = false;
|
||||
$('#flowEnabled').checked = saved.enabled === true;
|
||||
renderFlows(); renderFlowEditor(); updateBrowserUrl(`/flows/${saved.id}`, true); await loadBootstrap(); toast(tr(messageKey));
|
||||
}
|
||||
|
||||
async function saveFlow() {
|
||||
if (!app.flowDraft) return;
|
||||
const name = $('#flowName').value.trim(); if (!name) return toast(tr('flow.nameRequired'), true);
|
||||
if (app.flowNameEditing && !commitFlowName()) return toast(tr('flow.nameRequired'), true);
|
||||
const name = flowCurrentName(); if (!name) return toast(tr('flow.nameRequired'), true);
|
||||
const body = { name, enabled: $('#flowEnabled').checked, draft: false, description: app.flowDraft.description || '', nodes: app.flowDraft.nodes, edges: app.flowDraft.edges };
|
||||
if (app.flowDraft.id) body.expected_revision = Number(app.flowDraft.revision || 0);
|
||||
try {
|
||||
@@ -1079,6 +1116,7 @@ document.addEventListener('click', event => {
|
||||
else if (action === 'flow-logs') openFlowLogs();
|
||||
else if (action === 'flow-select-all') selectAllFlowNodes();
|
||||
else if (action === 'flow-clear-selection') clearFlowSelection();
|
||||
else if (action === 'edit-flow-name') editFlowName();
|
||||
else if (action === 'flow-shared-input-settings') {
|
||||
if (!showView('homeassistant')) return;
|
||||
requestAnimationFrame(() => {
|
||||
@@ -1097,5 +1135,13 @@ document.addEventListener('change', event => {
|
||||
$('#flowImportFile')?.addEventListener('change', event => importFlowFile(event.target.files?.[0]));
|
||||
$('#flowTemplateSearch')?.addEventListener('input', event => { flowPresetSearch = event.target.value || ''; renderFlowPresetBrowser(flowPresetActiveCategory); });
|
||||
$('#flowName')?.addEventListener('input', () => { if (app.flowDraft) app.flowDirty = true; });
|
||||
$('#flowName')?.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') { event.preventDefault(); commitFlowName(); }
|
||||
else if (event.key === 'Escape') { event.preventDefault(); $('#flowName').value = app.flowDraft?.name || ''; app.flowNameEditing = false; renderFlowNameMode(); }
|
||||
});
|
||||
$('#flowName')?.addEventListener('blur', () => {
|
||||
if (!app.flowDraft || !app.flowNameEditing) return;
|
||||
if ($('#flowName').value.trim()) commitFlowName();
|
||||
});
|
||||
$('#flowEnabled')?.addEventListener('change', () => { if (app.flowDraft) app.flowDirty = true; });
|
||||
window.addEventListener('resize', () => { if (!$('#flowEditor')?.hidden) renderFlowEdges(); });
|
||||
|
||||
+9
-1
@@ -6180,7 +6180,15 @@ body.flow-editor-open { overflow:hidden; }
|
||||
.flow-editor-bar { min-height:76px; display:flex; align-items:center; justify-content:space-between; gap:20px; padding:10px 18px; border-bottom:1px solid var(--line); background:var(--surface); }
|
||||
.flow-editor-title { display:flex; align-items:center; gap:12px; min-width:0; }
|
||||
.flow-editor-title > div { display:grid; gap:2px; min-width:0; }
|
||||
.flow-name-wrap { min-width:280px; min-height:36px; display:flex; align-items:center; }
|
||||
.flow-name-view { display:flex; align-items:center; gap:6px; min-width:0; }
|
||||
.flow-name-view[hidden] { display:none !important; }
|
||||
.flow-name-text { appearance:none; border:0; background:transparent; color:var(--text); font:inherit; font-weight:750; font-size:1.12rem; line-height:1.25; padding:4px 2px; min-width:0; max-width:min(52vw,620px); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; cursor:text; text-align:left; }
|
||||
.flow-name-text:hover { color:var(--accent); }
|
||||
.flow-name-edit { appearance:none; border:0; background:transparent; color:var(--muted); width:28px; height:28px; border-radius:7px; padding:0; cursor:pointer; display:grid; place-items:center; opacity:.62; transition:opacity .15s ease,background .15s ease,color .15s ease; }
|
||||
.flow-name-view:hover .flow-name-edit,.flow-name-edit:focus-visible { opacity:1; color:var(--accent); background:var(--accent-soft); outline:none; }
|
||||
.flow-editor-title input { border:1px solid var(--line-strong); background:var(--input-bg); color:var(--text); font-weight:750; font-size:1.12rem; padding:7px 10px; min-width:280px; outline:none; border-radius:9px; box-shadow:inset 0 0 0 1px transparent; transition:border-color .15s ease,box-shadow .15s ease,background .15s ease; }
|
||||
.flow-editor-title input[hidden] { display:none !important; }
|
||||
.flow-editor-title input:hover { border-color:var(--accent-border); background:var(--surface-2); }
|
||||
.flow-editor-title input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); background:var(--input-bg); }
|
||||
.flow-draft-badge { border-color:var(--orange); background:color-mix(in srgb,var(--orange) 14%,var(--surface)); color:var(--text); }
|
||||
@@ -6236,7 +6244,7 @@ body.flow-editor-open { overflow:hidden; }
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.flow-editor-body { grid-template-columns:180px minmax(360px,1fr) 260px; }
|
||||
.flow-editor-title input { min-width:180px; }
|
||||
.flow-name-wrap,.flow-editor-title input { min-width:180px; }
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.flow-editor-bar { align-items:flex-start; flex-direction:column; }
|
||||
|
||||
Reference in New Issue
Block a user