update at 2025-10-08 17:06:31
This commit is contained in:
30
build.sh
30
build.sh
@@ -4,16 +4,24 @@ set -e # 出错立即退出
|
|||||||
# 1. 构建
|
# 1. 构建
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# 2. 目标路径
|
# 2. 目标目录
|
||||||
TARGET=~/myweb/.obsidian/plugins/note-to-mp/main.js
|
PLUGIN_DIR=~/myweb/.obsidian/plugins/note-to-mp
|
||||||
BACKUP=~/myweb/.obsidian/plugins/note-to-mp/main.js.bk
|
FILES=("main.js" "styles.css" "manifest.json")
|
||||||
|
|
||||||
# 3. 如果存在 main.js,先备份
|
# 3. 遍历文件,逐一备份并覆盖
|
||||||
if [ -f "$TARGET" ]; then
|
for FILE in "${FILES[@]}"; do
|
||||||
cp -f "$TARGET" "$BACKUP"
|
TARGET="$PLUGIN_DIR/$FILE"
|
||||||
echo "已备份 $TARGET -> $BACKUP"
|
BACKUP="$PLUGIN_DIR/$FILE.bk"
|
||||||
fi
|
|
||||||
|
|
||||||
# 4. 覆盖复制新的 main.js
|
if [ -f "$TARGET" ]; then
|
||||||
cp -f main.js "$TARGET"
|
cp -f "$TARGET" "$BACKUP"
|
||||||
echo "已更新 $TARGET"
|
echo "已备份 $TARGET -> $BACKUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$FILE" ]; then
|
||||||
|
cp -f "$FILE" "$TARGET"
|
||||||
|
echo "已更新 $TARGET"
|
||||||
|
else
|
||||||
|
echo "⚠️ 源文件 $FILE 不存在,跳过"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ export class BatchPublishModal extends Modal {
|
|||||||
const preview = this.plugin.getNotePreview();
|
const preview = this.plugin.getNotePreview();
|
||||||
if (preview) {
|
if (preview) {
|
||||||
// 确保预览器处于微信模式
|
// 确保预览器处于微信模式
|
||||||
preview.setCurrentPlatform('wechat');
|
preview.currentPlatform = 'wechat';
|
||||||
await preview.renderMarkdown(file);
|
await preview.renderMarkdown(file);
|
||||||
await preview.postToWechat();
|
await preview.postToWechat();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
41
src/main.ts
41
src/main.ts
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Plugin, WorkspaceLeaf, App, PluginManifest, Menu, Notice, TAbstractFile, TFile, TFolder } from 'obsidian';
|
import { Plugin, WorkspaceLeaf, App, PluginManifest, Menu, Notice, TAbstractFile, TFile, TFolder } from 'obsidian';
|
||||||
import { NotePreview, VIEW_TYPE_NOTE_PREVIEW } from './mp-preview';
|
import { NotePreview, VIEW_TYPE_NOTE_PREVIEW } from './note-preview';
|
||||||
import { NMPSettings } from './settings';
|
import { NMPSettings } from './settings';
|
||||||
import { NoteToMpSettingTab } from './setting-tab';
|
import { NoteToMpSettingTab } from './setting-tab';
|
||||||
import AssetsManager from './assets';
|
import AssetsManager from './assets';
|
||||||
@@ -38,6 +38,7 @@ import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
|||||||
export default class NoteToMpPlugin extends Plugin {
|
export default class NoteToMpPlugin extends Plugin {
|
||||||
settings: NMPSettings;
|
settings: NMPSettings;
|
||||||
assetsManager: AssetsManager;
|
assetsManager: AssetsManager;
|
||||||
|
ribbonIconEl: HTMLElement | null = null;
|
||||||
constructor(app: App, manifest: PluginManifest) {
|
constructor(app: App, manifest: PluginManifest) {
|
||||||
super(app, manifest);
|
super(app, manifest);
|
||||||
AssetsManager.setup(app, manifest);
|
AssetsManager.setup(app, manifest);
|
||||||
@@ -55,6 +56,12 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
uevent('load');
|
uevent('load');
|
||||||
this.app.workspace.onLayoutReady(()=>{
|
this.app.workspace.onLayoutReady(()=>{
|
||||||
this.loadResource();
|
this.loadResource();
|
||||||
|
// 布局就绪后清理旧视图并自动打开一个新的标准预览(可选)
|
||||||
|
this.cleanupLegacyViews();
|
||||||
|
// 如果当前没有我们的预览叶子,自动激活一次,改善首次体验
|
||||||
|
if (this.app.workspace.getLeavesOfType(VIEW_TYPE_NOTE_PREVIEW).length === 0) {
|
||||||
|
this.activateView();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.registerView(
|
this.registerView(
|
||||||
@@ -62,10 +69,10 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
(leaf) => new NotePreview(leaf, this)
|
(leaf) => new NotePreview(leaf, this)
|
||||||
);
|
);
|
||||||
|
|
||||||
const ribbonIconEl = this.addRibbonIcon('clipboard-paste', '复制到公众号', (evt: MouseEvent) => {
|
this.ribbonIconEl = this.addRibbonIcon('clipboard-paste', '复制到公众号', (evt: MouseEvent) => {
|
||||||
this.activateView();
|
this.activateView();
|
||||||
});
|
});
|
||||||
ribbonIconEl.addClass('note-to-mp-plugin-ribbon-class');
|
this.ribbonIconEl.addClass('note-to-mp-plugin-ribbon-class');
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'note-to-mp-preview',
|
id: 'note-to-mp-preview',
|
||||||
@@ -146,7 +153,35 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
|
console.log('Unloading NoteToMP');
|
||||||
|
// 移除 ribbon icon,避免重载插件时重复创建
|
||||||
|
if (this.ribbonIconEl) {
|
||||||
|
this.ribbonIconEl.remove();
|
||||||
|
this.ribbonIconEl = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理历史失效视图:
|
||||||
|
* 某些用户可能曾使用过旧插件构建(例如 note-mp-preview-manager),升级后残留的标签页会提示“插件不再活动”。
|
||||||
|
* 这里做一次性清理,避免用户手动关标签造成困扰。
|
||||||
|
*/
|
||||||
|
private cleanupLegacyViews() {
|
||||||
|
try {
|
||||||
|
const legacyIds = ['note-mp-preview-manager']; // 可扩展
|
||||||
|
const { workspace } = this.app;
|
||||||
|
// 遍历所有叶子,关闭可能的失效 view(无法直接匹配 id 时,仅检测报错视图类型)
|
||||||
|
workspace.getLeavesOfType(VIEW_TYPE_NOTE_PREVIEW).forEach(l => {
|
||||||
|
// 如果 view 的 plugin 不存在或 manifest id 不匹配我们当前的 id,则关闭
|
||||||
|
const anyView: any = l.view;
|
||||||
|
const vid = (anyView?.plugin?.manifest?.id) || '';
|
||||||
|
if (vid && vid !== this.manifest.id && legacyIds.includes(vid)) {
|
||||||
|
workspace.detachLeavesOfType(VIEW_TYPE_NOTE_PREVIEW);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[NoteToMp] cleanupLegacyViews 失败', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* - 与批量发布/图片处理集成预留
|
* - 与批量发布/图片处理集成预留
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventRef, ItemView, Workspace, WorkspaceLeaf, Notice, Platform as ObsidianPlatform, TFile, TFolder, TAbstractFile, Plugin } from 'obsidian';
|
import { EventRef, ItemView, Workspace, WorkspaceLeaf, Notice, Platform, TFile, TFolder, TAbstractFile, Plugin } from 'obsidian';
|
||||||
import { uevent, debounce, waitForLayoutReady } from './utils';
|
import { uevent, debounce, waitForLayoutReady } from './utils';
|
||||||
import { NMPSettings } from './settings';
|
import { NMPSettings } from './settings';
|
||||||
import AssetsManager from './assets';
|
import AssetsManager from './assets';
|
||||||
@@ -20,9 +20,7 @@ import { XiaohongshuContentAdapter } from './xiaohongshu/adapter';
|
|||||||
import { XiaohongshuImageManager } from './xiaohongshu/image';
|
import { XiaohongshuImageManager } from './xiaohongshu/image';
|
||||||
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
||||||
import { XiaohongshuPost } from './xiaohongshu/types';
|
import { XiaohongshuPost } from './xiaohongshu/types';
|
||||||
import { XiaohongshuPreviewView } from './xiaohongshu/xhs-preview';
|
import { XiaohongshuPreviewView } from './xiaohongshu/preview-view';
|
||||||
import { PlatformChooser } from './platform-chooser';
|
|
||||||
import { Platform } from './types';
|
|
||||||
// 切图功能
|
// 切图功能
|
||||||
import { sliceArticleImage } from './slice-image';
|
import { sliceArticleImage } from './slice-image';
|
||||||
|
|
||||||
@@ -42,7 +40,7 @@ export class NotePreview extends ItemView {
|
|||||||
useLocalCover: HTMLInputElement;
|
useLocalCover: HTMLInputElement;
|
||||||
msgView: HTMLDivElement;
|
msgView: HTMLDivElement;
|
||||||
wechatSelect: HTMLSelectElement;
|
wechatSelect: HTMLSelectElement;
|
||||||
platformChooser: PlatformChooser; // 平台选择器组件
|
platformSelect: HTMLSelectElement; // 新增:平台选择器
|
||||||
themeSelect: HTMLSelectElement;
|
themeSelect: HTMLSelectElement;
|
||||||
highlightSelect: HTMLSelectElement;
|
highlightSelect: HTMLSelectElement;
|
||||||
listeners?: EventRef[];
|
listeners?: EventRef[];
|
||||||
@@ -55,7 +53,7 @@ export class NotePreview extends ItemView {
|
|||||||
currentTheme: string;
|
currentTheme: string;
|
||||||
currentHighlight: string;
|
currentHighlight: string;
|
||||||
currentAppId: string;
|
currentAppId: string;
|
||||||
currentPlatform: Platform = 'wechat'; // 当前选择的平台,默认微信
|
currentPlatform: string = 'wechat'; // 新增:当前选择的平台,默认微信
|
||||||
markedParser: MarkedParser;
|
markedParser: MarkedParser;
|
||||||
cachedElements: Map<string, string> = new Map();
|
cachedElements: Map<string, string> = new Map();
|
||||||
_articleRender: ArticleRender | null = null;
|
_articleRender: ArticleRender | null = null;
|
||||||
@@ -86,14 +84,6 @@ export class NotePreview extends ItemView {
|
|||||||
return '笔记预览';
|
return '笔记预览';
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentPlatform(): Platform {
|
|
||||||
return this.currentPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentPlatform(platform: Platform): void {
|
|
||||||
this.currentPlatform = platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
get render() {
|
get render() {
|
||||||
if (!this._articleRender) {
|
if (!this._articleRender) {
|
||||||
this._articleRender = new ArticleRender(this.app, this, this.styleEl, this.articleDiv);
|
this._articleRender = new ArticleRender(this.app, this, this.styleEl, this.articleDiv);
|
||||||
@@ -208,16 +198,36 @@ export class NotePreview extends ItemView {
|
|||||||
this.toolbar = parent.createDiv({ cls: 'preview-toolbar' });
|
this.toolbar = parent.createDiv({ cls: 'preview-toolbar' });
|
||||||
let lineDiv;
|
let lineDiv;
|
||||||
|
|
||||||
// 使用平台选择器组件
|
// 平台选择器(新增)- 始终显示
|
||||||
this.platformChooser = new PlatformChooser(this.toolbar, this.currentPlatform);
|
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line platform-selector-line' });
|
||||||
this.platformChooser.build();
|
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%); border-left: 4px solid #1e88e5; border-radius: 6px; margin: 8px 10px;';
|
||||||
this.platformChooser.onPlatformChange(async (platform: Platform) => {
|
|
||||||
this.currentPlatform = platform;
|
const platformLabel = lineDiv.createDiv({ cls: 'style-label' });
|
||||||
|
platformLabel.innerText = '发布平台';
|
||||||
|
platformLabel.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
||||||
|
|
||||||
|
const platformSelect = lineDiv.createEl('select', { cls: 'style-select' });
|
||||||
|
platformSelect.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 150px; font-weight: 500;';
|
||||||
|
|
||||||
|
// 添加平台选项
|
||||||
|
const wechatOption = platformSelect.createEl('option');
|
||||||
|
wechatOption.value = 'wechat';
|
||||||
|
wechatOption.text = '微信公众号';
|
||||||
|
wechatOption.selected = true;
|
||||||
|
|
||||||
|
const xiaohongshuOption = platformSelect.createEl('option');
|
||||||
|
xiaohongshuOption.value = 'xiaohongshu';
|
||||||
|
xiaohongshuOption.text = '小红书';
|
||||||
|
|
||||||
|
platformSelect.onchange = async () => {
|
||||||
|
this.currentPlatform = platformSelect.value;
|
||||||
await this.onPlatformChanged();
|
await this.onPlatformChanged();
|
||||||
});
|
};
|
||||||
|
|
||||||
|
this.platformSelect = platformSelect;
|
||||||
|
|
||||||
// 公众号
|
// 公众号
|
||||||
if (this.settings.wxInfo.length > 1 || ObsidianPlatform.isDesktop) {
|
if (this.settings.wxInfo.length > 1 || Platform.isDesktop) {
|
||||||
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line wechat-only' });
|
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line wechat-only' });
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: white; border-radius: 6px; margin: 8px 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.08);';
|
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: white; border-radius: 6px; margin: 8px 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.08);';
|
||||||
|
|
||||||
@@ -246,7 +256,7 @@ export class NotePreview extends ItemView {
|
|||||||
}
|
}
|
||||||
this.wechatSelect = wxSelect;
|
this.wechatSelect = wxSelect;
|
||||||
|
|
||||||
if (ObsidianPlatform.isDesktop) {
|
if (Platform.isDesktop) {
|
||||||
// 分隔线
|
// 分隔线
|
||||||
const separator = lineDiv.createDiv();
|
const separator = lineDiv.createDiv();
|
||||||
separator.style.cssText = 'width: 1px; height: 24px; background: #dadce0; margin: 0 4px;';
|
separator.style.cssText = 'width: 1px; height: 24px; background: #dadce0; margin: 0 4px;';
|
||||||
@@ -283,7 +293,7 @@ export class NotePreview extends ItemView {
|
|||||||
await this.renderMarkdown();
|
await this.renderMarkdown();
|
||||||
uevent('refresh');
|
uevent('refresh');
|
||||||
}
|
}
|
||||||
if (ObsidianPlatform.isDesktop) {
|
if (Platform.isDesktop) {
|
||||||
const copyBtn = lineDiv.createEl('button', { text: '📋 复制' });
|
const copyBtn = lineDiv.createEl('button', { text: '📋 复制' });
|
||||||
copyBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
copyBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
||||||
copyBtn.onmouseenter = () => copyBtn.style.transform = 'translateY(-1px)';
|
copyBtn.onmouseenter = () => copyBtn.style.transform = 'translateY(-1px)';
|
||||||
@@ -331,7 +341,7 @@ export class NotePreview extends ItemView {
|
|||||||
uevent('pub-images');
|
uevent('pub-images');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObsidianPlatform.isDesktop && this.settings.isAuthKeyVaild()) {
|
if (Platform.isDesktop && this.settings.isAuthKeyVaild()) {
|
||||||
const htmlBtn = lineDiv.createEl('button', { text: '💾 导出HTML' });
|
const htmlBtn = lineDiv.createEl('button', { text: '💾 导出HTML' });
|
||||||
htmlBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
htmlBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
||||||
htmlBtn.onmouseenter = () => htmlBtn.style.transform = 'translateY(-1px)';
|
htmlBtn.onmouseenter = () => htmlBtn.style.transform = 'translateY(-1px)';
|
||||||
@@ -559,21 +569,22 @@ export class NotePreview extends ItemView {
|
|||||||
* 切换到小红书预览模式
|
* 切换到小红书预览模式
|
||||||
*/
|
*/
|
||||||
private switchToXiaohongshuMode() {
|
private switchToXiaohongshuMode() {
|
||||||
// 隐藏微信相关的工具栏行
|
// 隐藏微信相关的工具栏行和平台选择器
|
||||||
if (this.toolbar) {
|
if (this.toolbar) {
|
||||||
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
||||||
wechatLines.forEach((line: HTMLElement) => {
|
wechatLines.forEach((line: HTMLElement) => {
|
||||||
line.style.display = 'none';
|
line.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// 平台选择器保持显示
|
// 也隐藏平台选择器行
|
||||||
if (this.platformChooser) {
|
// const platformLine = this.toolbar.querySelector('.platform-selector-line') as HTMLElement;
|
||||||
this.platformChooser.show();
|
// if (platformLine) {
|
||||||
|
// platformLine.style.display = 'none';
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 隐藏渲染区域
|
// 隐藏渲染区域
|
||||||
//if (this.renderDiv) this.renderDiv.style.display = 'none';
|
if (this.renderDiv) this.renderDiv.style.display = 'none';
|
||||||
|
|
||||||
// 创建或显示小红书预览视图
|
// 创建或显示小红书预览视图
|
||||||
if (!this._xiaohongshuPreview) {
|
if (!this._xiaohongshuPreview) {
|
||||||
@@ -588,7 +599,7 @@ export class NotePreview extends ItemView {
|
|||||||
this._xiaohongshuPreview.onPublishCallback = async () => {
|
this._xiaohongshuPreview.onPublishCallback = async () => {
|
||||||
await this.onXiaohongshuPublish();
|
await this.onXiaohongshuPublish();
|
||||||
};
|
};
|
||||||
this._xiaohongshuPreview.onPlatformChangeCallback = async (platform: Platform) => {
|
this._xiaohongshuPreview.onPlatformChangeCallback = async (platform: string) => {
|
||||||
this.currentPlatform = platform;
|
this.currentPlatform = platform;
|
||||||
if (platform === 'wechat') {
|
if (platform === 'wechat') {
|
||||||
await this.onPlatformChanged();
|
await this.onPlatformChanged();
|
||||||
@@ -611,17 +622,18 @@ export class NotePreview extends ItemView {
|
|||||||
* 切换到微信公众号模式
|
* 切换到微信公众号模式
|
||||||
*/
|
*/
|
||||||
private switchToWechatMode() {
|
private switchToWechatMode() {
|
||||||
// 显示微信相关的工具栏行
|
// 显示微信相关的工具栏行和平台选择器
|
||||||
if (this.toolbar) {
|
if (this.toolbar) {
|
||||||
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
||||||
wechatLines.forEach((line: HTMLElement) => {
|
wechatLines.forEach((line: HTMLElement) => {
|
||||||
line.style.display = 'flex';
|
line.style.display = 'flex';
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// 平台选择器保持显示
|
// 也显示平台选择器行
|
||||||
if (this.platformChooser) {
|
const platformLine = this.toolbar.querySelector('.platform-selector-line') as HTMLElement;
|
||||||
this.platformChooser.show();
|
if (platformLine) {
|
||||||
|
platformLine.style.display = 'flex';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示渲染区域
|
// 显示渲染区域
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
/**
|
|
||||||
* 平台选择器组件
|
|
||||||
* 提供统一的平台选择界面和切换逻辑
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Platform, PlatformInfo, PlatformChangeCallback, SUPPORTED_PLATFORMS } from './types';
|
|
||||||
|
|
||||||
export class PlatformChooser {
|
|
||||||
private container: HTMLElement;
|
|
||||||
private selectElement: HTMLSelectElement;
|
|
||||||
private currentPlatform: Platform;
|
|
||||||
private onChangeCallback?: PlatformChangeCallback;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, defaultPlatform: Platform = 'wechat') {
|
|
||||||
this.container = container;
|
|
||||||
this.currentPlatform = defaultPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建平台选择器UI
|
|
||||||
*/
|
|
||||||
public build(): HTMLElement {
|
|
||||||
const lineDiv = this.container.createDiv({ cls: 'toolbar-line platform-selector-line' });
|
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%); border-left: 4px solid #1e88e5; border-radius: 6px; margin: 8px 10px;';
|
|
||||||
|
|
||||||
// 标签
|
|
||||||
const platformLabel = lineDiv.createDiv({ cls: 'style-label' });
|
|
||||||
platformLabel.innerText = '发布平台';
|
|
||||||
platformLabel.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
|
||||||
|
|
||||||
// 选择器
|
|
||||||
this.selectElement = lineDiv.createEl('select', { cls: 'style-select' });
|
|
||||||
this.selectElement.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 150px; font-weight: 500;';
|
|
||||||
|
|
||||||
// 添加平台选项
|
|
||||||
SUPPORTED_PLATFORMS.forEach(platform => {
|
|
||||||
const option = this.selectElement.createEl('option');
|
|
||||||
option.value = platform.id;
|
|
||||||
option.text = `${platform.icon || ''} ${platform.name}`.trim();
|
|
||||||
if (platform.id === this.currentPlatform) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 绑定切换事件
|
|
||||||
this.selectElement.onchange = async () => {
|
|
||||||
const newPlatform = this.selectElement.value as Platform;
|
|
||||||
await this.switchPlatform(newPlatform);
|
|
||||||
};
|
|
||||||
|
|
||||||
return lineDiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置平台切换回调
|
|
||||||
*/
|
|
||||||
public onPlatformChange(callback: PlatformChangeCallback): void {
|
|
||||||
this.onChangeCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前选择的平台
|
|
||||||
*/
|
|
||||||
public getCurrentPlatform(): Platform {
|
|
||||||
return this.currentPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前平台(程序化切换)
|
|
||||||
*/
|
|
||||||
public setCurrentPlatform(platform: Platform): void {
|
|
||||||
this.currentPlatform = platform;
|
|
||||||
if (this.selectElement) {
|
|
||||||
this.selectElement.value = platform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换平台
|
|
||||||
*/
|
|
||||||
private async switchPlatform(platform: Platform): Promise<void> {
|
|
||||||
if (platform === this.currentPlatform) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[PlatformChooser] 切换平台: ${this.currentPlatform} -> ${platform}`);
|
|
||||||
|
|
||||||
const oldPlatform = this.currentPlatform;
|
|
||||||
this.currentPlatform = platform;
|
|
||||||
|
|
||||||
// 调用回调函数
|
|
||||||
if (this.onChangeCallback) {
|
|
||||||
try {
|
|
||||||
await this.onChangeCallback(platform);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[PlatformChooser] 平台切换失败:', error);
|
|
||||||
// 回滚到旧平台
|
|
||||||
this.currentPlatform = oldPlatform;
|
|
||||||
this.selectElement.value = oldPlatform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示选择器
|
|
||||||
*/
|
|
||||||
public show(): void {
|
|
||||||
if (this.container) {
|
|
||||||
const line = this.container.querySelector('.platform-selector-line') as HTMLElement;
|
|
||||||
if (line) {
|
|
||||||
line.style.display = 'flex';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏选择器
|
|
||||||
*/
|
|
||||||
public hide(): void {
|
|
||||||
if (this.container) {
|
|
||||||
const line = this.container.querySelector('.platform-selector-line') as HTMLElement;
|
|
||||||
if (line) {
|
|
||||||
line.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源
|
|
||||||
*/
|
|
||||||
public cleanup(): void {
|
|
||||||
this.onChangeCallback = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/**
|
|
||||||
* 平台选择器组件
|
|
||||||
* 提供统一的平台选择界面,配合 PlatformManager 处理切换逻辑
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Platform, PlatformInfo, SUPPORTED_PLATFORMS } from './types';
|
|
||||||
import { PlatformManager } from './platform-manager';
|
|
||||||
|
|
||||||
export class PlatformChooser {
|
|
||||||
private container: HTMLElement;
|
|
||||||
private selectElement: HTMLSelectElement;
|
|
||||||
private platformManager: PlatformManager;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, platformManager: PlatformManager) {
|
|
||||||
this.container = container;
|
|
||||||
this.platformManager = platformManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建平台选择器UI
|
|
||||||
*/
|
|
||||||
public build(): HTMLElement {
|
|
||||||
const lineDiv = this.container.createDiv({ cls: 'toolbar-line platform-selector-line' });
|
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%); border-left: 4px solid #1e88e5; border-radius: 6px; margin: 8px 10px;';
|
|
||||||
|
|
||||||
// 标签
|
|
||||||
const platformLabel = lineDiv.createDiv({ cls: 'style-label' });
|
|
||||||
platformLabel.innerText = '发布平台';
|
|
||||||
platformLabel.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
|
||||||
|
|
||||||
// 选择器
|
|
||||||
this.selectElement = lineDiv.createEl('select', { cls: 'style-select' });
|
|
||||||
this.selectElement.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 150px; font-weight: 500;';
|
|
||||||
|
|
||||||
// 添加平台选项
|
|
||||||
const currentPlatform = this.platformManager.getCurrentPlatform();
|
|
||||||
SUPPORTED_PLATFORMS.forEach(platform => {
|
|
||||||
const option = this.selectElement.createEl('option');
|
|
||||||
option.value = platform.id;
|
|
||||||
option.text = `${platform.icon || ''} ${platform.name}`.trim();
|
|
||||||
if (platform.id === currentPlatform) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 绑定切换事件 - 直接调用 PlatformManager
|
|
||||||
this.selectElement.onchange = async () => {
|
|
||||||
const newPlatform = this.selectElement.value as Platform;
|
|
||||||
try {
|
|
||||||
await this.platformManager.switchToPlatform(newPlatform);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[PlatformChooser] 平台切换失败:', error);
|
|
||||||
// 回滚选择器
|
|
||||||
this.selectElement.value = this.platformManager.getCurrentPlatform();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return lineDiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前选择的平台
|
|
||||||
*/
|
|
||||||
public getCurrentPlatform(): Platform {
|
|
||||||
return this.platformManager.getCurrentPlatform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前平台(程序化切换)
|
|
||||||
*/
|
|
||||||
public async setCurrentPlatform(platform: Platform): Promise<void> {
|
|
||||||
await this.platformManager.switchToPlatform(platform);
|
|
||||||
if (this.selectElement) {
|
|
||||||
this.selectElement.value = platform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示选择器
|
|
||||||
*/
|
|
||||||
public show(): void {
|
|
||||||
if (this.container) {
|
|
||||||
const line = this.container.querySelector('.platform-selector-line') as HTMLElement;
|
|
||||||
if (line) {
|
|
||||||
line.style.display = 'flex';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏选择器
|
|
||||||
*/
|
|
||||||
public hide(): void {
|
|
||||||
if (this.container) {
|
|
||||||
const line = this.container.querySelector('.platform-selector-line') as HTMLElement;
|
|
||||||
if (line) {
|
|
||||||
line.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源
|
|
||||||
*/
|
|
||||||
public cleanup(): void {
|
|
||||||
// 清理工作由 PlatformManager 负责
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
/**
|
|
||||||
* 平台管理器
|
|
||||||
* 负责管理和协调不同平台的预览视图
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Platform, IPlatformPreview } from './types';
|
|
||||||
import { PlatformChooser } from './platform-chooser';
|
|
||||||
|
|
||||||
export class PlatformManager {
|
|
||||||
private currentPlatform: Platform;
|
|
||||||
private platformChooser: PlatformChooser;
|
|
||||||
private platformPreviews: Map<Platform, IPlatformPreview>;
|
|
||||||
private container: HTMLElement;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, defaultPlatform: Platform = 'wechat') {
|
|
||||||
this.currentPlatform = defaultPlatform;
|
|
||||||
this.container = container;
|
|
||||||
this.platformPreviews = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化平台选择器
|
|
||||||
*/
|
|
||||||
public initChooser(toolbarContainer: HTMLElement): PlatformChooser {
|
|
||||||
this.platformChooser = new PlatformChooser(toolbarContainer, this.currentPlatform);
|
|
||||||
this.platformChooser.build();
|
|
||||||
this.platformChooser.onPlatformChange(async (platform: Platform) => {
|
|
||||||
await this.switchToPlatform(platform);
|
|
||||||
});
|
|
||||||
return this.platformChooser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册平台预览视图
|
|
||||||
*/
|
|
||||||
public registerPlatformPreview(platform: Platform, preview: IPlatformPreview): void {
|
|
||||||
this.platformPreviews.set(platform, preview);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前平台
|
|
||||||
*/
|
|
||||||
public getCurrentPlatform(): Platform {
|
|
||||||
return this.currentPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定平台的预览视图
|
|
||||||
*/
|
|
||||||
public getPlatformPreview(platform: Platform): IPlatformPreview | undefined {
|
|
||||||
return this.platformPreviews.get(platform);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前平台的预览视图
|
|
||||||
*/
|
|
||||||
public getCurrentPreview(): IPlatformPreview | undefined {
|
|
||||||
return this.platformPreviews.get(this.currentPlatform);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换到指定平台
|
|
||||||
*/
|
|
||||||
public async switchToPlatform(platform: Platform): Promise<void> {
|
|
||||||
if (platform === this.currentPlatform) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[PlatformManager] 切换平台: ${this.currentPlatform} -> ${platform}`);
|
|
||||||
|
|
||||||
// 隐藏当前平台的预览
|
|
||||||
const currentPreview = this.platformPreviews.get(this.currentPlatform);
|
|
||||||
if (currentPreview) {
|
|
||||||
this.hidePreview(this.currentPlatform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新当前平台
|
|
||||||
const oldPlatform = this.currentPlatform;
|
|
||||||
this.currentPlatform = platform;
|
|
||||||
|
|
||||||
// 显示新平台的预览
|
|
||||||
const newPreview = this.platformPreviews.get(platform);
|
|
||||||
if (newPreview) {
|
|
||||||
this.showPreview(platform);
|
|
||||||
} else {
|
|
||||||
console.warn(`[PlatformManager] 平台 ${platform} 的预览视图未注册`);
|
|
||||||
// 回滚
|
|
||||||
this.currentPlatform = oldPlatform;
|
|
||||||
if (this.platformChooser) {
|
|
||||||
this.platformChooser.setCurrentPlatform(oldPlatform);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新选择器
|
|
||||||
if (this.platformChooser) {
|
|
||||||
this.platformChooser.setCurrentPlatform(platform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示指定平台的预览
|
|
||||||
*/
|
|
||||||
private showPreview(platform: Platform): void {
|
|
||||||
const preview = this.platformPreviews.get(platform);
|
|
||||||
if (preview) {
|
|
||||||
// 调用预览视图的显示方法
|
|
||||||
const container = this.getPreviewContainer(platform);
|
|
||||||
if (container) {
|
|
||||||
container.style.display = 'flex';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏指定平台的预览
|
|
||||||
*/
|
|
||||||
private hidePreview(platform: Platform): void {
|
|
||||||
const preview = this.platformPreviews.get(platform);
|
|
||||||
if (preview) {
|
|
||||||
// 调用预览视图的隐藏方法
|
|
||||||
const container = this.getPreviewContainer(platform);
|
|
||||||
if (container) {
|
|
||||||
container.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取平台的容器元素
|
|
||||||
*/
|
|
||||||
private getPreviewContainer(platform: Platform): HTMLElement | null {
|
|
||||||
switch (platform) {
|
|
||||||
case 'wechat':
|
|
||||||
return this.container.querySelector('.wechat-preview-container') as HTMLElement;
|
|
||||||
case 'xiaohongshu':
|
|
||||||
return this.container.querySelector('.xiaohongshu-preview-container') as HTMLElement;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示平台选择器
|
|
||||||
*/
|
|
||||||
public showChooser(): void {
|
|
||||||
if (this.platformChooser) {
|
|
||||||
this.platformChooser.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏平台选择器
|
|
||||||
*/
|
|
||||||
public hideChooser(): void {
|
|
||||||
if (this.platformChooser) {
|
|
||||||
this.platformChooser.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源
|
|
||||||
*/
|
|
||||||
public cleanup(): void {
|
|
||||||
if (this.platformChooser) {
|
|
||||||
this.platformChooser.cleanup();
|
|
||||||
}
|
|
||||||
this.platformPreviews.forEach(preview => {
|
|
||||||
preview.cleanup();
|
|
||||||
});
|
|
||||||
this.platformPreviews.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
src/types.ts
63
src/types.ts
@@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* 公共类型定义文件
|
|
||||||
* 定义平台类型、回调接口等公共类型
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支持的发布平台类型
|
|
||||||
*/
|
|
||||||
export type Platform = 'wechat' | 'xiaohongshu';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平台信息接口
|
|
||||||
*/
|
|
||||||
export interface PlatformInfo {
|
|
||||||
id: Platform;
|
|
||||||
name: string;
|
|
||||||
icon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平台切换回调接口
|
|
||||||
*/
|
|
||||||
export interface PlatformChangeCallback {
|
|
||||||
(platform: Platform): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平台预览视图接口
|
|
||||||
*/
|
|
||||||
export interface IPlatformPreview {
|
|
||||||
/**
|
|
||||||
* 构建预览界面
|
|
||||||
*/
|
|
||||||
build(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染文章内容
|
|
||||||
*/
|
|
||||||
renderArticle(html: string, file: any): Promise<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示预览
|
|
||||||
*/
|
|
||||||
show(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏预览
|
|
||||||
*/
|
|
||||||
hide(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源
|
|
||||||
*/
|
|
||||||
cleanup(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支持的平台列表
|
|
||||||
*/
|
|
||||||
export const SUPPORTED_PLATFORMS: PlatformInfo[] = [
|
|
||||||
{ id: 'wechat', name: '微信公众号', icon: '📱' },
|
|
||||||
{ id: 'xiaohongshu', name: '小红书', icon: '📕' }
|
|
||||||
];
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
/**
|
|
||||||
* 微信公众号预览视图
|
|
||||||
* 专门处理微信公众号的预览和发布逻辑
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Notice, Platform as ObsidianPlatform, TFile } from 'obsidian';
|
|
||||||
import { IPlatformPreview } from './types';
|
|
||||||
import { NMPSettings } from './settings';
|
|
||||||
import AssetsManager from './assets';
|
|
||||||
import { ArticleRender } from './article-render';
|
|
||||||
|
|
||||||
export class WechatPreview implements IPlatformPreview {
|
|
||||||
private container: HTMLElement;
|
|
||||||
private toolbar: HTMLElement;
|
|
||||||
private renderDiv: HTMLElement;
|
|
||||||
private articleDiv: HTMLElement;
|
|
||||||
private settings: NMPSettings;
|
|
||||||
private assetsManager: AssetsManager;
|
|
||||||
private render: ArticleRender | null = null;
|
|
||||||
private app: any;
|
|
||||||
private itemView: any;
|
|
||||||
|
|
||||||
// UI 元素
|
|
||||||
private wechatSelect: HTMLSelectElement;
|
|
||||||
private themeSelect: HTMLSelectElement;
|
|
||||||
private highlightSelect: HTMLSelectElement;
|
|
||||||
private coverEl: HTMLInputElement;
|
|
||||||
private useDefaultCover: HTMLInputElement;
|
|
||||||
private useLocalCover: HTMLInputElement;
|
|
||||||
|
|
||||||
// 数据
|
|
||||||
private currentAppId: string;
|
|
||||||
private currentTheme: string;
|
|
||||||
private currentHighlight: string;
|
|
||||||
private currentFile?: TFile;
|
|
||||||
private articleHTML: string = '';
|
|
||||||
|
|
||||||
// 回调
|
|
||||||
public onRefreshCallback?: () => Promise<void>;
|
|
||||||
public onCopyCallback?: () => Promise<void>;
|
|
||||||
public onUploadCallback?: () => Promise<void>;
|
|
||||||
public onPublishCallback?: () => Promise<void>;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, app: any, itemView: any) {
|
|
||||||
this.container = container;
|
|
||||||
this.app = app;
|
|
||||||
this.itemView = itemView;
|
|
||||||
this.settings = NMPSettings.getInstance();
|
|
||||||
this.assetsManager = AssetsManager.getInstance();
|
|
||||||
this.currentTheme = this.settings.defaultStyle;
|
|
||||||
this.currentHighlight = this.settings.defaultHighlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建微信公众号预览界面
|
|
||||||
*/
|
|
||||||
public build(): void {
|
|
||||||
this.container.empty();
|
|
||||||
this.container.addClass('wechat-preview-container');
|
|
||||||
this.container.style.cssText = 'width: 100%; height: 100%; display: flex; flex-direction: column;';
|
|
||||||
|
|
||||||
// 构建工具栏
|
|
||||||
this.buildToolbar();
|
|
||||||
|
|
||||||
// 构建渲染区域
|
|
||||||
this.buildRenderArea();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建工具栏
|
|
||||||
*/
|
|
||||||
private buildToolbar(): void {
|
|
||||||
this.toolbar = this.container.createDiv({ cls: 'preview-toolbar wechat-toolbar' });
|
|
||||||
let lineDiv;
|
|
||||||
|
|
||||||
// 公众号选择
|
|
||||||
if (this.settings.wxInfo.length > 1 || ObsidianPlatform.isDesktop) {
|
|
||||||
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line wechat-only' });
|
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: white; border-radius: 6px; margin: 8px 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.08);';
|
|
||||||
|
|
||||||
const wxLabel = lineDiv.createDiv({ cls: 'style-label' });
|
|
||||||
wxLabel.innerText = '公众号';
|
|
||||||
wxLabel.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
|
||||||
|
|
||||||
this.wechatSelect = lineDiv.createEl('select', { cls: 'style-select' });
|
|
||||||
this.wechatSelect.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 200px;';
|
|
||||||
|
|
||||||
this.wechatSelect.onchange = () => {
|
|
||||||
this.currentAppId = this.wechatSelect.value;
|
|
||||||
this.onAppIdChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultOp = this.wechatSelect.createEl('option');
|
|
||||||
defaultOp.value = '';
|
|
||||||
defaultOp.text = '请在设置里配置公众号';
|
|
||||||
|
|
||||||
for (let i = 0; i < this.settings.wxInfo.length; i++) {
|
|
||||||
const op = this.wechatSelect.createEl('option');
|
|
||||||
const wx = this.settings.wxInfo[i];
|
|
||||||
op.value = wx.appid;
|
|
||||||
op.text = wx.name;
|
|
||||||
if (i === 0) {
|
|
||||||
op.selected = true;
|
|
||||||
this.currentAppId = wx.appid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ObsidianPlatform.isDesktop) {
|
|
||||||
// 分隔线
|
|
||||||
const separator = lineDiv.createDiv();
|
|
||||||
separator.style.cssText = 'width: 1px; height: 24px; background: #dadce0; margin: 0 4px;';
|
|
||||||
|
|
||||||
const openBtn = lineDiv.createEl('button', { text: '🌐 去公众号后台' });
|
|
||||||
openBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);';
|
|
||||||
openBtn.onmouseenter = () => openBtn.style.transform = 'translateY(-1px)';
|
|
||||||
openBtn.onmouseleave = () => openBtn.style.transform = 'translateY(0)';
|
|
||||||
openBtn.onclick = () => {
|
|
||||||
const { shell } = require('electron');
|
|
||||||
shell.openExternal('https://mp.weixin.qq.com');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (this.settings.wxInfo.length > 0) {
|
|
||||||
this.currentAppId = this.settings.wxInfo[0].appid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 功能按钮行
|
|
||||||
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line wechat-only' });
|
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: white; border-radius: 6px; margin: 8px 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); flex-wrap: wrap;';
|
|
||||||
|
|
||||||
// 刷新按钮
|
|
||||||
const refreshBtn = lineDiv.createEl('button', { text: '🔄 刷新' });
|
|
||||||
refreshBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);';
|
|
||||||
refreshBtn.onmouseenter = () => refreshBtn.style.transform = 'translateY(-1px)';
|
|
||||||
refreshBtn.onmouseleave = () => refreshBtn.style.transform = 'translateY(0)';
|
|
||||||
refreshBtn.onclick = async () => {
|
|
||||||
if (this.onRefreshCallback) {
|
|
||||||
await this.onRefreshCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 复制按钮
|
|
||||||
if (ObsidianPlatform.isDesktop) {
|
|
||||||
const copyBtn = lineDiv.createEl('button', { text: '📋 复制' });
|
|
||||||
copyBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
|
||||||
copyBtn.onmouseenter = () => copyBtn.style.transform = 'translateY(-1px)';
|
|
||||||
copyBtn.onmouseleave = () => copyBtn.style.transform = 'translateY(0)';
|
|
||||||
copyBtn.onclick = async () => {
|
|
||||||
if (this.onCopyCallback) {
|
|
||||||
await this.onCopyCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传图片按钮
|
|
||||||
const uploadImgBtn = lineDiv.createEl('button', { text: '📤 上传图片' });
|
|
||||||
uploadImgBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
|
||||||
uploadImgBtn.onmouseenter = () => uploadImgBtn.style.transform = 'translateY(-1px)';
|
|
||||||
uploadImgBtn.onmouseleave = () => uploadImgBtn.style.transform = 'translateY(0)';
|
|
||||||
uploadImgBtn.onclick = async () => {
|
|
||||||
if (this.onUploadCallback) {
|
|
||||||
await this.onUploadCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发草稿按钮
|
|
||||||
const postBtn = lineDiv.createEl('button', { text: '📝 发草稿' });
|
|
||||||
postBtn.style.cssText = 'padding: 6px 14px; background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 2px 6px rgba(30, 136, 229, 0.3);';
|
|
||||||
postBtn.onmouseenter = () => postBtn.style.transform = 'translateY(-1px)';
|
|
||||||
postBtn.onmouseleave = () => postBtn.style.transform = 'translateY(0)';
|
|
||||||
postBtn.onclick = async () => {
|
|
||||||
if (this.onPublishCallback) {
|
|
||||||
await this.onPublishCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 样式选择(如果启用)
|
|
||||||
if (this.settings.showStyleUI) {
|
|
||||||
lineDiv = this.toolbar.createDiv({ cls: 'toolbar-line wechat-only' });
|
|
||||||
lineDiv.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: white; border-radius: 6px; margin: 8px 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); flex-wrap: wrap;';
|
|
||||||
|
|
||||||
const cssStyle = lineDiv.createDiv({ cls: 'style-label' });
|
|
||||||
cssStyle.innerText = '样式';
|
|
||||||
cssStyle.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
|
||||||
|
|
||||||
this.themeSelect = lineDiv.createEl('select', { cls: 'style-select' });
|
|
||||||
this.themeSelect.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 120px;';
|
|
||||||
|
|
||||||
this.themeSelect.onchange = () => {
|
|
||||||
this.currentTheme = this.themeSelect.value;
|
|
||||||
if (this.render) {
|
|
||||||
this.render.updateStyle(this.themeSelect.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let s of this.assetsManager.themes) {
|
|
||||||
const op = this.themeSelect.createEl('option');
|
|
||||||
op.value = s.className;
|
|
||||||
op.text = s.name;
|
|
||||||
op.selected = s.className === this.settings.defaultStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分隔线
|
|
||||||
const separator = lineDiv.createDiv();
|
|
||||||
separator.style.cssText = 'width: 1px; height: 24px; background: #dadce0; margin: 0 4px;';
|
|
||||||
|
|
||||||
const highlightStyle = lineDiv.createDiv({ cls: 'style-label' });
|
|
||||||
highlightStyle.innerText = '代码高亮';
|
|
||||||
highlightStyle.style.cssText = 'font-size: 13px; color: #5f6368; font-weight: 500; white-space: nowrap;';
|
|
||||||
|
|
||||||
this.highlightSelect = lineDiv.createEl('select', { cls: 'style-select' });
|
|
||||||
this.highlightSelect.style.cssText = 'padding: 6px 12px; border: 1px solid #dadce0; border-radius: 6px; background: white; font-size: 13px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); min-width: 120px;';
|
|
||||||
|
|
||||||
this.highlightSelect.onchange = () => {
|
|
||||||
this.currentHighlight = this.highlightSelect.value;
|
|
||||||
if (this.render) {
|
|
||||||
this.render.updateHighLight(this.highlightSelect.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let s of this.assetsManager.highlights) {
|
|
||||||
const op = this.highlightSelect.createEl('option');
|
|
||||||
op.value = s.name;
|
|
||||||
op.text = s.name;
|
|
||||||
op.selected = s.name === this.settings.defaultHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建渲染区域
|
|
||||||
*/
|
|
||||||
private buildRenderArea(): void {
|
|
||||||
this.renderDiv = this.container.createDiv({ cls: 'render-div' });
|
|
||||||
this.articleDiv = this.renderDiv.createDiv({ cls: 'article' });
|
|
||||||
|
|
||||||
// 创建样式元素
|
|
||||||
const styleEl = this.container.createEl('style') as HTMLElement;
|
|
||||||
|
|
||||||
// 初始化渲染器
|
|
||||||
this.render = new ArticleRender(this.app, this.itemView, styleEl, this.articleDiv as HTMLDivElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染文章内容
|
|
||||||
*/
|
|
||||||
public async renderArticle(html: string, file: TFile): Promise<void> {
|
|
||||||
this.articleHTML = html;
|
|
||||||
this.currentFile = file;
|
|
||||||
|
|
||||||
// 使用渲染器渲染
|
|
||||||
if (this.render) {
|
|
||||||
await this.render.renderMarkdown(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示预览
|
|
||||||
*/
|
|
||||||
public show(): void {
|
|
||||||
if (this.container) {
|
|
||||||
this.container.style.display = 'flex';
|
|
||||||
}
|
|
||||||
// 显示所有微信相关的工具栏
|
|
||||||
if (this.toolbar) {
|
|
||||||
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
|
||||||
wechatLines.forEach((line: HTMLElement) => {
|
|
||||||
line.style.display = 'flex';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.renderDiv) {
|
|
||||||
this.renderDiv.style.display = 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏预览
|
|
||||||
*/
|
|
||||||
public hide(): void {
|
|
||||||
if (this.container) {
|
|
||||||
this.container.style.display = 'none';
|
|
||||||
}
|
|
||||||
// 隐藏所有微信相关的工具栏
|
|
||||||
if (this.toolbar) {
|
|
||||||
const wechatLines = this.toolbar.querySelectorAll('.wechat-only');
|
|
||||||
wechatLines.forEach((line: HTMLElement) => {
|
|
||||||
line.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.renderDiv) {
|
|
||||||
this.renderDiv.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源
|
|
||||||
*/
|
|
||||||
public cleanup(): void {
|
|
||||||
// 清理回调
|
|
||||||
this.onRefreshCallback = undefined;
|
|
||||||
this.onCopyCallback = undefined;
|
|
||||||
this.onUploadCallback = undefined;
|
|
||||||
this.onPublishCallback = undefined;
|
|
||||||
|
|
||||||
// 清理数据
|
|
||||||
this.currentFile = undefined;
|
|
||||||
this.articleHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前AppID
|
|
||||||
*/
|
|
||||||
public getCurrentAppId(): string {
|
|
||||||
return this.currentAppId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取渲染器
|
|
||||||
*/
|
|
||||||
public getRender(): ArticleRender | null {
|
|
||||||
return this.render;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 公众号切换事件
|
|
||||||
*/
|
|
||||||
private onAppIdChanged(): void {
|
|
||||||
// 可以在这里添加切换公众号后的逻辑
|
|
||||||
console.log('[WechatPreview] 切换到公众号:', this.currentAppId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
/* 文件:xiaohongshu/xhs-preview.ts — 小红书预览视图组件:顶部工具栏、分页导航、底部切图按钮。 */
|
/* 文件:xiaohongshu/preview-view.ts — 小红书预览视图组件:顶部工具栏、分页导航、底部切图按钮。 */
|
||||||
|
|
||||||
import { Notice, TFile } from 'obsidian';
|
import { Notice, TFile } from 'obsidian';
|
||||||
import { NMPSettings } from '../settings';
|
import { NMPSettings } from '../settings';
|
||||||
import AssetsManager from '../assets';
|
import AssetsManager from '../assets';
|
||||||
import { paginateArticle, renderPage, PageInfo } from './paginator';
|
import { paginateArticle, renderPage, PageInfo } from './paginator';
|
||||||
import { sliceCurrentPage, sliceAllPages } from './slice';
|
import { sliceCurrentPage, sliceAllPages } from './slice';
|
||||||
import { PlatformChooser } from '../platform-chooser';
|
|
||||||
import { Platform, IPlatformPreview } from '../types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小红书预览视图
|
* 小红书预览视图
|
||||||
*/
|
*/
|
||||||
export class XiaohongshuPreviewView implements IPlatformPreview {
|
export class XiaohongshuPreviewView {
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
settings: NMPSettings;
|
settings: NMPSettings;
|
||||||
assetsManager: AssetsManager;
|
assetsManager: AssetsManager;
|
||||||
@@ -39,7 +37,7 @@ export class XiaohongshuPreviewView implements IPlatformPreview {
|
|||||||
// 回调函数
|
// 回调函数
|
||||||
onRefreshCallback?: () => Promise<void>;
|
onRefreshCallback?: () => Promise<void>;
|
||||||
onPublishCallback?: () => Promise<void>;
|
onPublishCallback?: () => Promise<void>;
|
||||||
onPlatformChangeCallback?: (platform: Platform) => Promise<void>;
|
onPlatformChangeCallback?: (platform: string) => Promise<void>;
|
||||||
|
|
||||||
constructor(container: HTMLElement, app: any) {
|
constructor(container: HTMLElement, app: any) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
@@ -414,37 +412,4 @@ export class XiaohongshuPreviewView implements IPlatformPreview {
|
|||||||
new Notice('❌ 批量切图失败: ' + error.message);
|
new Notice('❌ 批量切图失败: ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示预览(实现 IPlatformPreview 接口)
|
|
||||||
*/
|
|
||||||
public show(): void {
|
|
||||||
if (this.container) {
|
|
||||||
this.container.style.display = 'flex';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏预览(实现 IPlatformPreview 接口)
|
|
||||||
*/
|
|
||||||
public hide(): void {
|
|
||||||
if (this.container) {
|
|
||||||
this.container.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理资源(实现 IPlatformPreview 接口)
|
|
||||||
*/
|
|
||||||
public cleanup(): void {
|
|
||||||
// 清理回调
|
|
||||||
this.onRefreshCallback = undefined;
|
|
||||||
this.onPublishCallback = undefined;
|
|
||||||
this.onPlatformChangeCallback = undefined;
|
|
||||||
|
|
||||||
// 清理数据
|
|
||||||
this.pages = [];
|
|
||||||
this.currentFile = null;
|
|
||||||
this.articleHTML = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
|
|
||||||
## 问题
|
## 问题
|
||||||
1. "发布平台"选“小红书”时,预览页面没有加载当前文章。
|
1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。
|
||||||
2. 顶部按钮适应窗口宽度,超出窗口,折行显示。
|
2. 顶部按钮适应窗口宽度,超出窗口,折行显示。
|
||||||
3. 页预览不完整,改为
|
3. 页预览不完整,改为
|
||||||
4. 修改:
|
4. 修改:
|
||||||
|
|||||||
Reference in New Issue
Block a user