mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
feat: redesign prompt block editor as single column
This commit is contained in:
264
style.css
264
style.css
@@ -2683,15 +2683,15 @@
|
||||
}
|
||||
|
||||
.bme-task-editor-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.bme-task-editor-grid > .bme-config-card:last-child {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
position: static;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.bme-task-regex-top {
|
||||
@@ -2812,6 +2812,260 @@
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
/* ═══════ Single-column Block Row (EW-style) ═══════ */
|
||||
|
||||
.bme-task-block-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.bme-task-block-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-task-block-row:hover {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.bme-task-block-row.is-expanded {
|
||||
border-color: var(--bme-primary);
|
||||
box-shadow: 0 0 0 1px var(--bme-primary);
|
||||
}
|
||||
|
||||
.bme-task-block-row.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bme-task-block-row.dragging {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.bme-task-block-row.drag-over-top {
|
||||
border-top: 2px solid var(--bme-primary);
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.bme-task-block-row.drag-over-bottom {
|
||||
border-bottom: 2px solid var(--bme-primary);
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
/* Row header */
|
||||
.bme-task-block-row-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
min-height: 44px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Drag handle */
|
||||
.bme-task-drag-handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
color: var(--bme-on-surface-dim);
|
||||
opacity: 0.45;
|
||||
cursor: grab;
|
||||
flex-shrink: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.bme-task-drag-handle:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bme-task-drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* Block type icon */
|
||||
.bme-task-block-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
color: var(--bme-on-surface-dim);
|
||||
}
|
||||
|
||||
/* Block name */
|
||||
.bme-task-block-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--bme-on-surface);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Badges (role / inject) */
|
||||
.bme-task-block-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
color: var(--bme-on-surface-dim);
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.bme-badge-role-system {
|
||||
background: rgba(140, 140, 160, 0.18);
|
||||
color: #b0b0c0;
|
||||
}
|
||||
|
||||
.bme-badge-role-user {
|
||||
background: rgba(160, 100, 220, 0.2);
|
||||
color: #c8a0f0;
|
||||
}
|
||||
|
||||
.bme-badge-role-assistant {
|
||||
background: rgba(80, 180, 120, 0.2);
|
||||
color: #80d0a0;
|
||||
}
|
||||
|
||||
/* Row spacer */
|
||||
.bme-task-block-row-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Row action buttons (edit / delete) */
|
||||
.bme-task-row-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--bme-on-surface-dim);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
font-size: 13px;
|
||||
transition: background 0.12s, color 0.12s;
|
||||
}
|
||||
|
||||
.bme-task-row-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--bme-on-surface);
|
||||
}
|
||||
|
||||
.bme-task-row-btn-danger:hover {
|
||||
background: rgba(220, 80, 80, 0.15);
|
||||
color: #f08080;
|
||||
}
|
||||
|
||||
/* Inline toggle switch (EW-style yellow) */
|
||||
.bme-task-row-toggle {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: 38px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bme-task-row-toggle input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.bme-task-row-toggle-slider {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.bme-task-row-toggle-slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 3px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #888;
|
||||
transition: transform 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.bme-task-row-toggle input:checked + .bme-task-row-toggle-slider {
|
||||
background: rgba(220, 180, 50, 0.35);
|
||||
}
|
||||
|
||||
.bme-task-row-toggle input:checked + .bme-task-row-toggle-slider::before {
|
||||
transform: translateX(16px);
|
||||
background: #e0b830;
|
||||
}
|
||||
|
||||
/* Expand editor area */
|
||||
.bme-task-block-expand {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
padding: 12px;
|
||||
animation: bme-block-expand-in 0.2s ease;
|
||||
}
|
||||
|
||||
.bme-task-expand-row2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bme-task-block-expand .bme-config-row {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.bme-task-block-expand .bme-config-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bme-task-block-expand .bme-config-textarea {
|
||||
min-height: 120px;
|
||||
max-height: 400px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.bme-task-expand-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@keyframes bme-block-expand-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
max-height: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bme-regex-preview-screen {
|
||||
gap: 14px;
|
||||
}
|
||||
@@ -3670,7 +3924,7 @@
|
||||
.bme-config-grid-2,
|
||||
.bme-theme-card-grid,
|
||||
.bme-task-field-grid,
|
||||
.bme-task-editor-grid,
|
||||
.bme-task-expand-row2,
|
||||
.bme-task-regex-top,
|
||||
.bme-task-debug-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
404
ui/panel.js
404
ui/panel.js
@@ -280,6 +280,7 @@ let currentConfigSectionId = "toggles";
|
||||
let currentTaskProfileTaskType = "extract";
|
||||
let currentTaskProfileTabId = "generation";
|
||||
let currentTaskProfileBlockId = "";
|
||||
let currentTaskProfileDragBlockId = "";
|
||||
let currentTaskProfileRuleId = "";
|
||||
let showGlobalRegexPanel = false;
|
||||
let currentGlobalRegexRuleId = "";
|
||||
@@ -4998,6 +4999,67 @@ function _bindTaskProfileWorkspace() {
|
||||
workspace.addEventListener("change", (event) => {
|
||||
_handleTaskProfileWorkspaceChange(event);
|
||||
});
|
||||
workspace.addEventListener("dragstart", (event) => {
|
||||
const target = event.target;
|
||||
if (!(target instanceof HTMLElement)) return;
|
||||
const handle = target.closest(".bme-task-drag-handle");
|
||||
const row = target.closest(".bme-task-block-row");
|
||||
if (!handle || !(row instanceof HTMLElement)) return;
|
||||
const blockId = String(row.dataset.blockId || "").trim();
|
||||
if (!blockId) return;
|
||||
currentTaskProfileDragBlockId = blockId;
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
event.dataTransfer.setData("text/plain", blockId);
|
||||
}
|
||||
window.requestAnimationFrame(() => {
|
||||
row.classList.add("dragging");
|
||||
});
|
||||
});
|
||||
workspace.addEventListener("dragover", (event) => {
|
||||
const target = event.target;
|
||||
if (!(target instanceof HTMLElement) || !currentTaskProfileDragBlockId) return;
|
||||
const row = target.closest(".bme-task-block-row");
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
event.preventDefault();
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
const position = _getTaskBlockDropPosition(row, event.clientY);
|
||||
_setTaskBlockDragIndicator(workspace, row, position);
|
||||
});
|
||||
workspace.addEventListener("dragleave", (event) => {
|
||||
const target = event.target;
|
||||
if (!(target instanceof HTMLElement)) return;
|
||||
const row = target.closest(".bme-task-block-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-task-block-row");
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
event.preventDefault();
|
||||
const sourceId =
|
||||
currentTaskProfileDragBlockId ||
|
||||
String(event.dataTransfer?.getData("text/plain") || "").trim();
|
||||
const targetId = String(row.dataset.blockId || "").trim();
|
||||
const position = _getTaskBlockDropPosition(row, event.clientY);
|
||||
_clearTaskBlockDragIndicators(workspace);
|
||||
currentTaskProfileDragBlockId = "";
|
||||
if (!sourceId || !targetId || sourceId === targetId) return;
|
||||
_reorderTaskBlocks(sourceId, targetId, position);
|
||||
});
|
||||
workspace.addEventListener("dragend", () => {
|
||||
currentTaskProfileDragBlockId = "";
|
||||
_clearTaskBlockDragIndicators(workspace);
|
||||
});
|
||||
workspace.dataset.bmeBound = "true";
|
||||
}
|
||||
|
||||
@@ -5327,7 +5389,7 @@ function _getTaskProfileWorkspaceState(settings = _getSettings?.() || {}) {
|
||||
? profile.regex.localRules
|
||||
: [];
|
||||
|
||||
if (!blocks.some((block) => block.id === currentTaskProfileBlockId)) {
|
||||
if (currentTaskProfileBlockId && !blocks.some((block) => block.id === currentTaskProfileBlockId)) {
|
||||
currentTaskProfileBlockId = blocks[0]?.id || "";
|
||||
}
|
||||
if (!regexRules.some((rule) => rule.id === currentTaskProfileRuleId)) {
|
||||
@@ -6035,6 +6097,21 @@ async function _handleTaskProfileWorkspaceClick(event) {
|
||||
currentTaskProfileBlockId = actionEl.dataset.blockId || "";
|
||||
_refreshTaskProfileWorkspace();
|
||||
return;
|
||||
case "toggle-block-expand": {
|
||||
// Ignore if the click originated from a toggle switch, delete button, or drag handle
|
||||
const originEl = event.target;
|
||||
if (originEl.closest(".bme-task-row-toggle") || originEl.closest(".bme-task-row-btn-danger") || originEl.closest(".bme-task-drag-handle")) {
|
||||
return;
|
||||
}
|
||||
const blockId = actionEl.dataset.blockId || "";
|
||||
if (currentTaskProfileBlockId === blockId) {
|
||||
currentTaskProfileBlockId = "";
|
||||
} else {
|
||||
currentTaskProfileBlockId = blockId;
|
||||
}
|
||||
_refreshTaskProfileWorkspace();
|
||||
return;
|
||||
}
|
||||
case "select-regex-rule":
|
||||
if (_isGlobalRegexPanelTarget(actionEl)) {
|
||||
currentGlobalRegexRuleId = actionEl.dataset.ruleId || "";
|
||||
@@ -6085,6 +6162,16 @@ async function _handleTaskProfileWorkspaceClick(event) {
|
||||
return { selectBlockId: block.id };
|
||||
});
|
||||
return;
|
||||
case "toggle-block-enabled-cb":
|
||||
_updateCurrentTaskProfile((draft) => {
|
||||
const blocks = _sortTaskBlocks(draft.blocks);
|
||||
const block = blocks.find((item) => item.id === actionEl.dataset.blockId);
|
||||
if (!block) return null;
|
||||
block.enabled = actionEl.checked;
|
||||
draft.blocks = _normalizeTaskBlocks(blocks);
|
||||
return { selectBlockId: currentTaskProfileBlockId };
|
||||
});
|
||||
return;
|
||||
case "delete-block":
|
||||
_deleteTaskBlock(actionEl.dataset.blockId);
|
||||
return;
|
||||
@@ -6365,57 +6452,40 @@ function _renderTaskProfileWorkspace(state) {
|
||||
}
|
||||
function _renderTaskPromptTab(state) {
|
||||
return `
|
||||
<div class="bme-task-editor-grid">
|
||||
<div class="bme-config-card">
|
||||
<div class="bme-config-card-head">
|
||||
<div>
|
||||
<div class="bme-config-card-title">Prompt 块列表</div>
|
||||
<div class="bme-config-card-subtitle">
|
||||
通过顺序、启停与角色控制最终请求的编排方式。
|
||||
<div class="bme-task-toolbar-row">
|
||||
<div class="bme-task-toolbar-inline">
|
||||
<button class="bme-config-secondary-btn" data-task-action="add-custom-block" type="button">
|
||||
+ 自定义块
|
||||
</button>
|
||||
<span class="bme-task-action-sep"></span>
|
||||
<select id="bme-task-builtin-select" class="bme-config-input bme-task-builtin-select">
|
||||
${state.builtinBlockDefinitions
|
||||
.map(
|
||||
(item) => `
|
||||
<option value="${_escAttr(item.sourceKey)}">
|
||||
${_escHtml(item.name)}
|
||||
</option>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</select>
|
||||
<button class="bme-config-secondary-btn" data-task-action="add-builtin-block" type="button">
|
||||
+ 内置块
|
||||
</button>
|
||||
</div>
|
||||
<span class="bme-task-block-count">${state.blocks.length} 个块</span>
|
||||
</div>
|
||||
|
||||
<div class="bme-task-block-rows">
|
||||
${state.blocks.length
|
||||
? state.blocks
|
||||
.map((block, index) => _renderTaskBlockRow(block, index, state))
|
||||
.join("")
|
||||
: `
|
||||
<div class="bme-task-empty">
|
||||
当前预设还没有块。可以先新增一个自定义块或内置块。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bme-task-toolbar-row">
|
||||
<div class="bme-task-toolbar-inline">
|
||||
<button class="bme-config-secondary-btn" data-task-action="add-custom-block" type="button">
|
||||
+ 自定义块
|
||||
</button>
|
||||
<span class="bme-task-action-sep"></span>
|
||||
<select id="bme-task-builtin-select" class="bme-config-input bme-task-builtin-select">
|
||||
${state.builtinBlockDefinitions
|
||||
.map(
|
||||
(item) => `
|
||||
<option value="${_escAttr(item.sourceKey)}">
|
||||
${_escHtml(item.name)}
|
||||
</option>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</select>
|
||||
<button class="bme-config-secondary-btn" data-task-action="add-builtin-block" type="button">
|
||||
+ 内置块
|
||||
</button>
|
||||
</div>
|
||||
<span class="bme-task-block-count">${state.blocks.length} 个块</span>
|
||||
</div>
|
||||
|
||||
<div class="bme-task-list">
|
||||
${state.blocks.length
|
||||
? state.blocks
|
||||
.map((block, index) => _renderTaskBlockListItem(block, index, state))
|
||||
.join("")
|
||||
: `
|
||||
<div class="bme-task-empty">
|
||||
当前预设还没有块。可以先新增一个自定义块或内置块。
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bme-config-card">
|
||||
${_renderTaskBlockEditor(state)}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -7388,73 +7458,93 @@ function _stringifyDebugValue(value) {
|
||||
}
|
||||
}
|
||||
|
||||
function _renderTaskBlockListItem(block, index, state) {
|
||||
const isSelected = block.id === state.selectedBlock?.id;
|
||||
function _getBlockTypeIcon(type) {
|
||||
switch (type) {
|
||||
case "builtin": return `<i class="fa-solid fa-thumbtack"></i>`;
|
||||
case "legacyPrompt": return `<i class="fa-solid fa-scroll"></i>`;
|
||||
default: return `<i class="fa-regular fa-file-lines"></i>`;
|
||||
}
|
||||
}
|
||||
|
||||
function _getInjectModeLabel(mode) {
|
||||
switch (mode) {
|
||||
case "append": return "追加";
|
||||
case "relative":
|
||||
default: return "相对";
|
||||
}
|
||||
}
|
||||
|
||||
function _renderTaskBlockRow(block, index, state) {
|
||||
const isExpanded = block.id === state.selectedBlock?.id;
|
||||
const roleClass = `bme-badge-role-${block.role || "system"}`;
|
||||
const disabledClass = block.enabled ? "" : "is-disabled";
|
||||
const expandedClass = isExpanded ? "is-expanded" : "";
|
||||
|
||||
return `
|
||||
<div class="bme-task-list-entry">
|
||||
<button
|
||||
class="bme-task-list-item ${isSelected ? "active" : ""}"
|
||||
data-task-action="select-block"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
type="button"
|
||||
>
|
||||
<span class="bme-task-list-index">#${index + 1}</span>
|
||||
<span class="bme-task-list-copy">
|
||||
<span class="bme-task-list-title">
|
||||
${_escHtml(block.name || _getTaskBlockTypeLabel(block.type))}
|
||||
</span>
|
||||
<span class="bme-task-list-meta">
|
||||
${_escHtml(_getTaskBlockTypeLabel(block.type))} · ${_escHtml(block.role || "system")} · ${block.enabled ? "启用" : "停用"}
|
||||
</span>
|
||||
<div
|
||||
class="bme-task-block-row ${disabledClass} ${expandedClass}"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
>
|
||||
<div class="bme-task-block-row-header" data-task-action="toggle-block-expand" data-block-id="${_escAttr(block.id)}">
|
||||
<span
|
||||
class="bme-task-drag-handle"
|
||||
title="拖拽排序"
|
||||
aria-label="拖拽排序"
|
||||
draggable="true"
|
||||
>
|
||||
<i class="fa-solid fa-grip-vertical"></i>
|
||||
</span>
|
||||
</button>
|
||||
<div class="bme-task-inline-actions">
|
||||
<span class="bme-task-block-icon">
|
||||
${_getBlockTypeIcon(block.type)}
|
||||
</span>
|
||||
<span class="bme-task-block-name">
|
||||
${_escHtml(block.name || _getTaskBlockTypeLabel(block.type))}
|
||||
</span>
|
||||
<span class="bme-task-block-badge ${roleClass}">
|
||||
${_escHtml(block.role || "system")}
|
||||
</span>
|
||||
<span class="bme-task-block-badge">
|
||||
${_escHtml(_getInjectModeLabel(block.injectionMode))}
|
||||
</span>
|
||||
<span class="bme-task-block-row-spacer"></span>
|
||||
<button
|
||||
class="bme-config-secondary-btn bme-task-mini-btn"
|
||||
data-task-action="move-block-up"
|
||||
class="bme-task-row-btn"
|
||||
data-task-action="toggle-block-expand"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
type="button"
|
||||
title="编辑"
|
||||
>
|
||||
上移
|
||||
<i class="fa-solid fa-pen"></i>
|
||||
</button>
|
||||
<button
|
||||
class="bme-config-secondary-btn bme-task-mini-btn"
|
||||
data-task-action="move-block-down"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
type="button"
|
||||
>
|
||||
下移
|
||||
</button>
|
||||
<button
|
||||
class="bme-config-secondary-btn bme-task-mini-btn"
|
||||
data-task-action="toggle-block-enabled"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
type="button"
|
||||
>
|
||||
${block.enabled ? "停用" : "启用"}
|
||||
</button>
|
||||
<button
|
||||
class="bme-config-secondary-btn bme-task-mini-btn"
|
||||
class="bme-task-row-btn bme-task-row-btn-danger"
|
||||
data-task-action="delete-block"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
type="button"
|
||||
title="删除"
|
||||
>
|
||||
删除
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
<label class="bme-task-row-toggle" title="${block.enabled ? "已启用" : "已停用"}">
|
||||
<input
|
||||
type="checkbox"
|
||||
data-task-action="toggle-block-enabled-cb"
|
||||
data-block-id="${_escAttr(block.id)}"
|
||||
${block.enabled ? "checked" : ""}
|
||||
/>
|
||||
<span class="bme-task-row-toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
${isExpanded ? `
|
||||
<div class="bme-task-block-expand">
|
||||
${_renderTaskBlockInlineEditor(block, state)}
|
||||
</div>
|
||||
` : ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function _renderTaskBlockEditor(state) {
|
||||
const block = state.selectedBlock;
|
||||
if (!block) {
|
||||
return `
|
||||
<div class="bme-config-card-title">块详情</div>
|
||||
<div class="bme-config-help">从左侧列表选择一个块进行编辑。</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function _renderTaskBlockInlineEditor(block, state) {
|
||||
const builtinOptions = state.builtinBlockDefinitions
|
||||
.map(
|
||||
(item) => `
|
||||
@@ -7474,31 +7564,18 @@ function _renderTaskBlockEditor(state) {
|
||||
: block.content || "";
|
||||
|
||||
return `
|
||||
<div class="bme-config-card-head">
|
||||
<div>
|
||||
<div class="bme-config-card-title">块详情</div>
|
||||
<div class="bme-config-card-subtitle">
|
||||
当前块会直接写回到任务预设中。
|
||||
</div>
|
||||
</div>
|
||||
<span class="bme-task-pill">${_escHtml(_getTaskBlockTypeLabel(block.type))}</span>
|
||||
${block.type === "builtin" ? _helpTip(
|
||||
(state.builtinBlockDefinitions.find((d) => d.sourceKey === block.sourceKey) || {}).description || ""
|
||||
) : ""}
|
||||
</div>
|
||||
|
||||
<div class="bme-config-row">
|
||||
<label>块名称</label>
|
||||
<input
|
||||
class="bme-config-input"
|
||||
type="text"
|
||||
data-block-field="name"
|
||||
value="${_escAttr(block.name || "")}"
|
||||
placeholder="用于工作区显示"
|
||||
/>
|
||||
<input
|
||||
class="bme-config-input"
|
||||
type="text"
|
||||
data-block-field="name"
|
||||
value="${_escAttr(block.name || "")}"
|
||||
placeholder="用于工作区显示"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="bme-task-field-grid">
|
||||
<div class="bme-task-expand-row2">
|
||||
<div class="bme-config-row">
|
||||
<label>角色</label>
|
||||
<select class="bme-config-input" data-block-field="role">
|
||||
@@ -7528,18 +7605,6 @@ function _renderTaskBlockEditor(state) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="bme-toggle-item bme-task-editor-toggle">
|
||||
<span class="bme-toggle-copy">
|
||||
<span class="bme-toggle-title">启用此块</span>
|
||||
<span class="bme-toggle-desc">停用后会从最终 prompt 编排中跳过。</span>
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
data-block-field="enabled"
|
||||
${block.enabled ? "checked" : ""}
|
||||
/>
|
||||
</label>
|
||||
|
||||
${
|
||||
block.type === "builtin"
|
||||
? (() => {
|
||||
@@ -7552,18 +7617,17 @@ function _renderTaskBlockEditor(state) {
|
||||
const externalLabel = externalSourceMap[block.sourceKey];
|
||||
return `
|
||||
<div class="bme-config-row">
|
||||
<label>内置来源${_helpTip("运行时自动从任务上下文注入的数据。不同任务类型使用不同来源。")}</label>
|
||||
<label>内置来源${_helpTip("运行时自动从任务上下文注入的数据。")}</label>
|
||||
<select class="bme-config-input" data-block-field="sourceKey">
|
||||
${builtinOptions}
|
||||
</select>
|
||||
</div>
|
||||
${externalLabel
|
||||
? `<div class="bme-task-note" style="text-align:center;padding:1rem;opacity:0.7;">
|
||||
此提示词的内容是从其他地方提取的,无法在此处进行编辑。<br/>
|
||||
来源:<strong>${externalLabel}</strong>
|
||||
? `<div class="bme-task-note" style="text-align:center;padding:0.75rem;opacity:0.7;">
|
||||
内容来源:<strong>${externalLabel}</strong>,无法在此编辑。
|
||||
</div>`
|
||||
: `<div class="bme-config-row">
|
||||
<label>覆盖内容(可选)${_helpTip("留空时自动从 sourceKey 对应的上下文数据读取。填写后将覆盖自动注入的内容。")}</label>
|
||||
<label>覆盖内容(可选)${_helpTip("留空时自动从 sourceKey 对应的上下文数据读取。")}</label>
|
||||
<textarea
|
||||
class="bme-config-textarea"
|
||||
data-block-field="content"
|
||||
@@ -7579,12 +7643,7 @@ function _renderTaskBlockEditor(state) {
|
||||
</div>
|
||||
<div class="bme-config-row">
|
||||
<label>兼容字段</label>
|
||||
<input
|
||||
class="bme-config-input"
|
||||
type="text"
|
||||
value="${_escAttr(legacyField || block.sourceField || "")}"
|
||||
readonly
|
||||
/>
|
||||
<input class="bme-config-input" type="text" value="${_escAttr(legacyField || block.sourceField || "")}" readonly />
|
||||
</div>
|
||||
<div class="bme-config-row">
|
||||
<label>兼容 prompt 内容</label>
|
||||
@@ -7606,6 +7665,12 @@ function _renderTaskBlockEditor(state) {
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
<div class="bme-task-expand-footer">
|
||||
<button class="bme-config-secondary-btn" data-task-action="toggle-block-expand" data-block-id="${_escAttr(block.id)}" type="button">
|
||||
<i class="fa-solid fa-chevron-up"></i> 收起
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -7959,6 +8024,57 @@ function _moveTaskBlock(blockId, direction) {
|
||||
});
|
||||
}
|
||||
|
||||
function _getTaskBlockDropPosition(row, clientY) {
|
||||
const rect = row.getBoundingClientRect();
|
||||
return clientY < rect.top + rect.height / 2 ? "before" : "after";
|
||||
}
|
||||
|
||||
function _clearTaskBlockDragIndicators(workspace = document) {
|
||||
workspace
|
||||
.querySelectorAll(".bme-task-block-row.dragging, .bme-task-block-row.drag-over-top, .bme-task-block-row.drag-over-bottom")
|
||||
.forEach((row) => {
|
||||
row.classList.remove("dragging", "drag-over-top", "drag-over-bottom");
|
||||
});
|
||||
}
|
||||
|
||||
function _setTaskBlockDragIndicator(workspace, activeRow, position) {
|
||||
workspace.querySelectorAll(".bme-task-block-row").forEach((row) => {
|
||||
if (row !== activeRow) {
|
||||
row.classList.remove("drag-over-top", "drag-over-bottom");
|
||||
return;
|
||||
}
|
||||
row.classList.toggle("drag-over-top", position === "before");
|
||||
row.classList.toggle("drag-over-bottom", position === "after");
|
||||
});
|
||||
}
|
||||
|
||||
function _reorderTaskBlocks(sourceBlockId, targetBlockId, position = "before") {
|
||||
if (!sourceBlockId || !targetBlockId || sourceBlockId === targetBlockId) return;
|
||||
_updateCurrentTaskProfile((draft) => {
|
||||
const blocks = _sortTaskBlocks(draft.blocks);
|
||||
const sourceIndex = blocks.findIndex((item) => item.id === sourceBlockId);
|
||||
const targetIndex = blocks.findIndex((item) => item.id === targetBlockId);
|
||||
if (sourceIndex < 0 || targetIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [sourceBlock] = blocks.splice(sourceIndex, 1);
|
||||
let insertIndex = targetIndex;
|
||||
|
||||
if (sourceIndex < targetIndex) {
|
||||
insertIndex -= 1;
|
||||
}
|
||||
if (position === "after") {
|
||||
insertIndex += 1;
|
||||
}
|
||||
|
||||
insertIndex = Math.max(0, Math.min(blocks.length, insertIndex));
|
||||
blocks.splice(insertIndex, 0, sourceBlock);
|
||||
draft.blocks = blocks.map((block, index) => ({ ...block, order: index }));
|
||||
return { selectBlockId: sourceBlockId };
|
||||
});
|
||||
}
|
||||
|
||||
function _deleteTaskBlock(blockId) {
|
||||
if (!blockId) return;
|
||||
_updateCurrentTaskProfile((draft) => {
|
||||
|
||||
Reference in New Issue
Block a user