代码: 全选
Here are the modifications required for your feature request. The code has been rewritten to add the settings slider logic, CSS bindings, SVG icons, and the new clean branch menu for `ejceesmini` mode.
### 1. Add Default Settings to `constructor`
Add the initial state variables for the settings in the `constructor`:
```javascript
// Inside constructor(), e.g. after `this.isExportTextMode = false;`
this.miniBgOpacity = 0.7;
this.miniTextOpacity = 1;
this.miniFontSize = 14;
```
### 2. Update CSS in `injectCSS`
Modify the `if (this.mode === 'ejceessingle' || this.mode === 'ejceesmini')` block to include CSS variables, the SVG background for the settings icon, the settings panel layout, and the new branch menu styling.
```javascript
// Replace the ejceesmini specific elements in the `afterContainerCSS` variable:
afterContainerCSS = `
/* ... (Keep your existing @container rules) ... */
/* ejceesmini specific elements */
.ejcees-inner.ejceesmini-mode {
--mini-bg-op: 0.7;
--mini-text-op: 1;
--mini-fz: 14px;
}
.ejceesmini-output-settings {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23ccc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>');
background-repeat: no-repeat;
background-position: center right 4px;
background-size: 16px 16px;
cursor: pointer;
padding-right: 20px !important;
}
.ejceesmini-settings-panel {
position: absolute;
top: 8px;
right: 8px;
background-color: rgba(0, 0, 0, 0.85);
color: #fff;
padding: 12px;
border-radius: 6px;
z-index: 100;
display: flex;
flex-direction: column;
gap: 10px;
font-size: 12px;
min-width: 160px;
}
.ejceesmini-settings-panel label {
display: flex;
flex-direction: column;
gap: 4px;
}
.ejceesmini-settings-panel input[type="range"] {
width: 100%;
}
.ejceesmini-branch-menu {
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, var(--mini-bg-op));
color: rgba(255, 255, 255, var(--mini-text-op));
padding: 4px;
cursor: pointer;
z-index: 20;
display: flex;
justify-content: center;
align-items: center;
border-bottom-right-radius: 4px;
}
.ejceesmini-branch-menu svg {
width: calc(var(--mini-fz) * 2);
height: calc(var(--mini-fz) * 2);
stroke: rgba(255, 255, 255, var(--mini-text-op));
fill: none;
}
.ejceesmini-comment {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background-color: rgba(0, 0, 0, var(--mini-bg-op));
color: rgba(255, 255, 255, var(--mini-text-op));
padding: 8px;
font-size: var(--mini-fz);
z-index: 20;
box-sizing: border-box;
white-space: pre-wrap;
word-break: break-all;
}
.mini-branch-dropdown {
position: absolute;
background-color: rgba(0, 0, 0, var(--mini-bg-op));
color: rgba(255, 255, 255, var(--mini-text-op));
font-size: var(--mini-fz);
z-index: 777;
border-radius: 4px;
padding: 4px 0;
min-width: 100px;
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
white-space: nowrap;
}
.mini-branch-dropdown .branch-move-link {
display: block;
padding: 8px 12px;
cursor: pointer;
}
.mini-branch-dropdown .branch-move-link:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.mini-branch-dropdown .branch-move-link.active {
font-weight: bold;
background-color: rgba(255, 255, 255, 0.3);
}
.ejceessvg > object {
width: 100%;
height: auto;
}
`;
```
### 3. Update DOM in `renderDOM`
Update the string generated for `ejceesmini` mode to include the settings panel and the SVG branch icon container.
```javascript
if (this.mode === 'ejceesmini') {
this.container.innerHTML = `
<div class="ejcees-inner ejceesmini-mode" style="--mini-bg-op: ${this.miniBgOpacity}; --mini-text-op: ${this.miniTextOpacity}; --mini-fz: ${this.miniFontSize}px;">
<div class="ejceessvg" style="position: relative;">
<div id="mini-branch-container" class="ejceesmini-branch-menu" style="display: none;">
<svg viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"></line><circle cx="18" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><path d="M18 9a9 9 0 0 1-9 9"></path></svg>
</div>
<div id="mini-comment-container" class="ejceesmini-comment" style="display: none;"></div>
<div id="mini-settings-panel" class="ejceesmini-settings-panel" style="display: none;">
<label>BG Opacity: <input type="range" id="mini-bg-op-slider" min="0" max="1" step="0.1" value="${this.miniBgOpacity}"></label>
<label>Text Opacity: <input type="range" id="mini-text-op-slider" min="0" max="1" step="0.1" value="${this.miniTextOpacity}"></label>
<label>Font Size (px): <input type="range" id="mini-fz-slider" min="12" max="24" step="1" value="${this.miniFontSize}"></label>
</div>
</div>
<div class="ejceesctrl" style="flex: 0 0 auto; min-height: unset; height: 48px;">
<div class="ejceesstep">
<div class="ejceesoutput ejceesmini-output-settings" style="width: 64px; height: 36px; display: flex; align-items: center; justify-content: center; font-size: 12px; padding: 0;">0/0</div>
<div class="ejceesstepminus">-</div>
<input type="range" class="ejceesstepdrop" min="0" max="0" value="0" />
<div class="ejceesstepplus">+</div>
<div class="note-btn" id="btn-autoplay" title="Auto Play" style="width: 36px; height: 36px; cursor: pointer; display: flex; align-items: center; justify-content: center; background-color: transparent;">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px; fill: #ccc;"><path d="M8 5v14l11-7z"/></svg>
</div>
</div>
</div>
<input type="file" id="file-input" style="display: none" />
</div>`;
}
```
### 4. Bind Settings and Sliders in `bindEvents`
Add the event handlers for the newly created settings overlay and sliders within `if (this.mode === 'ejceesmini') {`.
```javascript
if (this.mode === 'ejceesmini') {
// -- Bind Settings UI --
const outputEl = this.container.querySelector('.ejceesoutput');
const settingsPanel = this.container.querySelector('#mini-settings-panel');
const innerWrapper = this.container.querySelector('.ejcees-inner');
if (outputEl && settingsPanel) {
outputEl.addEventListener('click', () => {
settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'flex' : 'none';
});
}
const bgSlider = this.container.querySelector('#mini-bg-op-slider');
const textSlider = this.container.querySelector('#mini-text-op-slider');
const fzSlider = this.container.querySelector('#mini-fz-slider');
if (bgSlider) {
bgSlider.addEventListener('input', (e) => {
this.miniBgOpacity = e.target.value;
innerWrapper.style.setProperty('--mini-bg-op', this.miniBgOpacity);
});
}
if (textSlider) {
textSlider.addEventListener('input', (e) => {
this.miniTextOpacity = e.target.value;
innerWrapper.style.setProperty('--mini-text-op', this.miniTextOpacity);
});
}
if (fzSlider) {
fzSlider.addEventListener('input', (e) => {
this.miniFontSize = e.target.value;
innerWrapper.style.setProperty('--mini-fz', this.miniFontSize + 'px');
});
}
// 2. Implement btn-autoplay functionality (Toggle Auto Play)
// ... (Keep the rest of your original bindEvents logic here)
```
### 5. Update Branch Rendering in `renderRecordUI`
Modify the `renderRecordUI` code block dedicated to `ejceesmini` to handle the SVG menu toggling.
```javascript
renderRecordUI() {
if (this.mode === 'ejceesmini') {
const totalDepth = this.getPathDepth(this.historyFEN, this.currentBranch);
this.stepSlider.max = Math.max(0, totalDepth - 1);
const branchContainer = this.container.querySelector('#mini-branch-container');
const parentNode = this.getNodeAtStep(this.currentStepIndex - 1);
if (parentNode && parentNode.v && parentNode.v.length > 1) {
branchContainer.style.display = 'flex'; // Use flex for proper SVG centering
let forkIndex = -1;
let tempNode = this.historyFEN;
for (let j = 0; j < this.currentStepIndex - 1; j++) {
if (tempNode.v && tempNode.v.length > 1) forkIndex++;
const choice = tempNode.v && tempNode.v.length > 1 ? this.currentBranch[forkIndex] || 0 : 0;
tempNode = tempNode.v[choice];
}
forkIndex++;
branchContainer.onclick = e => {
e.stopPropagation();
this.showMiniBranchMenu(branchContainer, parentNode, forkIndex, this.currentStepIndex);
};
} else {
branchContainer.style.display = 'none';
}
this.updateToolHighlights();
return;
}
// ... (Keep the rest of renderRecordUI)
```
### 6. Create `showMiniBranchMenu` Method
Add the custom branch dropdown builder as a new class method inside the `Ejcees` class block.
```javascript
/**
* Dedicated branch menu constructor for ejceesmini.
* Simple UI containing ONLY move links that vanishes automatically on click.
*/
showMiniBranchMenu(anchor, parentNode, forkIndex, stepIndex) {
const existingMenu = this.container.querySelector('.mini-branch-dropdown');
if (existingMenu) {
existingMenu.remove();
return;
}
const menu = document.createElement('div');
menu.className = 'mini-branch-dropdown';
parentNode.v.forEach((child, idx) => {
const moveLink = document.createElement('div');
moveLink.className = 'branch-move-link';
if (this.currentBranch[forkIndex] === idx) {
moveLink.classList.add('active');
}
moveLink.innerText = this.NotationConverter.toChinese(child.move);
moveLink.onclick = (e) => {
e.stopPropagation();
this.updateBranchPath(forkIndex, idx);
this.jumpToStep(stepIndex);
this.renderRecordUI();
menu.remove();
this.saveStateToUndo();
this.updateToolHighlights();
};
menu.appendChild(moveLink);
});
anchor.appendChild(menu);
// Ensure it's visually aligned under the anchor
menu.style.top = `${anchor.offsetHeight}px`;
menu.style.left = `0px`;
// Close menu when clicking elsewhere
const closeMenu = e => {
if (!menu.contains(e.target) && e.target !== anchor) {
menu.remove();
this.container.removeEventListener('click', closeMenu);
}
};
setTimeout(() => this.container.addEventListener('click', closeMenu), 10);
}
```