mirror of
https://github.com/yeban8090/mp-preview.git
synced 2026-07-22 05:46:27 +00:00
新增模板预览及背景自定义
This commit is contained in:
parent
914411bac3
commit
70db8a3327
16 changed files with 1395 additions and 179 deletions
25
package-lock.json
generated
25
package-lock.json
generated
|
|
@ -1,13 +1,16 @@
|
|||
{
|
||||
"name": "mp-preview",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mp-preview",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
|
|
@ -1856,6 +1859,24 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mp-preview",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "一键将 Markdown 文档转换为微信公众号格式",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
@ -23,5 +23,8 @@
|
|||
"obsidian": "latest",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": "^5.1.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,41 @@
|
|||
import { backgrounds } from './backgrounds';
|
||||
import { SettingsManager } from "./settings/settings";
|
||||
|
||||
export interface Background {
|
||||
id: string;
|
||||
name: string;
|
||||
style: string;
|
||||
isPreset?: boolean;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
export class BackgroundManager {
|
||||
private backgrounds: Background[];
|
||||
private currentBackground: Background | null = null;
|
||||
private settingsManager: SettingsManager;
|
||||
|
||||
constructor() {
|
||||
this.backgrounds = backgrounds.backgrounds;
|
||||
constructor(settingsManager: SettingsManager) {
|
||||
this.settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public getAllBackgrounds(): Background[] {
|
||||
return this.backgrounds;
|
||||
}
|
||||
|
||||
public setBackground(id: string | null) {
|
||||
public setBackground(id: string | null): boolean {
|
||||
if (!id) {
|
||||
this.currentBackground = null;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
const background = this.backgrounds.find(bg => bg.id === id);
|
||||
|
||||
const background = this.settingsManager.getBackground(id);
|
||||
if (background) {
|
||||
// 检查背景是否可见
|
||||
if (background.isVisible === false) {
|
||||
console.warn(`尝试设置不可见的背景: ${id}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentBackground = background;
|
||||
return true;
|
||||
}
|
||||
|
||||
console.warn(`未找到背景: ${id}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
public applyBackground(element: HTMLElement) {
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ import { DonateManager } from './donateManager';
|
|||
import { MPSettingTab } from './settings/MPSettingTab';
|
||||
export default class MPPlugin extends Plugin {
|
||||
settingsManager: SettingsManager;
|
||||
|
||||
templateManager: TemplateManager;
|
||||
async onload() {
|
||||
// 初始化设置管理器
|
||||
this.settingsManager = new SettingsManager(this);
|
||||
await this.settingsManager.loadSettings();
|
||||
|
||||
// 初始化模板管理器
|
||||
const templateManager = new TemplateManager(this.app, this.settingsManager);
|
||||
this.templateManager = new TemplateManager(this.app, this.settingsManager);
|
||||
|
||||
// 初始化转换器
|
||||
MPConverter.initialize(this.app);
|
||||
|
|
@ -24,7 +24,7 @@ export default class MPPlugin extends Plugin {
|
|||
// 注册视图
|
||||
this.registerView(
|
||||
VIEW_TYPE_MP,
|
||||
(leaf) => new MPView(leaf, templateManager, this.settingsManager)
|
||||
(leaf) => new MPView(leaf, this.templateManager, this.settingsManager)
|
||||
);
|
||||
|
||||
// 自动打开视图但不聚焦
|
||||
|
|
|
|||
551
src/settings/CreateBackgroundModal.ts
Normal file
551
src/settings/CreateBackgroundModal.ts
Normal file
|
|
@ -0,0 +1,551 @@
|
|||
import { App, Modal, Setting, Notice } from 'obsidian';
|
||||
import { Background } from '../backgroundManager';
|
||||
import { nanoid } from '../utils/nanoid';
|
||||
|
||||
interface CssTemplate {
|
||||
name: string;
|
||||
style: string;
|
||||
settings: string[];
|
||||
}
|
||||
|
||||
export class CreateBackgroundModal extends Modal {
|
||||
private onSubmit: (background: Background) => void;
|
||||
private background: Background;
|
||||
private isEditing: boolean;
|
||||
|
||||
// 内部使用的背景类型
|
||||
private backgroundType: 'color' | 'css' = 'color';
|
||||
private backgroundColor: string = '#f5f5f5';
|
||||
private backgroundCssStyle: string = '';
|
||||
private cssTemplateType: string = 'custom'; // 新增:CSS模板类型
|
||||
// 新增属性
|
||||
private patternColor: string = 'rgba(50, 0, 0, 0.03)';
|
||||
private patternSize: number = 20;
|
||||
|
||||
// 根据模板类型更新设置项的可见性
|
||||
private updateSettingsVisibility(cssSection: HTMLElement, templateType: string) {
|
||||
const template = this.cssTemplates[templateType];
|
||||
if (!template) return;
|
||||
|
||||
// 获取所有可能的设置项容器
|
||||
const colorSettingContainer = cssSection.querySelector('.pattern-color-setting');
|
||||
const sizeSettingContainer = cssSection.querySelector('.pattern-size-setting');
|
||||
const customCssContainer = cssSection.querySelector('.custom-css-container');
|
||||
|
||||
// 根据模板需要的设置项显示或隐藏
|
||||
if (colorSettingContainer) {
|
||||
colorSettingContainer.toggleClass('is-hidden', !template.settings.includes('color'));
|
||||
}
|
||||
|
||||
if (sizeSettingContainer) {
|
||||
sizeSettingContainer.toggleClass('is-hidden', !template.settings.includes('size'));
|
||||
}
|
||||
|
||||
if (customCssContainer) {
|
||||
customCssContainer.toggleClass('is-hidden', !template.settings.includes('custom'));
|
||||
|
||||
// 如果显示自定义CSS,同时显示文本区域
|
||||
const customCssTextArea = cssSection.querySelector('.custom-css-textarea');
|
||||
if (customCssTextArea && template.settings.includes('custom')) {
|
||||
customCssTextArea.toggleClass('is-hidden', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析CSS样式到各个组件
|
||||
private parseCssToComponents() {
|
||||
// 提取颜色
|
||||
const colorMatch = this.backgroundCssStyle.match(/rgba?\([^)]+\)/);
|
||||
if (colorMatch) {
|
||||
this.patternColor = colorMatch[0];
|
||||
}
|
||||
|
||||
// 提取大小
|
||||
const sizeMatch = this.backgroundCssStyle.match(/background-size:\s*(\d+)px/);
|
||||
if (sizeMatch && sizeMatch[1]) {
|
||||
this.patternSize = parseInt(sizeMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新图案颜色(保持透明度不变)
|
||||
private updatePatternColor(hexColor: string) {
|
||||
const r = parseInt(hexColor.slice(1, 3), 16);
|
||||
const g = parseInt(hexColor.slice(3, 5), 16);
|
||||
const b = parseInt(hexColor.slice(5, 7), 16);
|
||||
|
||||
// 提取当前透明度
|
||||
let alpha = 0.03;
|
||||
const alphaMatch = this.patternColor.match(/rgba\([^,]+,[^,]+,[^,]+,([^)]+)\)/);
|
||||
if (alphaMatch && alphaMatch[1]) {
|
||||
alpha = parseFloat(alphaMatch[1]);
|
||||
}
|
||||
|
||||
this.patternColor = `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
|
||||
// 更新图案透明度(保持颜色不变)
|
||||
private updatePatternOpacity(opacity: number) {
|
||||
const colorMatch = this.patternColor.match(/rgba\(([^,]+),([^,]+),([^,]+),[^)]+\)/);
|
||||
if (colorMatch) {
|
||||
const [_, r, g, b] = colorMatch;
|
||||
this.patternColor = `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
} else {
|
||||
// 如果之前不是rgba格式,转换为rgba
|
||||
const r = parseInt(this.patternColor.slice(1, 3), 16);
|
||||
const g = parseInt(this.patternColor.slice(3, 5), 16);
|
||||
const b = parseInt(this.patternColor.slice(5, 7), 16);
|
||||
this.patternColor = `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据当前组件设置更新CSS样式
|
||||
private updateCssStyle() {
|
||||
if (this.cssTemplateType === 'custom') {
|
||||
return; // 自定义模式下不自动更新CSS
|
||||
}
|
||||
|
||||
const template = this.cssTemplates[this.cssTemplateType];
|
||||
if (!template) return;
|
||||
|
||||
// 替换模板中的颜色和大小
|
||||
let style = template.style;
|
||||
|
||||
// 替换所有颜色
|
||||
style = style.replace(/rgba\([^)]+\)/g, this.patternColor);
|
||||
|
||||
// 替换所有大小
|
||||
style = style.replace(/(\d+)px/g, `${this.patternSize}px`);
|
||||
|
||||
this.backgroundCssStyle = style;
|
||||
}
|
||||
// 预设的CSS模板
|
||||
private cssTemplates: Record<string, CssTemplate> = {
|
||||
custom: {
|
||||
name: '自定义',
|
||||
style: '',
|
||||
settings: ['custom']
|
||||
},
|
||||
grid: {
|
||||
name: '网格',
|
||||
style: 'background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.03) 2%, transparent 2%), linear-gradient(360deg, rgba(50, 0, 0, 0.03) 2%, transparent 2%); background-size: 20px 20px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
diagonalStripes: {
|
||||
name: '对角条纹',
|
||||
style: 'background-image: linear-gradient(135deg, rgba(50, 0, 0, 0.05) 25%, transparent 25%, transparent 50%, rgba(50, 0, 0, 0.05) 50%, rgba(50, 0, 0, 0.05) 75%, transparent 75%, transparent); background-size: 20px 20px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
polkaDots: {
|
||||
name: '波尔卡圆点',
|
||||
style: 'background-image: radial-gradient(circle, rgba(50, 0, 0, 0.05) 10%, transparent 10%); background-size: 20px 20px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
zigzag: {
|
||||
name: '锯齿形',
|
||||
style: 'background-image: linear-gradient(135deg, rgba(50, 0, 0, 0.05) 25%, transparent 25%, transparent 50%, rgba(50, 0, 0, 0.05) 50%, rgba(50, 0, 0, 0.05) 75%, transparent 75%, transparent); background-size: 20px 20px; background-position: 0 0, 10px 10px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
honeycomb: {
|
||||
name: '蜂窝',
|
||||
style: 'background-image: linear-gradient(30deg, rgba(50, 0, 0, 0.05) 12%, transparent 12%, transparent 50%, rgba(50, 0, 0, 0.05) 50%, rgba(50, 0, 0, 0.05) 62%, transparent 62%, transparent); background-size: 20px 35px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
wave: {
|
||||
name: '波浪',
|
||||
style: 'background-image: linear-gradient(45deg, rgba(50, 0, 0, 0.04) 12%, transparent 12%, transparent 88%, rgba(50, 0, 0, 0.04) 88%), linear-gradient(135deg, rgba(50, 0, 0, 0.04) 12%, transparent 12%, transparent 88%, rgba(50, 0, 0, 0.04) 88%); background-size: 30px 30px; background-position: 0 0, 0 0, 15px 15px, 15px 15px;',
|
||||
settings: ['color', 'size']
|
||||
},
|
||||
checkerboard: {
|
||||
name: '棋盘',
|
||||
style: 'background-image: linear-gradient(45deg, rgba(50, 0, 0, 0.04) 25%, transparent 25%), linear-gradient(-45deg, rgba(50, 0, 0, 0.04) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(50, 0, 0, 0.04) 75%), linear-gradient(-45deg, transparent 75%, rgba(50, 0, 0, 0.04) 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px;',
|
||||
settings: ['color', 'size']
|
||||
}
|
||||
};
|
||||
|
||||
// 从 style 字符串中解析出背景类型和相关属性
|
||||
private parseStyleToProperties(style: string) {
|
||||
if (style.includes('background-color:')) {
|
||||
this.backgroundType = 'color';
|
||||
const colorMatch = style.match(/background-color: (#[a-fA-F0-9]+)/);
|
||||
if (colorMatch && colorMatch[1]) {
|
||||
this.backgroundColor = colorMatch[1];
|
||||
}
|
||||
} else {
|
||||
this.backgroundType = 'css';
|
||||
// 移除基本样式,保留自定义 CSS
|
||||
this.backgroundCssStyle = style.replace(/box-sizing: border-box; margin: 0; padding: 0;/, '').trim();
|
||||
|
||||
// 尝试匹配预设模板
|
||||
let matched = false;
|
||||
for (const [key, template] of Object.entries(this.cssTemplates)) {
|
||||
if (key === 'custom') continue;
|
||||
|
||||
if (this.backgroundCssStyle === template.style) {
|
||||
this.cssTemplateType = key;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
this.cssTemplateType = 'custom';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
onSubmit: (background: Background) => void,
|
||||
background?: Background
|
||||
) {
|
||||
super(app);
|
||||
this.onSubmit = onSubmit;
|
||||
this.isEditing = !!background;
|
||||
|
||||
if (background) {
|
||||
this.background = { ...background };
|
||||
// 从 style 中解析出类型和相关属性
|
||||
this.parseStyleToProperties(background.style);
|
||||
} else {
|
||||
this.background = {
|
||||
id: nanoid(),
|
||||
name: '',
|
||||
style: 'background-color: #f5f5f5;'
|
||||
};
|
||||
this.backgroundType = 'color';
|
||||
this.backgroundColor = '#f5f5f5';
|
||||
}
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.addClass('mp-background-modal');
|
||||
|
||||
// 标题
|
||||
contentEl.createEl('h2', { text: this.isEditing ? '编辑背景' : '创建新背景' });
|
||||
|
||||
// 基本信息
|
||||
const basicSection = contentEl.createDiv('background-basic-section');
|
||||
|
||||
// 背景名称
|
||||
new Setting(basicSection)
|
||||
.setName('背景名称')
|
||||
.setDesc('输入背景的名称')
|
||||
.addText(text => {
|
||||
text.setValue(this.background.name || '')
|
||||
.onChange(value => {
|
||||
this.background.name = value;
|
||||
});
|
||||
});
|
||||
|
||||
// 背景类型
|
||||
new Setting(basicSection)
|
||||
.setName('背景类型')
|
||||
.setDesc('选择背景的类型')
|
||||
.addDropdown(dropdown => {
|
||||
dropdown
|
||||
.addOption('color', '纯色背景')
|
||||
.addOption('css', 'CSS背景图案')
|
||||
.setValue(this.backgroundType)
|
||||
.onChange(value => {
|
||||
this.backgroundType = value as 'color' | 'css';
|
||||
this.updateTypeSpecificSettings();
|
||||
});
|
||||
});
|
||||
|
||||
// 类型特定设置容器
|
||||
const typeSpecificSection = contentEl.createDiv('background-type-specific-section');
|
||||
|
||||
// 纯色背景设置
|
||||
const colorSection = typeSpecificSection.createDiv('background-color-section');
|
||||
new Setting(colorSection)
|
||||
.setName('背景颜色')
|
||||
.setDesc('选择背景的颜色')
|
||||
.addColorPicker(color => {
|
||||
color.setValue(this.backgroundColor)
|
||||
.onChange(value => {
|
||||
this.backgroundColor = value;
|
||||
this.updatePreview();
|
||||
});
|
||||
});
|
||||
|
||||
// CSS背景图案设置
|
||||
const cssSection = typeSpecificSection.createDiv('background-css-section');
|
||||
|
||||
// 添加模板选择下拉框
|
||||
new Setting(cssSection)
|
||||
.setName('背景模板')
|
||||
.setDesc('选择预设的背景模板')
|
||||
.addDropdown(dropdown => {
|
||||
for (const [key, template] of Object.entries(this.cssTemplates)) {
|
||||
dropdown.addOption(key, template.name);
|
||||
}
|
||||
dropdown.setValue(this.cssTemplateType)
|
||||
.onChange(value => {
|
||||
this.cssTemplateType = value;
|
||||
if (value !== 'custom') {
|
||||
this.backgroundCssStyle = this.cssTemplates[value].style;
|
||||
|
||||
// 解析CSS样式到各个组件
|
||||
this.parseCssToComponents();
|
||||
|
||||
// 更新UI控件以反映当前模板的设置
|
||||
const colorPicker = cssSection.querySelector('input[type="color"]');
|
||||
const opacitySlider = cssSection.querySelector('.slider');
|
||||
const sizeSlider = cssSection.querySelectorAll('.slider')[1];
|
||||
|
||||
if (colorPicker) {
|
||||
// 从rgba提取十六进制颜色
|
||||
const rgbaMatch = this.patternColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
if (rgbaMatch) {
|
||||
const [_, r, g, b] = rgbaMatch;
|
||||
const hexColor = '#' +
|
||||
parseInt(r).toString(16).padStart(2, '0') +
|
||||
parseInt(g).toString(16).padStart(2, '0') +
|
||||
parseInt(b).toString(16).padStart(2, '0');
|
||||
(colorPicker as HTMLInputElement).value = hexColor;
|
||||
}
|
||||
}
|
||||
|
||||
if (opacitySlider) {
|
||||
const opacityMatch = this.patternColor.match(/rgba\([^,]+,[^,]+,[^,]+,([^)]+)\)/);
|
||||
if (opacityMatch && opacityMatch[1]) {
|
||||
const opacity = parseFloat(opacityMatch[1]) * 100;
|
||||
// 使用类型断言来访问__component__属性
|
||||
const sliderComponent = (opacitySlider.parentElement as any).__component__;
|
||||
if (sliderComponent && typeof sliderComponent.setValue === 'function') {
|
||||
sliderComponent.setValue(opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeSlider) {
|
||||
// 使用类型断言来访问__component__属性
|
||||
const sliderComponent = (sizeSlider.parentElement as any).__component__;
|
||||
if (sliderComponent && typeof sliderComponent.setValue === 'function') {
|
||||
sliderComponent.setValue(this.patternSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新自定义CSS区域的可见性
|
||||
const customCssContainer = cssSection.querySelector('.custom-css-container');
|
||||
const customCssTextArea = cssSection.querySelector('.custom-css-textarea');
|
||||
if (customCssContainer && customCssTextArea) {
|
||||
const isCustom = value === 'custom';
|
||||
customCssTextArea.toggleClass('is-hidden', !isCustom);
|
||||
}
|
||||
|
||||
// 更新CSS设置项的可见性 - 移动到这里更合适
|
||||
this.updateSettingsVisibility(cssSection, value);
|
||||
|
||||
this.updatePreview();
|
||||
});
|
||||
});
|
||||
|
||||
// 背景颜色设置
|
||||
const colorSettingContainer = cssSection.createDiv('pattern-color-setting');
|
||||
new Setting(colorSettingContainer)
|
||||
.setName('图案颜色')
|
||||
.setDesc('设置背景图案的颜色')
|
||||
.addColorPicker(color => {
|
||||
// 从rgba提取十六进制颜色
|
||||
const rgbaMatch = this.patternColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
let hexColor = '#320000'; // 默认颜色
|
||||
|
||||
if (rgbaMatch) {
|
||||
const [_, r, g, b] = rgbaMatch;
|
||||
hexColor = '#' +
|
||||
parseInt(r).toString(16).padStart(2, '0') +
|
||||
parseInt(g).toString(16).padStart(2, '0') +
|
||||
parseInt(b).toString(16).padStart(2, '0');
|
||||
}
|
||||
|
||||
color.setValue(hexColor)
|
||||
.onChange(value => {
|
||||
// 更新颜色但保持透明度
|
||||
this.updatePatternColor(value);
|
||||
this.updateCssStyle();
|
||||
this.updatePreview();
|
||||
});
|
||||
})
|
||||
.addSlider(slider => {
|
||||
// 提取当前透明度
|
||||
let opacity = 3; // 默认透明度3%
|
||||
const alphaMatch = this.patternColor.match(/rgba\([^,]+,[^,]+,[^,]+,([^)]+)\)/);
|
||||
if (alphaMatch && alphaMatch[1]) {
|
||||
opacity = parseFloat(alphaMatch[1]) * 100;
|
||||
}
|
||||
|
||||
slider.setLimits(0, 100, 1)
|
||||
.setValue(opacity)
|
||||
.setDynamicTooltip()
|
||||
.onChange(value => {
|
||||
// 更新透明度但保持颜色
|
||||
this.updatePatternOpacity(value / 100);
|
||||
this.updateCssStyle();
|
||||
this.updatePreview();
|
||||
});
|
||||
});
|
||||
|
||||
// 图案大小设置
|
||||
const sizeSettingContainer = cssSection.createDiv('pattern-size-setting');
|
||||
new Setting(sizeSettingContainer)
|
||||
.setName('图案大小')
|
||||
.setDesc('设置背景图案的大小')
|
||||
.addSlider(slider => {
|
||||
slider.setLimits(5, 50, 1)
|
||||
.setValue(this.patternSize) // 使用当前模板的大小
|
||||
.setDynamicTooltip()
|
||||
.onChange(value => {
|
||||
this.patternSize = value;
|
||||
this.updateCssStyle();
|
||||
this.updatePreview();
|
||||
});
|
||||
});
|
||||
|
||||
// 移除自定义CSS切换按钮,直接创建文本区域容器
|
||||
const customCssContainer = cssSection.createDiv('custom-css-container');
|
||||
const customCssTextArea = customCssContainer.createDiv('custom-css-textarea');
|
||||
customCssTextArea.toggleClass('is-hidden', this.cssTemplateType !== 'custom');
|
||||
|
||||
new Setting(customCssTextArea)
|
||||
.setName('CSS代码')
|
||||
.setDesc('直接输入CSS样式代码')
|
||||
.addTextArea(text => {
|
||||
text.setValue(this.backgroundCssStyle)
|
||||
.setPlaceholder(`/* CSS背景样式示例 */
|
||||
/* 渐变背景 */
|
||||
background: linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%);
|
||||
|
||||
/* 条纹背景 */
|
||||
background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 50%, transparent 50%);
|
||||
background-size: 10px 10px;
|
||||
|
||||
/* 网格背景 */
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px);
|
||||
background-size: 20px 20px;`)
|
||||
.onChange(value => {
|
||||
this.backgroundCssStyle = value;
|
||||
this.updatePreview();
|
||||
});
|
||||
text.inputEl.rows = 10;
|
||||
text.inputEl.cols = 50;
|
||||
});
|
||||
// 背景预览
|
||||
const previewSection = contentEl.createDiv('background-preview-section');
|
||||
previewSection.createEl('h3', { text: '预览' });
|
||||
const previewEl = previewSection.createDiv('background-preview');
|
||||
previewEl.createSpan({ text: '预览效果' });
|
||||
this.updatePreview(previewEl);
|
||||
|
||||
// 按钮区域
|
||||
const buttonSection = contentEl.createDiv('background-button-section');
|
||||
new Setting(buttonSection)
|
||||
.addButton(btn => {
|
||||
btn.setButtonText('取消')
|
||||
.onClick(() => {
|
||||
this.close();
|
||||
});
|
||||
})
|
||||
.addButton(btn => {
|
||||
btn.setButtonText(this.isEditing ? '保存' : '创建')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
if (!this.validateForm()) {
|
||||
return;
|
||||
}
|
||||
this.generateStyle();
|
||||
this.onSubmit(this.background);
|
||||
this.close();
|
||||
});
|
||||
});
|
||||
|
||||
// 初始化类型特定设置
|
||||
this.updateTypeSpecificSettings();
|
||||
}
|
||||
|
||||
private validateForm(): boolean {
|
||||
if (!this.background.name) {
|
||||
new Notice('请输入背景名称');
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.backgroundType) {
|
||||
case 'color':
|
||||
if (!this.backgroundColor) {
|
||||
new Notice('请选择背景颜色');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'css':
|
||||
if (!this.backgroundCssStyle) {
|
||||
new Notice('请输入CSS样式');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private updateTypeSpecificSettings() {
|
||||
const colorSection = this.contentEl.querySelector('.background-color-section');
|
||||
const cssSection = this.contentEl.querySelector('.background-css-section');
|
||||
|
||||
if (colorSection && cssSection) {
|
||||
colorSection.toggleClass('is-hidden', this.backgroundType !== 'color');
|
||||
cssSection.toggleClass('is-hidden', this.backgroundType !== 'css');
|
||||
|
||||
// 如果切换到CSS背景类型,根据当前模板更新设置项可见性
|
||||
if (this.backgroundType === 'css') {
|
||||
this.updateSettingsVisibility(cssSection as HTMLElement, this.cssTemplateType);
|
||||
}
|
||||
}
|
||||
|
||||
const previewEl = this.contentEl.querySelector('.background-preview');
|
||||
if (previewEl) {
|
||||
this.updatePreview(previewEl as HTMLElement);
|
||||
}
|
||||
}
|
||||
|
||||
private updatePreview(previewEl?: HTMLElement) {
|
||||
const el = previewEl || this.contentEl.querySelector('.background-preview');
|
||||
if (!el) return;
|
||||
|
||||
let style = '';
|
||||
switch (this.backgroundType) {
|
||||
case 'color':
|
||||
style = `background-color: ${this.backgroundColor};`;
|
||||
break;
|
||||
case 'css':
|
||||
style = this.backgroundCssStyle;
|
||||
break;
|
||||
}
|
||||
|
||||
el.setAttribute('style', style);
|
||||
}
|
||||
|
||||
private generateStyle() {
|
||||
let style = 'box-sizing: border-box; margin: 0; padding: 0; ';
|
||||
|
||||
switch (this.backgroundType) {
|
||||
case 'color':
|
||||
style += `background-color: ${this.backgroundColor};`;
|
||||
break;
|
||||
case 'css':
|
||||
style += this.backgroundCssStyle;
|
||||
break;
|
||||
}
|
||||
|
||||
this.background.style = style;
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { App, Modal, Setting, Notice, setIcon, ColorComponent } from 'obsidian';
|
||||
import MPPlugin from '../main';
|
||||
import { Template } from '../templateManager';
|
||||
import { TemplatePreviewModal } from './templatePreviewModal';
|
||||
|
||||
export class CreateTemplateModal extends Modal {
|
||||
private template: Template;
|
||||
|
|
@ -180,6 +181,16 @@ export class CreateTemplateModal extends Modal {
|
|||
// 保存和取消按钮
|
||||
const buttonContainer = contentEl.createDiv('modal-button-container');
|
||||
new Setting(buttonContainer)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('预览')
|
||||
.onClick(() => {
|
||||
// 打开预览模式
|
||||
const previewModal = new TemplatePreviewModal(this.app, this.template, this.plugin.templateManager);
|
||||
previewModal.open();
|
||||
}))
|
||||
.addButton(btn => btn
|
||||
.setButtonText('取消')
|
||||
.onClick(() => this.close()))
|
||||
.addButton(btn => btn
|
||||
.setButtonText('保存')
|
||||
.setCta()
|
||||
|
|
@ -187,10 +198,7 @@ export class CreateTemplateModal extends Modal {
|
|||
if (await this.validateAndSubmit()) {
|
||||
this.close();
|
||||
}
|
||||
}))
|
||||
.addButton(btn => btn
|
||||
.setButtonText('取消')
|
||||
.onClick(() => this.close()));
|
||||
}));
|
||||
|
||||
this.nameInput.addEventListener('keydown', async (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { App, PluginSettingTab, Setting, setIcon, Notice } from 'obsidian';
|
|||
import MPPlugin from '../main'; // 修改插件名以匹配类名
|
||||
import { CreateTemplateModal } from './CreateTemplateModal';
|
||||
import { CreateFontModal } from './CreateFontModal';
|
||||
import { ConfirmModal } from './ConfirmModal'; // 添加确认模态框导入
|
||||
|
||||
import { CreateBackgroundModal } from './CreateBackgroundModal'; // 添加导入
|
||||
import { ConfirmModal } from './ConfirmModal';
|
||||
import { TemplatePreviewModal } from './templatePreviewModal'; // 添加导入
|
||||
export class MPSettingTab extends PluginSettingTab {
|
||||
plugin: MPPlugin; // 修改插件类型以匹配类名
|
||||
private expandedSections: Set<string> = new Set();
|
||||
|
|
@ -16,15 +17,15 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
private createSection(containerEl: HTMLElement, title: string, renderContent: (contentEl: HTMLElement) => void) {
|
||||
const section = containerEl.createDiv('settings-section');
|
||||
const header = section.createDiv('settings-section-header');
|
||||
|
||||
|
||||
const toggle = header.createSpan('settings-section-toggle');
|
||||
setIcon(toggle, 'chevron-right');
|
||||
|
||||
|
||||
header.createEl('h4', { text: title });
|
||||
|
||||
|
||||
const content = section.createDiv('settings-section-content');
|
||||
renderContent(content);
|
||||
|
||||
|
||||
header.addEventListener('click', () => {
|
||||
const isExpanded = !section.hasClass('is-expanded');
|
||||
section.toggleClass('is-expanded', isExpanded);
|
||||
|
|
@ -35,13 +36,13 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
this.expandedSections.delete(title);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (this.expandedSections.has(title) || (!containerEl.querySelector('.settings-section'))) {
|
||||
section.addClass('is-expanded');
|
||||
setIcon(toggle, 'chevron-down');
|
||||
this.expandedSections.add(title);
|
||||
}
|
||||
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
|
|
@ -54,9 +55,206 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
|
||||
this.createSection(containerEl, '基本设置', el => this.renderBasicSettings(el));
|
||||
this.createSection(containerEl, '模板设置', el => this.renderTemplateSettings(el));
|
||||
this.createSection(containerEl, '背景设置', el => this.renderBackgroundSettings(el));
|
||||
}
|
||||
|
||||
private renderTemplateSettings(containerEl: HTMLElement): void {
|
||||
private renderBasicSettings(containerEl: HTMLElement): void {
|
||||
// 字体管理区域
|
||||
const fontSection = containerEl.createDiv('mp-settings-subsection');
|
||||
const fontHeader = fontSection.createDiv('mp-settings-subsection-header');
|
||||
const fontToggle = fontHeader.createSpan('mp-settings-subsection-toggle');
|
||||
setIcon(fontToggle, 'chevron-right');
|
||||
|
||||
fontHeader.createEl('h3', { text: '字体管理' });
|
||||
|
||||
const fontContent = fontSection.createDiv('mp-settings-subsection-content');
|
||||
|
||||
// 折叠/展开逻辑
|
||||
fontHeader.addEventListener('click', () => {
|
||||
const isExpanded = !fontSection.hasClass('is-expanded');
|
||||
fontSection.toggleClass('is-expanded', isExpanded);
|
||||
setIcon(fontToggle, isExpanded ? 'chevron-down' : 'chevron-right');
|
||||
});
|
||||
|
||||
// 字体列表
|
||||
const fontList = fontContent.createDiv('font-management');
|
||||
this.plugin.settingsManager.getFontOptions().forEach(font => {
|
||||
const fontItem = fontList.createDiv('font-item');
|
||||
const setting = new Setting(fontItem)
|
||||
.setName(font.label)
|
||||
.setDesc(font.value);
|
||||
|
||||
// 只为非预设字体添加编辑和删除按钮
|
||||
if (!font.isPreset) {
|
||||
setting
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('pencil')
|
||||
.setTooltip('编辑')
|
||||
.onClick(() => {
|
||||
new CreateFontModal(
|
||||
this.app,
|
||||
async (updatedFont) => {
|
||||
await this.plugin.settingsManager.updateFont(font.value, updatedFont);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
},
|
||||
font
|
||||
).open();
|
||||
}))
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('trash')
|
||||
.setTooltip('删除')
|
||||
.onClick(() => {
|
||||
// 新增确认模态框
|
||||
new ConfirmModal(
|
||||
this.app,
|
||||
'确认删除字体',
|
||||
`确定要删除「${font.label}」字体配置吗?`,
|
||||
async () => {
|
||||
await this.plugin.settingsManager.removeFont(font.value);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
}
|
||||
).open();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// 添加新字体按钮
|
||||
new Setting(fontContent)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('+ 添加字体')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
new CreateFontModal(
|
||||
this.app,
|
||||
async (newFont) => {
|
||||
await this.plugin.settingsManager.addCustomFont(newFont);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
}
|
||||
).open();
|
||||
}));
|
||||
}
|
||||
|
||||
private renderTemplateSettings(containerEl: HTMLElement): void {
|
||||
// 模板显示设置部分 - 从基本设置移动到这里
|
||||
const templateVisibilitySection = containerEl.createDiv('mp-settings-subsection');
|
||||
const templateVisibilityHeader = templateVisibilitySection.createDiv('mp-settings-subsection-header');
|
||||
|
||||
const templateVisibilityToggle = templateVisibilityHeader.createSpan('mp-settings-subsection-toggle');
|
||||
setIcon(templateVisibilityToggle, 'chevron-right');
|
||||
|
||||
templateVisibilityHeader.createEl('h3', { text: '模板显示设置' });
|
||||
|
||||
const templateVisibilityContent = templateVisibilitySection.createDiv('mp-settings-subsection-content');
|
||||
|
||||
// 折叠/展开逻辑
|
||||
templateVisibilityHeader.addEventListener('click', () => {
|
||||
const isExpanded = !templateVisibilitySection.hasClass('is-expanded');
|
||||
templateVisibilitySection.toggleClass('is-expanded', isExpanded);
|
||||
setIcon(templateVisibilityToggle, isExpanded ? 'chevron-down' : 'chevron-right');
|
||||
});
|
||||
|
||||
// 模板选择容器
|
||||
const templateSelectionContainer = templateVisibilityContent.createDiv('template-selection-container');
|
||||
|
||||
// 左侧:所有模板列表
|
||||
const allTemplatesContainer = templateSelectionContainer.createDiv('all-templates-container');
|
||||
allTemplatesContainer.createEl('h4', { text: '隐藏模板' });
|
||||
const allTemplatesList = allTemplatesContainer.createDiv('templates-list');
|
||||
|
||||
// 中间:控制按钮
|
||||
const controlButtonsContainer = templateSelectionContainer.createDiv('control-buttons-container');
|
||||
const addButton = controlButtonsContainer.createEl('button', { text: '>' });
|
||||
const removeButton = controlButtonsContainer.createEl('button', { text: '<' });
|
||||
|
||||
// 右侧:显示的模板列表
|
||||
const visibleTemplatesContainer = templateSelectionContainer.createDiv('visible-templates-container');
|
||||
visibleTemplatesContainer.createEl('h4', { text: '显示模板' });
|
||||
const visibleTemplatesList = visibleTemplatesContainer.createDiv('templates-list');
|
||||
|
||||
// 获取所有模板
|
||||
const allTemplates = this.plugin.settingsManager.getAllTemplates();
|
||||
|
||||
// 渲染模板列表
|
||||
const renderTemplateLists = () => {
|
||||
// 清空列表
|
||||
allTemplatesList.empty();
|
||||
visibleTemplatesList.empty();
|
||||
|
||||
// 填充左侧列表(所有未显示的模板)
|
||||
allTemplates
|
||||
.filter(template => template.isVisible === false)
|
||||
.forEach(template => {
|
||||
const templateItem = allTemplatesList.createDiv('template-list-item');
|
||||
templateItem.textContent = template.name;
|
||||
templateItem.dataset.templateId = template.id;
|
||||
|
||||
// 点击选中/取消选中
|
||||
templateItem.addEventListener('click', () => {
|
||||
templateItem.toggleClass('selected', !templateItem.hasClass('selected'));
|
||||
});
|
||||
});
|
||||
|
||||
// 填充右侧列表(所有显示的模板)
|
||||
allTemplates
|
||||
.filter(template => template.isVisible !== false) // 默认显示
|
||||
.forEach(template => {
|
||||
const templateItem = visibleTemplatesList.createDiv('template-list-item');
|
||||
templateItem.textContent = template.name;
|
||||
templateItem.dataset.templateId = template.id;
|
||||
|
||||
// 点击选中/取消选中
|
||||
templateItem.addEventListener('click', () => {
|
||||
templateItem.toggleClass('selected', !templateItem.hasClass('selected'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 初始渲染
|
||||
renderTemplateLists();
|
||||
|
||||
// 添加按钮事件
|
||||
addButton.addEventListener('click', async () => {
|
||||
const selectedItems = Array.from(allTemplatesList.querySelectorAll('.template-list-item.selected'));
|
||||
if (selectedItems.length === 0) return;
|
||||
|
||||
for (const item of selectedItems) {
|
||||
const templateId = (item as HTMLElement).dataset.templateId;
|
||||
if (!templateId) continue;
|
||||
|
||||
const template = allTemplates.find(t => t.id === templateId);
|
||||
if (template) {
|
||||
template.isVisible = true;
|
||||
await this.plugin.settingsManager.updateTemplate(templateId, template);
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplateLists();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
});
|
||||
|
||||
// 移除按钮事件
|
||||
removeButton.addEventListener('click', async () => {
|
||||
const selectedItems = Array.from(visibleTemplatesList.querySelectorAll('.template-list-item.selected'));
|
||||
if (selectedItems.length === 0) return;
|
||||
|
||||
for (const item of selectedItems) {
|
||||
const templateId = (item as HTMLElement).dataset.templateId;
|
||||
if (!templateId) continue;
|
||||
|
||||
const template = allTemplates.find(t => t.id === templateId);
|
||||
if (template) {
|
||||
template.isVisible = false;
|
||||
await this.plugin.settingsManager.updateTemplate(templateId, template);
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplateLists();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
});
|
||||
|
||||
// 模板管理区域
|
||||
const templateList = containerEl.createDiv('template-management');
|
||||
// 渲染自定义模板
|
||||
|
|
@ -69,6 +267,12 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
.setName(template.name)
|
||||
.setDesc(template.description)
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('eye')
|
||||
.setTooltip('预览')
|
||||
.onClick(() => {
|
||||
new TemplatePreviewModal(this.app, template, this.plugin.templateManager).open(); // 修改为使用预览模态框
|
||||
}))
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('pencil')
|
||||
.setTooltip('编辑')
|
||||
.onClick(() => {
|
||||
|
|
@ -83,7 +287,7 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
template
|
||||
).open();
|
||||
}))
|
||||
.addExtraButton(btn =>
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('trash')
|
||||
.setTooltip('删除')
|
||||
.onClick(() => {
|
||||
|
|
@ -100,7 +304,7 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
// 添加新模板按钮
|
||||
new Setting(containerEl)
|
||||
.addButton(btn => btn
|
||||
|
|
@ -119,198 +323,184 @@ export class MPSettingTab extends PluginSettingTab {
|
|||
}));
|
||||
}
|
||||
|
||||
private renderBasicSettings(containerEl: HTMLElement): void {
|
||||
// 模板显示设置部分
|
||||
const templateVisibilitySection = containerEl.createDiv('mp-settings-subsection');
|
||||
const templateVisibilityHeader = templateVisibilitySection.createDiv('mp-settings-subsection-header');
|
||||
|
||||
const templateVisibilityToggle = templateVisibilityHeader.createSpan('mp-settings-subsection-toggle');
|
||||
setIcon(templateVisibilityToggle, 'chevron-right');
|
||||
|
||||
templateVisibilityHeader.createEl('h3', { text: '模板显示设置' });
|
||||
|
||||
const templateVisibilityContent = templateVisibilitySection.createDiv('mp-settings-subsection-content');
|
||||
|
||||
private renderBackgroundSettings(containerEl: HTMLElement): void {
|
||||
// 背景显示设置部分
|
||||
const backgroundVisibilitySection = containerEl.createDiv('mp-settings-subsection');
|
||||
const backgroundVisibilityHeader = backgroundVisibilitySection.createDiv('mp-settings-subsection-header');
|
||||
|
||||
const backgroundVisibilityToggle = backgroundVisibilityHeader.createSpan('mp-settings-subsection-toggle');
|
||||
setIcon(backgroundVisibilityToggle, 'chevron-right');
|
||||
|
||||
backgroundVisibilityHeader.createEl('h3', { text: '背景显示设置' });
|
||||
|
||||
const backgroundVisibilityContent = backgroundVisibilitySection.createDiv('mp-settings-subsection-content');
|
||||
|
||||
// 折叠/展开逻辑
|
||||
templateVisibilityHeader.addEventListener('click', () => {
|
||||
const isExpanded = !templateVisibilitySection.hasClass('is-expanded');
|
||||
templateVisibilitySection.toggleClass('is-expanded', isExpanded);
|
||||
setIcon(templateVisibilityToggle, isExpanded ? 'chevron-down' : 'chevron-right');
|
||||
backgroundVisibilityHeader.addEventListener('click', () => {
|
||||
const isExpanded = !backgroundVisibilitySection.hasClass('is-expanded');
|
||||
backgroundVisibilitySection.toggleClass('is-expanded', isExpanded);
|
||||
setIcon(backgroundVisibilityToggle, isExpanded ? 'chevron-down' : 'chevron-right');
|
||||
});
|
||||
|
||||
// 模板选择容器
|
||||
const templateSelectionContainer = templateVisibilityContent.createDiv('template-selection-container');
|
||||
|
||||
// 左侧:所有模板列表
|
||||
const allTemplatesContainer = templateSelectionContainer.createDiv('all-templates-container');
|
||||
allTemplatesContainer.createEl('h4', { text: '隐藏模板' });
|
||||
const allTemplatesList = allTemplatesContainer.createDiv('templates-list');
|
||||
|
||||
|
||||
// 背景选择容器
|
||||
const backgroundSelectionContainer = backgroundVisibilityContent.createDiv('background-selection-container');
|
||||
|
||||
// 左侧:所有背景列表
|
||||
const allBackgroundsContainer = backgroundSelectionContainer.createDiv('all-backgrounds-container');
|
||||
allBackgroundsContainer.createEl('h4', { text: '隐藏背景' });
|
||||
const allBackgroundsList = allBackgroundsContainer.createDiv('backgrounds-list');
|
||||
|
||||
// 中间:控制按钮
|
||||
const controlButtonsContainer = templateSelectionContainer.createDiv('control-buttons-container');
|
||||
const controlButtonsContainer = backgroundSelectionContainer.createDiv('control-buttons-container');
|
||||
const addButton = controlButtonsContainer.createEl('button', { text: '>' });
|
||||
const removeButton = controlButtonsContainer.createEl('button', { text: '<' });
|
||||
|
||||
// 右侧:显示的模板列表
|
||||
const visibleTemplatesContainer = templateSelectionContainer.createDiv('visible-templates-container');
|
||||
visibleTemplatesContainer.createEl('h4', { text: '显示模板' });
|
||||
const visibleTemplatesList = visibleTemplatesContainer.createDiv('templates-list');
|
||||
|
||||
// 获取所有模板
|
||||
const allTemplates = this.plugin.settingsManager.getAllTemplates();
|
||||
|
||||
// 渲染模板列表
|
||||
const renderTemplateLists = () => {
|
||||
|
||||
// 右侧:显示的背景列表
|
||||
const visibleBackgroundsContainer = backgroundSelectionContainer.createDiv('visible-backgrounds-container');
|
||||
visibleBackgroundsContainer.createEl('h4', { text: '显示背景' });
|
||||
const visibleBackgroundsList = visibleBackgroundsContainer.createDiv('backgrounds-list');
|
||||
|
||||
// 获取所有背景
|
||||
const allBackgrounds = this.plugin.settingsManager.getAllBackgrounds();
|
||||
|
||||
// 渲染背景列表
|
||||
const renderBackgroundLists = () => {
|
||||
// 清空列表
|
||||
allTemplatesList.empty();
|
||||
visibleTemplatesList.empty();
|
||||
|
||||
// 填充左侧列表(所有未显示的模板)
|
||||
allTemplates
|
||||
.filter(template => template.isVisible === false)
|
||||
.forEach(template => {
|
||||
const templateItem = allTemplatesList.createDiv('template-list-item');
|
||||
templateItem.textContent = template.name;
|
||||
templateItem.dataset.templateId = template.id;
|
||||
|
||||
allBackgroundsList.empty();
|
||||
visibleBackgroundsList.empty();
|
||||
|
||||
// 填充左侧列表(所有未显示的背景)
|
||||
allBackgrounds
|
||||
.filter(background => background.isVisible === false)
|
||||
.forEach(background => {
|
||||
const backgroundItem = allBackgroundsList.createDiv('background-list-item');
|
||||
backgroundItem.textContent = background.name;
|
||||
backgroundItem.dataset.backgroundId = background.id;
|
||||
|
||||
// 点击选中/取消选中
|
||||
templateItem.addEventListener('click', () => {
|
||||
templateItem.toggleClass('selected', !templateItem.hasClass('selected'));
|
||||
backgroundItem.addEventListener('click', () => {
|
||||
backgroundItem.toggleClass('selected', !backgroundItem.hasClass('selected'));
|
||||
});
|
||||
});
|
||||
|
||||
// 填充右侧列表(所有显示的模板)
|
||||
allTemplates
|
||||
.filter(template => template.isVisible !== false) // 默认显示
|
||||
.forEach(template => {
|
||||
const templateItem = visibleTemplatesList.createDiv('template-list-item');
|
||||
templateItem.textContent = template.name;
|
||||
templateItem.dataset.templateId = template.id;
|
||||
|
||||
|
||||
// 填充右侧列表(所有显示的背景)
|
||||
allBackgrounds
|
||||
.filter(background => background.isVisible !== false) // 默认显示
|
||||
.forEach(background => {
|
||||
const backgroundItem = visibleBackgroundsList.createDiv('background-list-item');
|
||||
backgroundItem.textContent = background.name;
|
||||
backgroundItem.dataset.backgroundId = background.id;
|
||||
|
||||
// 点击选中/取消选中
|
||||
templateItem.addEventListener('click', () => {
|
||||
templateItem.toggleClass('selected', !templateItem.hasClass('selected'));
|
||||
backgroundItem.addEventListener('click', () => {
|
||||
backgroundItem.toggleClass('selected', !backgroundItem.hasClass('selected'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// 初始渲染
|
||||
renderTemplateLists();
|
||||
|
||||
renderBackgroundLists();
|
||||
|
||||
// 添加按钮事件
|
||||
addButton.addEventListener('click', async () => {
|
||||
const selectedItems = Array.from(allTemplatesList.querySelectorAll('.template-list-item.selected'));
|
||||
const selectedItems = Array.from(allBackgroundsList.querySelectorAll('.background-list-item.selected'));
|
||||
if (selectedItems.length === 0) return;
|
||||
|
||||
|
||||
for (const item of selectedItems) {
|
||||
const templateId = (item as HTMLElement).dataset.templateId;
|
||||
if (!templateId) continue;
|
||||
|
||||
const template = allTemplates.find(t => t.id === templateId);
|
||||
if (template) {
|
||||
template.isVisible = true;
|
||||
await this.plugin.settingsManager.updateTemplate(templateId, template);
|
||||
const backgroundId = (item as HTMLElement).dataset.backgroundId;
|
||||
if (!backgroundId) continue;
|
||||
|
||||
const background = allBackgrounds.find(b => b.id === backgroundId);
|
||||
if (background) {
|
||||
background.isVisible = true;
|
||||
await this.plugin.settingsManager.updateBackground(backgroundId, background);
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplateLists();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
|
||||
renderBackgroundLists();
|
||||
new Notice('背景显示设置已更新');
|
||||
});
|
||||
|
||||
|
||||
// 移除按钮事件
|
||||
removeButton.addEventListener('click', async () => {
|
||||
const selectedItems = Array.from(visibleTemplatesList.querySelectorAll('.template-list-item.selected'));
|
||||
const selectedItems = Array.from(visibleBackgroundsList.querySelectorAll('.background-list-item.selected'));
|
||||
if (selectedItems.length === 0) return;
|
||||
|
||||
|
||||
for (const item of selectedItems) {
|
||||
const templateId = (item as HTMLElement).dataset.templateId;
|
||||
if (!templateId) continue;
|
||||
|
||||
const template = allTemplates.find(t => t.id === templateId);
|
||||
if (template) {
|
||||
template.isVisible = false;
|
||||
await this.plugin.settingsManager.updateTemplate(templateId, template);
|
||||
const backgroundId = (item as HTMLElement).dataset.backgroundId;
|
||||
if (!backgroundId) continue;
|
||||
|
||||
const background = allBackgrounds.find(b => b.id === backgroundId);
|
||||
if (background) {
|
||||
background.isVisible = false;
|
||||
await this.plugin.settingsManager.updateBackground(backgroundId, background);
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplateLists();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
|
||||
renderBackgroundLists();
|
||||
new Notice('背景显示设置已更新');
|
||||
});
|
||||
|
||||
|
||||
// 字体管理区域
|
||||
const fontSection = containerEl.createDiv('mp-settings-subsection');
|
||||
const fontHeader = fontSection.createDiv('mp-settings-subsection-header');
|
||||
const fontToggle = fontHeader.createSpan('mp-settings-subsection-toggle');
|
||||
setIcon(fontToggle, 'chevron-right');
|
||||
|
||||
fontHeader.createEl('h3', { text: '字体管理' });
|
||||
|
||||
const fontContent = fontSection.createDiv('mp-settings-subsection-content');
|
||||
|
||||
// 折叠/展开逻辑
|
||||
fontHeader.addEventListener('click', () => {
|
||||
const isExpanded = !fontSection.hasClass('is-expanded');
|
||||
fontSection.toggleClass('is-expanded', isExpanded);
|
||||
setIcon(fontToggle, isExpanded ? 'chevron-down' : 'chevron-right');
|
||||
});
|
||||
|
||||
// 字体列表
|
||||
const fontList = fontContent.createDiv('font-management');
|
||||
this.plugin.settingsManager.getFontOptions().forEach(font => {
|
||||
const fontItem = fontList.createDiv('font-item');
|
||||
const setting = new Setting(fontItem)
|
||||
.setName(font.label)
|
||||
.setDesc(font.value);
|
||||
|
||||
// 只为非预设字体添加编辑和删除按钮
|
||||
if (!font.isPreset) {
|
||||
setting
|
||||
.addExtraButton(btn =>
|
||||
|
||||
// 背景管理区域
|
||||
const backgroundList = containerEl.createDiv('background-management');
|
||||
|
||||
// 渲染自定义背景
|
||||
backgroundList.createEl('h4', { text: '自定义背景', cls: 'background-custom-header' });
|
||||
this.plugin.settingsManager.getAllBackgrounds()
|
||||
.filter(background => !background.isPreset)
|
||||
.forEach(background => {
|
||||
const backgroundItem = backgroundList.createDiv('background-item');
|
||||
new Setting(backgroundItem)
|
||||
.setName(background.name)
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('pencil')
|
||||
.setTooltip('编辑')
|
||||
.onClick(() => {
|
||||
new CreateFontModal(
|
||||
// 使用背景编辑模态框
|
||||
new CreateBackgroundModal(
|
||||
this.app,
|
||||
async (updatedFont) => {
|
||||
await this.plugin.settingsManager.updateFont(font.value, updatedFont);
|
||||
async (updatedBackground) => {
|
||||
await this.plugin.settingsManager.updateBackground(background.id, updatedBackground);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
new Notice('背景已更新');
|
||||
},
|
||||
font
|
||||
background
|
||||
).open();
|
||||
}))
|
||||
.addExtraButton(btn =>
|
||||
.addExtraButton(btn =>
|
||||
btn.setIcon('trash')
|
||||
.setTooltip('删除')
|
||||
.onClick(() => {
|
||||
// 新增确认模态框
|
||||
new ConfirmModal(
|
||||
this.app,
|
||||
'确认删除字体',
|
||||
`确定要删除「${font.label}」字体配置吗?`,
|
||||
'确认删除背景',
|
||||
`确定要删除「${background.name}」背景吗?此操作不可恢复。`,
|
||||
async () => {
|
||||
await this.plugin.settingsManager.removeFont(font.value);
|
||||
await this.plugin.settingsManager.removeBackground(background.id);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
new Notice('背景已删除');
|
||||
}
|
||||
).open();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// 添加新字体按钮
|
||||
new Setting(fontContent)
|
||||
|
||||
// 添加背景预览
|
||||
const previewEl = backgroundItem.createDiv('background-preview');
|
||||
previewEl.setAttribute('style', background.style);
|
||||
});
|
||||
|
||||
// 添加新背景按钮
|
||||
new Setting(containerEl)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('+ 添加字体')
|
||||
.setButtonText('+ 新建背景')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
new CreateFontModal(
|
||||
// 使用新的背景创建模态框
|
||||
new CreateBackgroundModal(
|
||||
this.app,
|
||||
async (newFont) => {
|
||||
await this.plugin.settingsManager.addCustomFont(newFont);
|
||||
async (newBackground) => {
|
||||
await this.plugin.settingsManager.addCustomBackground(newBackground);
|
||||
this.display();
|
||||
new Notice('请重启 Obsidian 或重新加载以使更改生效');
|
||||
new Notice('背景已创建');
|
||||
}
|
||||
).open();
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Template } from '../templateManager';
|
||||
import { Background } from '../backgroundManager';
|
||||
|
||||
interface MPSettings {
|
||||
backgroundId: string;
|
||||
|
|
@ -7,6 +8,8 @@ interface MPSettings {
|
|||
fontSize: number;
|
||||
templates: Template[];
|
||||
customTemplates: Template[];
|
||||
backgrounds: Background[];
|
||||
customBackgrounds: Background[];
|
||||
customFonts: { value: string; label: string; isPreset?: boolean }[];
|
||||
}
|
||||
|
||||
|
|
@ -17,6 +20,8 @@ const DEFAULT_SETTINGS: MPSettings = {
|
|||
fontSize: 16,
|
||||
templates: [],
|
||||
customTemplates: [],
|
||||
backgrounds: [],
|
||||
customBackgrounds: [],
|
||||
customFonts: [
|
||||
{
|
||||
value: 'Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif',
|
||||
|
|
@ -58,6 +63,21 @@ export class SettingsManager {
|
|||
if (!savedData.customFonts) {
|
||||
savedData.customFonts = DEFAULT_SETTINGS.customFonts;
|
||||
}
|
||||
// 加载背景设置
|
||||
if (!savedData.backgrounds || savedData.backgrounds.length === 0) {
|
||||
const { backgrounds } = await import('../backgrounds');
|
||||
savedData.backgrounds = backgrounds.backgrounds.map(background => ({
|
||||
...background,
|
||||
isPreset: true,
|
||||
isVisible: true
|
||||
}));
|
||||
}
|
||||
if (!savedData.customBackgrounds) {
|
||||
savedData.customBackgrounds = [];
|
||||
}
|
||||
if (!savedData.customFonts) {
|
||||
savedData.customFonts = DEFAULT_SETTINGS.customFonts;
|
||||
}
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, savedData);
|
||||
}
|
||||
|
||||
|
|
@ -155,4 +175,62 @@ export class SettingsManager {
|
|||
await this.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// 背景相关方法
|
||||
getAllBackgrounds(): Background[] {
|
||||
return [...this.settings.backgrounds, ...this.settings.customBackgrounds];
|
||||
}
|
||||
|
||||
getVisibleBackgrounds(): Background[] {
|
||||
return this.getAllBackgrounds().filter(background => background.isVisible !== false);
|
||||
}
|
||||
|
||||
getBackground(backgroundId: string): Background | undefined {
|
||||
return this.settings.backgrounds.find(background => background.id === backgroundId)
|
||||
|| this.settings.customBackgrounds.find(background => background.id === backgroundId);
|
||||
}
|
||||
|
||||
async addCustomBackground(background: Background) {
|
||||
background.isPreset = false;
|
||||
background.isVisible = true; // 默认可见
|
||||
this.settings.customBackgrounds.push(background);
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
async updateBackground(backgroundId: string, updatedBackground: Partial<Background>) {
|
||||
const presetBackgroundIndex = this.settings.backgrounds.findIndex(b => b.id === backgroundId);
|
||||
if (presetBackgroundIndex !== -1) {
|
||||
this.settings.backgrounds[presetBackgroundIndex] = {
|
||||
...this.settings.backgrounds[presetBackgroundIndex],
|
||||
...updatedBackground
|
||||
};
|
||||
await this.saveSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
const customBackgroundIndex = this.settings.customBackgrounds.findIndex(b => b.id === backgroundId);
|
||||
if (customBackgroundIndex !== -1) {
|
||||
this.settings.customBackgrounds[customBackgroundIndex] = {
|
||||
...this.settings.customBackgrounds[customBackgroundIndex],
|
||||
...updatedBackground
|
||||
};
|
||||
await this.saveSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async removeBackground(backgroundId: string): Promise<boolean> {
|
||||
const background = this.getBackground(backgroundId);
|
||||
if (background && !background.isPreset) {
|
||||
this.settings.customBackgrounds = this.settings.customBackgrounds.filter(b => b.id !== backgroundId);
|
||||
if (this.settings.backgroundId === backgroundId) {
|
||||
this.settings.backgroundId = 'default';
|
||||
}
|
||||
await this.saveSettings();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
75
src/settings/templatePreviewModal.ts
Normal file
75
src/settings/templatePreviewModal.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { App, Modal } from 'obsidian';
|
||||
import { TemplateManager } from '../templateManager';
|
||||
|
||||
export class TemplatePreviewModal extends Modal {
|
||||
private template: any;
|
||||
private templateManager: TemplateManager;
|
||||
|
||||
constructor(app: App, template: any, templateManager: TemplateManager) {
|
||||
super(app);
|
||||
this.template = template;
|
||||
this.templateManager = templateManager;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.addClass('template-preview-modal');
|
||||
|
||||
// 添加标题
|
||||
contentEl.createEl('h2', { text: `模板预览: ${this.template.name}`, cls: 'mp-template-title' });
|
||||
|
||||
// 添加预览区域
|
||||
const container = contentEl.createDiv('tp-mp-preview-area');
|
||||
const content = container.createDiv('tp-mp-content-section');
|
||||
|
||||
// 标题样式
|
||||
content.createEl('h2', { text: '探索夜半插件的无限可能'});
|
||||
content.createEl('h3', { text: '探索我的插件,让您的笔记发布变得更加轻松!'});
|
||||
|
||||
// 段落样式
|
||||
const paragraph1 = content.createEl('p');
|
||||
paragraph1.createEl('span', { text: '插件为您提供各种' });
|
||||
paragraph1.createEl('strong', { text: '优雅的操作,' });
|
||||
paragraph1.createEl('span', { text: '助您轻松发布笔记。' });
|
||||
|
||||
const paragraph2 = content.createEl('p');
|
||||
paragraph2.createEl('span', { text: '通过插件,您可以快速组织内容,' });
|
||||
paragraph2.createEl('em', { text: '提升工作效率。' });
|
||||
|
||||
content.createEl('hr');
|
||||
|
||||
// 列表样式
|
||||
const list = content.createEl('ol');
|
||||
list.createEl('li', { text: '轻松定制模板样式' });
|
||||
list.createEl('li', { text: '实时预览模板效果' });
|
||||
|
||||
// 引用样式
|
||||
const quote = content.createEl('blockquote');
|
||||
quote.createEl('p', { text: '“让笔记发帖变得如此简单。”' });
|
||||
|
||||
// 代码样式
|
||||
const codeBlock = content.createEl('pre');
|
||||
const header = codeBlock.createDiv('mp-code-header'); // 添加窗口按钮
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const dot = document.createElement('span');
|
||||
dot.className = 'mp-code-dot';
|
||||
header.appendChild(dot);
|
||||
}
|
||||
codeBlock.insertBefore(header, codeBlock.firstChild);
|
||||
codeBlock.createEl('code', { text: 'console.log("欢迎使用夜半插件!");' });
|
||||
|
||||
// 添加打赏引导文案
|
||||
content.createEl('strong', { text: '如果您觉得我的插件对您有帮助,请打赏支持我。'});
|
||||
|
||||
// 分隔线样式
|
||||
content.createEl('hr');
|
||||
|
||||
this.templateManager.applyTemplate(container, this.template);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,4 +2,6 @@
|
|||
@import url('./view/preview.css');
|
||||
@import url('./settings/settings.css');
|
||||
@import url('./settings/font-modal.css');
|
||||
@import url('./settings/template-modal.css');
|
||||
@import url('./settings/template-modal.css');
|
||||
@import url('./settings/template-preview-modal.css');
|
||||
@import url('./settings/background-modal.css');
|
||||
71
src/styles/settings/background-modal.css
Normal file
71
src/styles/settings/background-modal.css
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
.mp-background-modal {
|
||||
padding: 20px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.mp-background-modal h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.mp-background-modal .background-basic-section,
|
||||
.mp-background-modal .background-type-specific-section,
|
||||
.mp-background-modal .background-preview-section,
|
||||
.mp-background-modal .background-button-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mp-background-modal .background-preview-section h3 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.1em;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.mp-background-modal .background-preview {
|
||||
height: 120px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mp-background-modal .background-preview span {
|
||||
padding: 5px 10px;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.mp-background-modal .is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mp-background-modal .background-image-preview {
|
||||
margin-top: 10px;
|
||||
max-height: 150px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.mp-background-modal .background-image-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.mp-background-modal .setting-item {
|
||||
border-top: none;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.mp-background-modal textarea {
|
||||
width: 100%;
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
|
||||
/* 设置子区域折叠样式 */
|
||||
.mp-settings .mp-settings-subsection {
|
||||
margin-bottom: 1.5rem;
|
||||
margin: 0 12px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 8px;
|
||||
overflow-y: auto;
|
||||
|
|
@ -283,4 +283,128 @@
|
|||
|
||||
.mp-settings .mp-settings-subsection.is-expanded .mp-settings-subsection-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 背景管理区域样式 */
|
||||
.mp-settings .background-management {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
/* 背景管理滚动条样式 */
|
||||
.mp-settings .background-management::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.mp-settings .background-management::-webkit-scrollbar-track {
|
||||
background: var(--background-primary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mp-settings .background-management::-webkit-scrollbar-thumb {
|
||||
background: var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mp-settings .background-management::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--background-modifier-border-hover);
|
||||
}
|
||||
|
||||
/* 背景项目样式 */
|
||||
.mp-settings .background-item {
|
||||
margin-bottom: 0.8rem;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.mp-settings .background-item:hover {
|
||||
box-shadow: 0 4px 12px var(--background-modifier-box-shadow);
|
||||
}
|
||||
|
||||
.mp-settings .background-item .setting-item {
|
||||
border: none;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* 背景预览区域 */
|
||||
.mp-settings .background-preview {
|
||||
height: 20px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
/* 背景显示设置样式 */
|
||||
.background-selection-container {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
gap: 15px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.all-backgrounds-container, .visible-backgrounds-container {
|
||||
flex: 1;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
background-color: var(--background-primary);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.all-backgrounds-container h4, .visible-backgrounds-container h4 {
|
||||
margin: 0 0 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
font-size: 1.1em;
|
||||
color: var(--text-normal);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.backgrounds-list {
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.background-list-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
background-color: var(--background-secondary);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.background-list-item:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.background-list-item.selected {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
/* 背景列表滚动条样式 */
|
||||
.backgrounds-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.backgrounds-list::-webkit-scrollbar-track {
|
||||
background: var(--background-primary);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.backgrounds-list::-webkit-scrollbar-thumb {
|
||||
background: var(--background-modifier-border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.backgrounds-list::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--background-modifier-border-hover);
|
||||
}
|
||||
83
src/styles/settings/template-preview-modal.css
Normal file
83
src/styles/settings/template-preview-modal.css
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
.template-preview-modal {
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.template-preview-modal .mp-template-title{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===== 预览区域 ===== */
|
||||
.tp-mp-preview-area {
|
||||
padding: 10px 20px 20px 20px;
|
||||
margin: 10px;
|
||||
height: calc(100% - 180px);
|
||||
overflow-y: auto;
|
||||
background: #fcfcfc;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.02);
|
||||
border: 1px solid rgba(82, 144, 220, 0.08);
|
||||
}
|
||||
|
||||
/* 链接样式 */
|
||||
.tp-mp-content-section a {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.tp-mp-content-section table {
|
||||
border-collapse: collapse;
|
||||
margin: 1em 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tp-mp-content-section th,
|
||||
.tp-mp-content-section td {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 分割线样式 */
|
||||
.tp-mp-content-section hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* 删除线样式 */
|
||||
.tp-mp-content-section del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* 任务列表样式 */
|
||||
.tp-mp-content-section .task-list-item {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.tp-mp-content-section .task-list-item input[type="checkbox"] {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* 脚注样式 */
|
||||
.tp-mp-content-section .footnote-ref,
|
||||
.tp-mp-content-section .footnote-backref {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 图片样式 */
|
||||
.tp-mp-content-section img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
/* 引用块样式 */
|
||||
.tp-mp-content-section blockquote p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
|
@ -98,8 +98,8 @@ export class TemplateManager {
|
|||
this.currentFontSize = size;
|
||||
}
|
||||
|
||||
public applyTemplate(element: HTMLElement): void {
|
||||
const styles = this.currentTemplate.styles;
|
||||
public applyTemplate(element: HTMLElement, template?: Template): void {
|
||||
const styles = template ? template.styles : this.currentTemplate.styles;
|
||||
// 应用标题样式
|
||||
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach(tag => {
|
||||
element.querySelectorAll(tag).forEach(el => {
|
||||
|
|
|
|||
1
src/utils/nanoid.ts
Normal file
1
src/utils/nanoid.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { nanoid } from 'nanoid';
|
||||
|
|
@ -30,7 +30,7 @@ export class MPView extends ItemView {
|
|||
super(leaf);
|
||||
this.templateManager = templateManager;
|
||||
this.settingsManager = settingsManager;
|
||||
this.backgroundManager = new BackgroundManager();
|
||||
this.backgroundManager = new BackgroundManager(this.settingsManager);
|
||||
}
|
||||
|
||||
getViewType() {
|
||||
|
|
@ -68,7 +68,7 @@ export class MPView extends ItemView {
|
|||
// 添加背景选择器
|
||||
const backgroundOptions = [
|
||||
{ value: '', label: '无背景' },
|
||||
...(this.backgroundManager.getAllBackgrounds()?.map(bg => ({
|
||||
...(this.settingsManager.getVisibleBackgrounds()?.map(bg => ({
|
||||
value: bg.id,
|
||||
label: bg.name
|
||||
})) || [])
|
||||
|
|
|
|||
Loading…
Reference in a new issue