From 622390d0561c66f0fec38d787670ba053245d01a Mon Sep 17 00:00:00 2001 From: Youzini-afk <13153778771cx@gmail.com> Date: Thu, 9 Apr 2026 21:18:27 +0800 Subject: [PATCH] feat: redesign regex editor as single column --- style.css | 133 ++++++++++++++++- ui/panel.js | 403 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 454 insertions(+), 82 deletions(-) diff --git a/style.css b/style.css index 8569025..c03944d 100644 --- a/style.css +++ b/style.css @@ -2694,12 +2694,6 @@ top: auto; } -.bme-task-regex-top { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 14px; -} - .bme-task-block-count { font-size: 11px; color: var(--bme-on-surface-dim); @@ -3063,6 +3057,126 @@ margin-top: 8px; } +/* ═══════ Single-column Regex Editor ═══════ */ + +.bme-regex-settings-stack { + display: flex; + flex-direction: column; + gap: 14px; +} + +.bme-regex-settings-card .bme-task-section-label { + margin-top: 0; + margin-bottom: 10px; +} + +.bme-regex-rule-card .bme-task-empty, +.bme-regex-rule-card .bme-task-note { + margin: 0; +} + +.bme-regex-rule-rows { + display: flex; + flex-direction: column; + gap: 4px; +} + +.bme-regex-rule-row { + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 10px; + background: rgba(255, 255, 255, 0.025); + transition: border-color 0.15s, box-shadow 0.15s; +} + +.bme-regex-rule-row:hover { + border-color: rgba(255, 255, 255, 0.12); +} + +.bme-regex-rule-row.is-expanded { + border-color: var(--bme-primary); + box-shadow: 0 0 0 1px var(--bme-primary); +} + +.bme-regex-rule-row.is-disabled { + opacity: 0.56; +} + +.bme-regex-rule-row.dragging { + opacity: 0.35; +} + +.bme-regex-rule-row.drag-over-top { + border-top: 2px solid var(--bme-primary); + margin-top: -1px; +} + +.bme-regex-rule-row.drag-over-bottom { + border-bottom: 2px solid var(--bme-primary); + margin-bottom: -1px; +} + +.bme-regex-rule-row-header { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + min-height: 44px; + cursor: pointer; + user-select: none; + flex-wrap: wrap; +} + +.bme-regex-rule-name { + flex: 0 1 220px; + min-width: 120px; + font-size: 13px; + font-weight: 600; + color: var(--bme-on-surface); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.bme-regex-rule-status { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 42px; + padding: 2px 7px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + white-space: nowrap; + flex-shrink: 0; +} + +.bme-regex-rule-status.is-enabled { + background: rgba(80, 180, 120, 0.18); + color: #80d0a0; +} + +.bme-regex-rule-status.is-disabled { + background: rgba(160, 160, 180, 0.14); + color: var(--bme-on-surface-dim); +} + +.bme-regex-rule-preview { + flex: 1 1 260px; + min-width: 0; + font-size: 11px; + line-height: 1.45; + color: var(--bme-on-surface-dim); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.bme-regex-rule-expand { + border-top: 1px solid rgba(255, 255, 255, 0.06); + padding: 12px; + animation: bme-block-expand-in 0.2s ease; +} + @keyframes bme-block-expand-in { from { opacity: 0; @@ -3934,7 +4048,6 @@ .bme-theme-card-grid, .bme-task-field-grid, .bme-task-expand-row2, - .bme-task-regex-top, .bme-task-debug-grid { grid-template-columns: 1fr; } @@ -4131,6 +4244,12 @@ max-width: 100%; } + .bme-regex-rule-preview { + flex: 1 1 100%; + order: 10; + margin-left: 28px; + } + /* Dashboard 统计卡片横向滚动 */ .bme-stats-grid { display: flex; diff --git a/ui/panel.js b/ui/panel.js index 4c91ffc..4a48d62 100644 --- a/ui/panel.js +++ b/ui/panel.js @@ -282,6 +282,8 @@ let currentTaskProfileTabId = "generation"; let currentTaskProfileBlockId = ""; let currentTaskProfileDragBlockId = ""; let currentTaskProfileRuleId = ""; +let currentTaskProfileDragRuleId = ""; +let currentTaskProfileDragRuleIsGlobal = false; let showGlobalRegexPanel = false; let currentGlobalRegexRuleId = ""; let currentCognitionOwnerKey = ""; @@ -5060,6 +5062,74 @@ function _bindTaskProfileWorkspace() { currentTaskProfileDragBlockId = ""; _clearTaskBlockDragIndicators(workspace); }); + workspace.addEventListener("dragstart", (event) => { + const target = event.target; + if (!(target instanceof HTMLElement)) return; + const handle = target.closest(".bme-regex-drag-handle"); + const row = target.closest(".bme-regex-rule-row"); + if (!handle || !(row instanceof HTMLElement)) return; + const ruleId = String(row.dataset.ruleId || "").trim(); + if (!ruleId) return; + currentTaskProfileDragRuleId = ruleId; + currentTaskProfileDragRuleIsGlobal = _isGlobalRegexPanelTarget(row); + if (event.dataTransfer) { + event.dataTransfer.effectAllowed = "move"; + event.dataTransfer.dropEffect = "move"; + event.dataTransfer.setData("text/plain", ruleId); + } + window.requestAnimationFrame(() => { + row.classList.add("dragging"); + }); + }); + workspace.addEventListener("dragover", (event) => { + const target = event.target; + if (!(target instanceof HTMLElement) || !currentTaskProfileDragRuleId) return; + const row = target.closest(".bme-regex-rule-row"); + if (!(row instanceof HTMLElement)) return; + const isGlobalRow = _isGlobalRegexPanelTarget(row); + if (isGlobalRow !== currentTaskProfileDragRuleIsGlobal) return; + event.preventDefault(); + if (event.dataTransfer) { + event.dataTransfer.dropEffect = "move"; + } + const position = _getRegexRuleDropPosition(row, event.clientY); + _setRegexRuleDragIndicator(workspace, row, position); + }); + workspace.addEventListener("dragleave", (event) => { + const target = event.target; + if (!(target instanceof HTMLElement)) return; + const row = target.closest(".bme-regex-rule-row"); + if (!(row instanceof HTMLElement)) return; + const relatedTarget = event.relatedTarget; + if (relatedTarget instanceof Node && row.contains(relatedTarget)) { + return; + } + row.classList.remove("drag-over-top", "drag-over-bottom"); + }); + workspace.addEventListener("drop", (event) => { + const target = event.target; + if (!(target instanceof HTMLElement)) return; + const row = target.closest(".bme-regex-rule-row"); + if (!(row instanceof HTMLElement)) return; + const isGlobalRow = _isGlobalRegexPanelTarget(row); + if (isGlobalRow !== currentTaskProfileDragRuleIsGlobal) return; + event.preventDefault(); + const sourceId = + currentTaskProfileDragRuleId || + String(event.dataTransfer?.getData("text/plain") || "").trim(); + const targetId = String(row.dataset.ruleId || "").trim(); + const position = _getRegexRuleDropPosition(row, event.clientY); + _clearRegexRuleDragIndicators(workspace); + currentTaskProfileDragRuleId = ""; + currentTaskProfileDragRuleIsGlobal = false; + if (!sourceId || !targetId || sourceId === targetId) return; + _reorderRegexRules(sourceId, targetId, position, isGlobalRow); + }); + workspace.addEventListener("dragend", () => { + currentTaskProfileDragRuleId = ""; + currentTaskProfileDragRuleIsGlobal = false; + _clearRegexRuleDragIndicators(workspace); + }); workspace.dataset.bmeBound = "true"; } @@ -5280,6 +5350,13 @@ function _handleTaskProfileWorkspaceInput(event) { } else { _persistSelectedRegexRuleField(target, false); } + return; + } + + if (target.matches("[data-regex-rule-row-enabled]")) { + const ruleId = String(target.dataset.ruleId || "").trim(); + if (!ruleId) return; + _persistRegexRuleEnabledById(ruleId, Boolean(target.checked), isGlobalRegexPanel, false); } } @@ -5353,6 +5430,13 @@ function _handleTaskProfileWorkspaceChange(event) { } else { _persistSelectedRegexRuleField(target, true); } + return; + } + + if (target.matches("[data-regex-rule-row-enabled]")) { + const ruleId = String(target.dataset.ruleId || "").trim(); + if (!ruleId) return; + _persistRegexRuleEnabledById(ruleId, Boolean(target.checked), isGlobalRegexPanel, true); } } @@ -5392,10 +5476,10 @@ function _getTaskProfileWorkspaceState(settings = _getSettings?.() || {}) { if (currentTaskProfileBlockId && !blocks.some((block) => block.id === currentTaskProfileBlockId)) { currentTaskProfileBlockId = blocks[0]?.id || ""; } - if (!regexRules.some((rule) => rule.id === currentTaskProfileRuleId)) { + if (currentTaskProfileRuleId && !regexRules.some((rule) => rule.id === currentTaskProfileRuleId)) { currentTaskProfileRuleId = regexRules[0]?.id || ""; } - if (!globalRegexRules.some((rule) => rule.id === currentGlobalRegexRuleId)) { + if (currentGlobalRegexRuleId && !globalRegexRules.some((rule) => rule.id === currentGlobalRegexRuleId)) { currentGlobalRegexRuleId = globalRegexRules[0]?.id || ""; } @@ -6112,6 +6196,26 @@ async function _handleTaskProfileWorkspaceClick(event) { _refreshTaskProfileWorkspace(); return; } + case "toggle-regex-rule-expand": { + const originEl = event.target; + if ( + originEl.closest(".bme-task-row-toggle") || + originEl.closest(".bme-task-row-btn-danger") || + originEl.closest(".bme-regex-drag-handle") + ) { + return; + } + const ruleId = actionEl.dataset.ruleId || ""; + if (_isGlobalRegexPanelTarget(actionEl)) { + currentGlobalRegexRuleId = + currentGlobalRegexRuleId === ruleId ? "" : ruleId; + } else { + currentTaskProfileRuleId = + currentTaskProfileRuleId === ruleId ? "" : ruleId; + } + _refreshTaskProfileWorkspace(); + return; + } case "select-regex-rule": if (_isGlobalRegexPanelTarget(actionEl)) { currentGlobalRegexRuleId = actionEl.dataset.ruleId || ""; @@ -6560,7 +6664,6 @@ function _renderTaskRegexTab(state, options = {}) { const selectedRule = options.selectedRule === undefined ? state.selectedRule : options.selectedRule; const normalizedStages = normalizeTaskRegexStages(regex.stages || {}); - const selectAction = options.selectAction || "select-regex-rule"; const deleteAction = options.deleteAction || "delete-regex-rule"; const addAction = options.addAction || "add-regex-rule"; const addButtonLabel = options.addButtonLabel || "+ 新增规则"; @@ -6578,6 +6681,9 @@ function _renderTaskRegexTab(state, options = {}) { const emptyText = options.emptyText || "当前预设还没有本地正则规则。"; const defaultNamePrefix = options.defaultNamePrefix || "本地规则"; const headerExtraActions = options.extraHeaderActions || ""; + const enableToggleTitle = options.enableToggleTitle || "启用任务正则"; + const enableToggleDesc = + options.enableToggleDesc || "关闭后当前配置不执行任何任务级正则。"; const editorState = { ...state, selectedRule, @@ -6585,8 +6691,8 @@ function _renderTaskRegexTab(state, options = {}) { return `