diff --git a/src/components/TypographyView.ts b/src/components/TypographyView.ts
index 9a71b95..dcb36fc 100644
--- a/src/components/TypographyView.ts
+++ b/src/components/TypographyView.ts
@@ -8,7 +8,7 @@ import { ImgTemplateManager, ImgTemplate } from '../services/ImgTemplateManager'
import { ThemeManager, Theme } from '../services/ThemeManager';
import { CoverManager, CoverSettings } from '../services/CoverManager';
import { CoverSettingModal } from '../modals/CoverSettingModal';
-import { PaginatedEngine, IBlock, extractBlocks, generatePaginatedTOC } from '../utils/PaginatedEngine';
+import { PaginatedEngine, extractBlocks, generatePaginatedTOC } from '../utils/PaginatedEngine';
interface TypographySettings {
fontFamily: string;
fontSize: string;
@@ -128,8 +128,6 @@ export class TypographyView {
}
private createPreviewArea(container: HTMLElement) {
- container.createEl('h2', { text: i18n.t('PREVIEW') });
-
// 创建一个包含封面和内容的容器
const previewContainer = container.createDiv({ cls: 'typography-preview-container' });
@@ -144,25 +142,29 @@ export class TypographyView {
// 按照要求的顺序创建顶部选择器:书籍、模板、主题、字体、字号大小
private createTopSelectors(container: HTMLElement) {
- const selectorsContainer = container.createDiv({ cls: 'typography-selectors' });
+ // 创建第一行选择器容器
+ const selectorsRow1 = container.createDiv({ cls: 'typography-selectors-row' });
// 1. 创建书籍选择器
- this.createBookSelector(selectorsContainer);
+ this.createBookSelector(selectorsRow1);
// 2. 创建模板选择器
- this.createTemplateSelector(selectorsContainer);
+ this.createTemplateSelector(selectorsRow1);
// 3. 创建主题选择器
- this.createThemeSelector(selectorsContainer);
+ this.createThemeSelector(selectorsRow1);
+
+ // 创建第二行选择器容器
+ const selectorsRow2 = container.createDiv({ cls: 'typography-selectors-row' });
// 4. 创建字体选择器
- this.createFontSelector(selectorsContainer);
+ this.createFontSelector(selectorsRow2);
// 5. 创建字号大小控制
- this.createFontSizeControls(selectorsContainer);
+ this.createFontSizeControls(selectorsRow2);
// 6. 创建开本大小选择器
- this.createBookSizeSelector(selectorsContainer);
+ this.createBookSizeSelector(selectorsRow2);
}
// 自定义选择器通用方法
@@ -416,9 +418,13 @@ export class TypographyView {
private createFontSelector(parent: HTMLElement) {
const fontOptions = [
{ value: 'default', text: i18n.t('DEFAULT_FONT') || '默认字体' },
+ { value: 'songti', text: i18n.t('SONGTI_FONT') || '宋体' },
+ { value: 'heiti', text: i18n.t('HEITI_FONT') || '黑体' },
+ { value: 'kaiti', text: i18n.t('KAITI_FONT') || '楷体' },
+ { value: 'fangsong', text: i18n.t('FANGSONG_FONT') || '仿宋' },
{ value: 'serif', text: i18n.t('SERIF_FONT') || '衬线字体' },
{ value: 'sans-serif', text: i18n.t('SANS_SERIF_FONT') || '无衬线字体' },
- { value: 'monospace', text: i18n.t('MONOSPACE_FONT') || '等宽字体' }
+ { value: 'monospace', text: i18n.t('MONOSPACE_FONT') || '等宽字体' },
];
this.customFontSelect = this.createCustomSelect(
@@ -443,31 +449,60 @@ export class TypographyView {
private createFontSizeControls(parent: HTMLElement) {
const fontSizeGroup = parent.createEl('div', { cls: 'red-font-size-group' });
-
+ // 添加减小按钮
+ const decreaseButton = fontSizeGroup.createEl('button', {
+ cls: 'red-font-size-button red-decrease-button',
+ text: '-'
+ });
+
this.fontSizeInput = fontSizeGroup.createEl('input', {
cls: 'red-font-size-input',
type: 'text',
value: '16',
attr: {
- style: 'border: none; outline: none; background: transparent;'
+ style: 'border: none; outline: none; background: transparent;',
+ min: '12',
+ max: '30'
}
}) as HTMLInputElement;
-
+
+ // 添加增大按钮
+ const increaseButton = fontSizeGroup.createEl('button', {
+ cls: 'red-font-size-button red-increase-button',
+ text: '+'
+ });
+
+ // 添加单位标签
+ fontSizeGroup.createEl('span', {
+ cls: 'red-font-size-unit'
+ });
}
-
+
// 初始化字号控制
private initializeFontSizeControls() {
if (!this.fontSizeInput) return;
-
+
const updateFontSize = () => {
if (!this.fontSizeInput) return;
+
+ // 获取当前值并确保在有效范围内
+ let currentSize = parseInt(this.fontSizeInput.value) || 16;
+ currentSize = Math.max(12, Math.min(30, currentSize));
+
+ // 更新输入框值
+ this.fontSizeInput.value = currentSize.toString();
+
+ // 更新主题管理器中的字体大小
+ this.themeManager.setFontSize(currentSize);
+
+ // 更新预览
this.updatePreview();
};
-
+
// 获取按钮元素
- const decreaseButton = this.fontSizeInput.parentElement?.querySelector('button:first-child');
- const increaseButton = this.fontSizeInput.parentElement?.querySelector('button:last-child');
-
+ const decreaseButton = this.fontSizeInput.parentElement?.querySelector('.red-decrease-button');
+ const increaseButton = this.fontSizeInput.parentElement?.querySelector('.red-increase-button');
+
// 添加减小字号事件
decreaseButton?.addEventListener('click', () => {
if (!this.fontSizeInput) return;
@@ -477,7 +512,7 @@ export class TypographyView {
updateFontSize();
}
});
-
+
// 添加增大字号事件
increaseButton?.addEventListener('click', () => {
if (!this.fontSizeInput) return;
@@ -487,7 +522,7 @@ export class TypographyView {
updateFontSize();
}
});
-
+
// 添加输入框变化事件
this.fontSizeInput.addEventListener('change', updateFontSize);
this.fontSizeInput.addEventListener('input', updateFontSize);
@@ -586,60 +621,56 @@ export class TypographyView {
// 清空预览元素
this.previewElement.empty();
- // 应用模板
- if (this.currentTemplate) {
- this.currentTemplate.render(this.previewElement);
- }
-
- // 应用主题
- this.themeManager.applyTheme(this.previewElement);
-
- // 渲染内容
+ // 渲染内容(内部会应用主题和模板)
await this.renderPaginatedContent(settings);
-
-
}
}
- // 新增:渲染分页内容
+ // 修改 renderPaginatedContent 方法,确保一致的应用顺序
private async renderPaginatedContent(settings: TypographySettings) {
if (!this.previewElement || !this.tempRenderContainer) return;
-
+
// 清空临时容器
this.tempRenderContainer.empty();
-
+
// 1. 先渲染所有内容到临时容器
await this.renderAllContent(this.tempRenderContainer);
-
+
// 2. 提取内容块,转换为支持文字级分页的 IBlock 对象
const blocks = extractBlocks(this.tempRenderContainer);
+
// 3. 创建分页内容容器
const contentContainer = this.previewElement.createDiv({ cls: 'typography-content-pages' });
-
+
// 4. 创建分页引擎并应用分页
const engine = new PaginatedEngine(contentContainer);
engine.setOptions({
bookSize: settings.bookSize
});
-
+
// 执行分页
- const pageCount = engine.paginate(blocks);
-
+ engine.paginate(blocks);
+
+ // 添加页码标记,修改格式为三位数
+ engine.addPageMarkers(" {page} ");
+
// 5. 生成分页目录
- const tocPages = generatePaginatedTOC(this.previewElement, contentContainer, settings.bookSize);
-
+ const tocPages = generatePaginatedTOC(contentContainer, settings.bookSize);
+
// 6. 将目录页添加到内容前面
const tocContainer = this.previewElement.createDiv({ cls: 'typography-toc-pages' });
tocPages.forEach(tocPage => {
tocContainer.appendChild(tocPage);
});
-
- // 将目录容器移到内容容器前面
+
+ // 7.将目录容器移到内容容器前面
this.previewElement.insertBefore(tocContainer, contentContainer);
-
- // 7. 添加页码信息
- const pageInfo = this.previewElement.createDiv({ cls: 'page-info' });
- pageInfo.textContent = `共 ${pageCount + tocPages.length} 页(目录 ${tocPages.length} 页,正文 ${pageCount} 页)`;
+
+ // 8. 在分页完成后应用主题和模板
+ if (this.currentTemplate) {
+ this.currentTemplate.render(this.previewElement);
+ }
+ this.themeManager.applyTheme(this.previewElement);
}
// 渲染所有内容到指定容器
@@ -732,13 +763,19 @@ export class TypographyView {
// 底部按钮
private createButtons(container: HTMLElement) {
// 创建按钮容器
- const buttonsContainer = container.createDiv({ cls: 'typography-buttons' });
-
+ const buttonsContainer = container.createDiv({ cls: 'typography-buttons-container' });
+
+ // 第一行按钮组 - 封面相关
+ const coverButtonsGroup = buttonsContainer.createDiv({ cls: 'typography-buttons-group cover-buttons-group' });
+
// 添加封面设计开关
- this.createCoverToggle(buttonsContainer);
-
+ this.createCoverToggle(coverButtonsGroup);
+
+ // 第二行按钮组 - 操作按钮
+ const actionButtonsGroup = buttonsContainer.createDiv({ cls: 'typography-buttons-group action-buttons-group' });
+
// 应用按钮
- const applyBtn = buttonsContainer.createEl('button', {
+ const applyBtn = actionButtonsGroup.createEl('button', {
text: i18n.t('APPLY') || '应用',
cls: 'typography-btn apply-btn'
});
@@ -746,9 +783,9 @@ export class TypographyView {
// 应用排版设置
new Notice(i18n.t('TYPOGRAPHY_APPLIED') || '排版设置已应用');
});
-
+
// 导出按钮
- const exportBtn = buttonsContainer.createEl('button', {
+ const exportBtn = actionButtonsGroup.createEl('button', {
text: i18n.t('EXPORT') || '导出',
cls: 'typography-btn export-btn'
});
diff --git a/src/i18n/interfaces/modals/toolbarModals.ts b/src/i18n/interfaces/modals/toolbarModals.ts
index 3ed68f1..a7b78e7 100644
--- a/src/i18n/interfaces/modals/toolbarModals.ts
+++ b/src/i18n/interfaces/modals/toolbarModals.ts
@@ -61,6 +61,10 @@ export interface ToolbarModalTranslation {
SERIF_FONT: string;
SANS_SERIF_FONT: string;
MONOSPACE_FONT: string;
+ SONGTI_FONT: string;
+ HEITI_FONT: string;
+ KAITI_FONT: string;
+ FANGSONG_FONT: string;
APPLY: string;
TYPOGRAPHY_APPLIED: string;
EXPORT: string;
@@ -69,7 +73,6 @@ export interface ToolbarModalTranslation {
EXPORT_FAILED: string;
FORMAT: string;
CUSTOM_SIZE: string;
-
// CoverModal
DESIGN_COVER: string;
SHOW_COVER: string;
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 66403f5..4c2fb77 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -383,6 +383,10 @@ const toolbarModalTranslation: ToolbarModalTranslation = {
SERIF_FONT: 'Serif font',
SANS_SERIF_FONT: 'Sans-serif font',
MONOSPACE_FONT: 'Monospace font',
+ SONGTI_FONT: 'Songti font',
+ HEITI_FONT: 'Heiti font',
+ KAITI_FONT: 'Kaiti font',
+ FANGSONG_FONT: 'Fangsong font',
APPLY: 'Apply',
TYPOGRAPHY_APPLIED: 'Typography applied',
EXPORT: 'Export',
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index aaf5cbc..b02bff9 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -382,6 +382,10 @@ const toolbarModalTranslation: ToolbarModalTranslation = {
SERIF_FONT: '衬线字体',
SANS_SERIF_FONT: '无衬线字体',
MONOSPACE_FONT: '等宽字体',
+ SONGTI_FONT: '宋体',
+ HEITI_FONT: '黑体',
+ KAITI_FONT: '楷体',
+ FANGSONG_FONT: '仿宋',
APPLY: '应用',
TYPOGRAPHY_APPLIED: '排版设置已应用',
EXPORT: '导出',
diff --git a/src/services/ImgTemplateManager.ts b/src/services/ImgTemplateManager.ts
index dbb64c7..1f9fae4 100644
--- a/src/services/ImgTemplateManager.ts
+++ b/src/services/ImgTemplateManager.ts
@@ -86,7 +86,20 @@ class DefaultTemplate implements ImgTemplate {
constructor() {}
render(element: HTMLElement, settings?: any) {
- // 实现默认模板的渲染逻辑
+ // 添加模板特定的类名
+ element.classList.add('template-default');
+
+ // 设置基本布局
+ const contentPages = element.querySelector('.typography-content-pages');
+ if (contentPages) {
+ contentPages.classList.add('default-content-layout');
+ }
+
+ // 设置页面边距和布局
+ const pages = element.querySelectorAll('.page');
+ pages.forEach(page => {
+ page.classList.add('default-page-layout');
+ });
}
}
@@ -104,7 +117,27 @@ class NotesTemplate implements ImgTemplate {
constructor() {}
render(element: HTMLElement, settings?: any) {
- // 实现备忘录模板的渲染逻辑
+ // 添加模板特定的类名
+ element.classList.add('template-notes');
+
+ // 设置备忘录风格的页眉
+ const pages = element.querySelectorAll('.page');
+ pages.forEach((page, index) => {
+ // 添加页眉
+ const header = document.createElement('div');
+ header.className = 'notes-header';
+ header.innerHTML = `
备忘录
${new Date().toLocaleDateString()}
`;
+
+ // 将页眉插入到页面内容之前
+ if (page.firstChild) {
+ page.insertBefore(header, page.firstChild);
+ } else {
+ page.appendChild(header);
+ }
+
+ // 添加备忘录风格的线条背景
+ page.classList.add('notes-page-style');
+ });
}
}
@@ -122,7 +155,27 @@ class BookTemplate implements ImgTemplate {
constructor() {}
render(element: HTMLElement, settings?: any) {
- // 实现书籍模板的渲染逻辑
+ // 添加模板特定的类名
+ element.classList.add('template-book');
+
+ // 设置书籍风格的页面
+ const pages = element.querySelectorAll('.page');
+ pages.forEach((page, index) => {
+ // 添加书籍风格的页面样式
+ page.classList.add('book-page-style');
+
+ // 添加页脚(页码)
+ const footer = document.createElement('div');
+ footer.className = 'book-footer';
+ footer.innerHTML = `${index + 1}
`;
+ page.appendChild(footer);
+ });
+
+ // 设置封面样式
+ const coverPage = element.querySelector('.page:first-child');
+ if (coverPage) {
+ coverPage.classList.add('book-cover');
+ }
}
}
@@ -140,6 +193,38 @@ class MagazineTemplate implements ImgTemplate {
constructor() {}
render(element: HTMLElement, settings?: any) {
- // 实现杂志模板的渲染逻辑
+ // 添加模板特定的类名
+ element.classList.add('template-magazine');
+
+ // 设置杂志风格的页面
+ const pages = element.querySelectorAll('.page');
+ pages.forEach((page, index) => {
+ // 添加杂志风格的页面样式
+ page.classList.add('magazine-page-style');
+
+ // 添加页眉
+ const header = document.createElement('div');
+ header.className = 'magazine-header';
+ header.innerHTML = `专题
`;
+
+ // 将页眉插入到页面内容之前
+ if (page.firstChild) {
+ page.insertBefore(header, page.firstChild);
+ } else {
+ page.appendChild(header);
+ }
+
+ // 添加页脚
+ const footer = document.createElement('div');
+ footer.className = 'magazine-footer';
+ footer.innerHTML = `${index + 1}
`;
+ page.appendChild(footer);
+ });
+
+ // 设置封面样式
+ const coverPage = element.querySelector('.page:first-child');
+ if (coverPage) {
+ coverPage.classList.add('magazine-cover');
+ }
}
}
\ No newline at end of file
diff --git a/src/services/ThemeManager.ts b/src/services/ThemeManager.ts
index 67032fc..bc2bfe2 100644
--- a/src/services/ThemeManager.ts
+++ b/src/services/ThemeManager.ts
@@ -228,95 +228,19 @@ export class ThemeManager {
}
// 应用主题到元素
+ // 修改 applyTheme 方法,使用 CSS 类而不是内联样式
applyTheme(element: HTMLElement, themeId?: string): void {
const theme = themeId ? this.getTheme(themeId) : this.getCurrentTheme();
if (!theme) return;
- // 应用容器样式
- element.setAttribute('style', theme.styles.container);
+ // 移除所有主题相关的类
+ const themeClasses = Array.from(element.classList).filter(cls => cls.startsWith('theme-'));
+ themeClasses.forEach(cls => element.classList.remove(cls));
- // 应用标题样式
- element.querySelectorAll('h1').forEach(el => {
- el.setAttribute('style', theme.styles.title.h1);
- });
+ // 添加当前主题的类
+ element.classList.add(`theme-${theme.id}`);
- element.querySelectorAll('h2').forEach(el => {
- el.setAttribute('style', theme.styles.title.h2);
- });
-
- element.querySelectorAll('h3').forEach(el => {
- el.setAttribute('style', theme.styles.title.h3);
- });
-
- // 应用段落样式
- element.querySelectorAll('p').forEach(el => {
- el.setAttribute('style', theme.styles.paragraph);
- });
-
- // 应用引用样式
- element.querySelectorAll('blockquote').forEach(el => {
- el.setAttribute('style', theme.styles.quote);
- });
-
- // 应用列表样式
- element.querySelectorAll('ul').forEach(el => {
- el.setAttribute('style', theme.styles.list.ul);
- });
-
- element.querySelectorAll('ol').forEach(el => {
- el.setAttribute('style', theme.styles.list.ol);
- });
-
- element.querySelectorAll('li').forEach(el => {
- el.setAttribute('style', theme.styles.list.li);
- });
-
- // 应用强调样式
- element.querySelectorAll('strong').forEach(el => {
- el.setAttribute('style', theme.styles.emphasis.strong);
- });
-
- element.querySelectorAll('em').forEach(el => {
- el.setAttribute('style', theme.styles.emphasis.em);
- });
-
- element.querySelectorAll('del').forEach(el => {
- el.setAttribute('style', theme.styles.emphasis.del);
- });
-
- // 应用代码样式
- element.querySelectorAll('pre code').forEach(el => {
- el.setAttribute('style', theme.styles.code.block);
- });
-
- element.querySelectorAll('code:not(pre code)').forEach(el => {
- el.setAttribute('style', theme.styles.code.inline);
- });
-
- // 应用表格样式
- element.querySelectorAll('table').forEach(el => {
- el.setAttribute('style', theme.styles.table.container);
- });
-
- element.querySelectorAll('th').forEach(el => {
- el.setAttribute('style', theme.styles.table.header);
- });
-
- element.querySelectorAll('td').forEach(el => {
- el.setAttribute('style', theme.styles.table.cell);
- });
-
- // 应用其他元素样式
- element.querySelectorAll('hr').forEach(el => {
- el.setAttribute('style', theme.styles.hr);
- });
-
- element.querySelectorAll('img').forEach(el => {
- el.setAttribute('style', theme.styles.image);
- });
-
- element.querySelectorAll('a').forEach(el => {
- el.setAttribute('style', theme.styles.link);
- });
+ // 设置字体大小
+ element.style.fontSize = `${this.currentFontSize}px`;
}
}
\ No newline at end of file
diff --git a/src/styles/components/cover-design.css b/src/styles/components/cover-design.css
index e6fd334..be63374 100644
--- a/src/styles/components/cover-design.css
+++ b/src/styles/components/cover-design.css
@@ -99,7 +99,9 @@
display: flex;
align-items: center;
gap: 10px;
- margin-bottom: 10px;
+ margin-bottom: 0; /* 移除底部边距 */
+ justify-content: center; /* 居中显示 */
+ width: 100%;
}
.cover-toggle-label {
diff --git a/src/styles/components/paginated-engine.css b/src/styles/components/paginated-engine.css
index 58cb15e..b54df73 100644
--- a/src/styles/components/paginated-engine.css
+++ b/src/styles/components/paginated-engine.css
@@ -22,33 +22,35 @@
width: 210mm;
min-height: 297mm;
max-height: 297mm;
- padding-bottom:10px
+ padding: 15mm 10mm 10mm 10mm;
}
.book-size-a5 {
width: 148mm;
min-height: 210mm;
max-height: 210mm;
+ padding: 15mm 10mm 10mm 10mm;
}
.book-size-b5 {
width: 176mm;
min-height: 250mm;
max-height: 250mm;
+ padding: 15mm 10mm 10mm 10mm;
}
.book-size-16k {
width: 184mm;
min-height: 260mm;
max-height: 260mm;
- padding: 18mm;
+ padding: 15mm 10mm 10mm 10mm;
}
.book-size-custom {
width: 180mm;
min-height: 240mm;
max-height: 240mm;
- padding: 15mm;
+ padding: 15mm 10mm 10mm 10mm;
}
/* 目录样式 */
@@ -90,6 +92,8 @@
.toc-page-ref {
margin-left: 10px;
+ font-size: 14px;
+ font-weight: 200;
color: #666;
}
@@ -143,16 +147,32 @@
color: #666;
}
+/* 新增:页码标记内容布局 */
+.marker-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 15px;
+}
+
/* 页码标记样式 */
.page-marker {
position: absolute;
- bottom: 30px;
+ bottom: 10px;
left: 0;
right: 0;
- text-align: center;
+ text-align: right;
font-size: 12px;
color: #999;
border-top: 1px solid #eee;
padding-top: 5px;
margin-top: 10px;
+}
+
+/* 新增:BookSmith标识样式 */
+.booksmith-logo {
+ font-size: 12px;
+ color: #999;
+ font-style: italic;
+ font-weight: bold;
}
\ No newline at end of file
diff --git a/src/styles/components/typography-view.css b/src/styles/components/typography-view.css
index 0bd1744..2c825d0 100644
--- a/src/styles/components/typography-view.css
+++ b/src/styles/components/typography-view.css
@@ -47,19 +47,48 @@
overflow: hidden;
}
-/* 顶部选择器面板样式 */
+/* 顶部选择器面板样式 - 防止换行 */
.typography-top-panel {
padding: 16px;
+ max-width: 686px;
border-bottom: 1px solid var(--background-modifier-border);
background-color: var(--background-secondary-alt);
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
}
-/* 选择器容器样式 */
-.typography-selectors {
+/* 选择器行容器样式 */
+.typography-selectors-row {
display: flex;
- flex-wrap: wrap;
+ flex-wrap: nowrap; /* 防止换行 */
gap: 12px;
align-items: center;
+ justify-content: center; /* 居中排列 */
+ min-width: min-content; /* 确保内容不会被压缩 */
+ max-width: 1200px; /* 设置最大宽度 */
+ margin: 0 auto; /* 居中显示 */
+}
+
+/* 移除原有的选择器容器样式 */
+/* .typography-selectors {
+ display: flex;
+ flex-wrap: nowrap;
+ gap: 12px;
+ align-items: center;
+ justify-content: flex-start;
+ min-width: min-content;
+ max-width: 1200px;
+ margin: 0 auto;
+} */
+
+/* 自定义选择器容器样式 */
+.red-select-container {
+ position: relative;
+ flex: 0 0 auto; /* 防止压缩 */
+ min-width: 120px; /* 最小宽度 */
+ max-width: 200px; /* 最大宽度 */
+
}
/* 表单字段样式 */
@@ -126,13 +155,52 @@
font-size: 12px;
}
-/* 预览面板样式 */
+/* 预览面板样式 - 添加滚动条支持 */
.typography-preview-panel {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
+ align-items: center; /* 确保内容居中 */
+}
+
+/* 预览容器样式 - 添加居中和缩放支持 */
+.typography-preview-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-width: 100%;
+ max-width: 100%;
+}
+
+/* 封面和内容容器样式 */
+.typography-cover-container,
+.typography-content-container {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+/* 预览内容区域 - 确保缩放时的行为正确 */
+.typography-preview {
+ padding: 20px;
+ background-color: white;
+ color: black;
+ border-radius: 4px;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
+ overflow: auto;
+ margin: 0 auto;
+ min-width: 650px;
+ transform-origin: center top; /* 确保缩放以中心为基准 */
+}
+
+/* 书页样式优化 */
+.book-page {
+ margin: 0 auto; /* 确保居中 */
+ max-width: 100%; /* 限制最大宽度 */
+ box-sizing: border-box;
}
/* 预览标题 */
@@ -144,19 +212,6 @@
text-align: center;
}
-/* 预览内容区域 */
-.typography-preview {
- flex: 1;
- padding: 20px;
- background-color: white;
- color: black;
- border-radius: 4px;
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
- overflow-y: auto;
- margin: 0 auto;
- max-width: 800px;
-}
-
/* 底部按钮面板样式 */
.typography-bottom-panel {
padding: 16px;
@@ -164,13 +219,35 @@
background-color: var(--background-secondary-alt);
}
-/* 按钮样式 */
-.typography-buttons {
+/* 按钮容器样式 */
+.typography-buttons-container {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+}
+
+/* 按钮组样式 */
+.typography-buttons-group {
display: flex;
gap: 12px;
justify-content: center;
+ align-items: center;
+ width: 100%;
}
+/* 封面按钮组样式 */
+.cover-buttons-group {
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--background-modifier-border);
+}
+
+/* 操作按钮组样式 */
+.action-buttons-group {
+ padding-top: 4px;
+}
+
+/* 原有的按钮样式保持不变 */
.apply-btn, .export-btn {
padding: 10px 16px;
border-radius: 6px;
@@ -210,7 +287,7 @@
100% { background-color: var(--background-primary); }
}
-/* 保留原有的字体、字号、行高和页边距样式 */
+/* 字体样式 */
.font-default {
font-family: var(--default-font);
}
@@ -227,6 +304,22 @@
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}
+.font-songti {
+ font-family: SimSun, "宋体", STSong, "华文宋体", serif;
+}
+
+.font-heiti {
+ font-family: SimHei, "黑体", STHeiti, "华文黑体", sans-serif;
+}
+
+.font-kaiti {
+ font-family: KaiTi, "楷体", STKaiti, "华文楷体", serif;
+}
+
+.font-fangsong {
+ font-family: FangSong, "仿宋", STFangsong, "华文仿宋", serif;
+}
+
/* 字号预览样式 */
.font-size-small {
font-size: 14px;
@@ -359,13 +452,6 @@
transition: background-color 0.5s ease;
}
-/* ===== 下拉选择器 ===== */
-.red-select-container {
- position: relative;
- max-width: 200px;
- flex: 1;
-}
-
.red-select {
height: 36px;
padding: 0 12px;
@@ -450,6 +536,8 @@
/* ===== 字号调整组件 ===== */
.red-font-size-group {
+ min-width: 36px;
+ max-width: 120px;
height: 36px;
flex: 1;
display: flex;
diff --git a/src/styles/templates/templates.css b/src/styles/templates/templates.css
new file mode 100644
index 0000000..0373808
--- /dev/null
+++ b/src/styles/templates/templates.css
@@ -0,0 +1,98 @@
+/* 默认模板样式 */
+.template-default .page {
+ padding: 20px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+/* 备忘录模板样式 */
+.template-notes .page {
+ background-color: #fffdf7;
+ background-image: linear-gradient(#eee 1px, transparent 1px);
+ background-size: 100% 24px;
+ padding: 30px 20px;
+ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.15);
+}
+
+.notes-header {
+ display: flex;
+ justify-content: space-between;
+ padding-bottom: 10px;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #ddd;
+}
+
+.notes-title {
+ font-weight: bold;
+ color: #555;
+}
+
+.notes-date {
+ color: #888;
+}
+
+/* 书籍模板样式 */
+.template-book .page {
+ padding: 30px;
+ background-color: #fffef5;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+}
+
+.book-page-style {
+ position: relative;
+ min-height: 800px;
+}
+
+.book-footer {
+ position: absolute;
+ bottom: 20px;
+ width: 100%;
+ text-align: center;
+}
+
+.book-cover {
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: #f5f5f5;
+}
+
+/* 杂志模板样式 */
+.template-magazine .page {
+ padding: 20px;
+ background-color: white;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.magazine-header {
+ display: flex;
+ justify-content: flex-end;
+ padding-bottom: 10px;
+ margin-bottom: 20px;
+ border-bottom: 2px solid #333;
+}
+
+.magazine-section {
+ font-weight: bold;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.magazine-footer {
+ display: flex;
+ justify-content: center;
+ padding-top: 20px;
+ margin-top: 30px;
+ border-top: 1px solid #eee;
+}
+
+.magazine-cover {
+ background-color: #333;
+ color: white;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/src/styles/themes/themes.css b/src/styles/themes/themes.css
new file mode 100644
index 0000000..6465e44
--- /dev/null
+++ b/src/styles/themes/themes.css
@@ -0,0 +1,98 @@
+/* 默认主题 */
+.theme-default {
+ background-color: white;
+ color: #333;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+}
+
+.theme-default h1 {
+ font-size: 2em;
+ margin-bottom: 0.5em;
+ color: #222;
+}
+
+.theme-default h2 {
+ font-size: 1.5em;
+ margin-bottom: 0.5em;
+ color: #333;
+}
+
+.theme-default h3 {
+ font-size: 1.2em;
+ margin-bottom: 0.5em;
+ color: #444;
+}
+
+.theme-default p {
+ margin-bottom: 1em;
+ line-height: 1.6;
+}
+
+.theme-default blockquote {
+ border-left: 4px solid #ddd;
+ padding-left: 1em;
+ color: #666;
+ font-style: italic;
+}
+
+.theme-default ul {
+ list-style-type: disc;
+ padding-left: 2em;
+ margin-bottom: 1em;
+}
+
+.theme-default ol {
+ list-style-type: decimal;
+ padding-left: 2em;
+ margin-bottom: 1em;
+}
+
+.theme-default li {
+ margin-bottom: 0.5em;
+}
+
+.theme-default strong {
+ font-weight: bold;
+}
+
+.theme-default em {
+ font-style: italic;
+}
+
+.theme-default del {
+ text-decoration: line-through;
+}
+
+.theme-default pre code {
+ background-color: #f5f5f5;
+ padding: 1em;
+ border-radius: 4px;
+ font-family: monospace;
+ overflow-x: auto;
+ display: block;
+}
+
+.theme-default code:not(pre code) {
+ background-color: #f5f5f5;
+ padding: 0.2em 0.4em;
+ border-radius: 3px;
+ font-family: monospace;
+}
+
+/* 亮色主题 */
+.theme-light {
+ background-color: #f9f9f9;
+ color: #333;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+}
+
+/* 添加亮色主题的其他样式... */
+
+/* 暗色主题 */
+.theme-dark {
+ background-color: #222;
+ color: #eee;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+}
+
+/* 添加暗色主题的其他样式... */
\ No newline at end of file
diff --git a/src/utils/PaginatedEngine.ts b/src/utils/PaginatedEngine.ts
index 8518bc1..f77e4ed 100644
--- a/src/utils/PaginatedEngine.ts
+++ b/src/utils/PaginatedEngine.ts
@@ -47,7 +47,7 @@ export class TextBlock implements IBlock {
const head = this.content.slice(0, splitPoint).trim();
const tail = this.content.slice(splitPoint).trim();
- if (tail.length < minLength * 0.6) return null;
+ if (tail.length < minLength) return null;
this.content = head;
return new TextBlock(this.type, tail);
@@ -151,18 +151,7 @@ export class Page {
return this.blocks.pop() || null;
}
isOverflow(maxHeight: number): boolean {
- return this.element.scrollHeight > maxHeight;
- }
- setPageNumber(number: number): void {
- const pageNumberElement = this.element.querySelector('.page-number');
- if (pageNumberElement) {
- pageNumberElement.textContent = number.toString();
- (pageNumberElement as HTMLElement).style.position = 'absolute';
- (pageNumberElement as HTMLElement).style.bottom = '10px';
- (pageNumberElement as HTMLElement).style.left = '0';
- (pageNumberElement as HTMLElement).style.right = '0';
- (pageNumberElement as HTMLElement).style.textAlign = 'center';
- }
+ return this.element.scrollHeight > maxHeight + 20;
}
}
@@ -260,7 +249,6 @@ export class PaginatedEngine {
unpaginatedBlocks.shift();
}
}
- this.pages.forEach((page, idx) => page.setPageNumber(idx + 1));
return this.pages.length;
}
@@ -275,7 +263,26 @@ export class PaginatedEngine {
this.pages.forEach((page, idx) => {
const marker = document.createElement('div');
marker.classList.add('page-marker');
- marker.textContent = format.replace("{page}", (idx + 1).toString());
+
+ // 将页码格式化为三位数
+ const pageNum = (idx + 1).toString().padStart(3, '0');
+
+ // 创建包含BookSmith标识和页码的内容
+ const markerContent = document.createElement('div');
+ markerContent.classList.add('marker-content');
+
+ // 添加BookSmith标识
+ const bookSmithLogo = document.createElement('span');
+ bookSmithLogo.classList.add('booksmith-logo');
+ bookSmithLogo.textContent = 'BookSmith';
+ markerContent.appendChild(bookSmithLogo);
+
+ // 添加页码
+ const pageMarker = document.createElement('span');
+ pageMarker.textContent = format.replace("{page}", pageNum);
+ markerContent.appendChild(pageMarker);
+
+ marker.appendChild(markerContent);
page.element.appendChild(marker);
});
}
@@ -296,43 +303,30 @@ export function createNewPage(bookSize: string = 'a4'): HTMLElement {
return page;
}
-/**
- * 生成分页目录
- */
-export function generatePaginatedTOC(container: HTMLElement, contentContainer: HTMLElement, bookSize: string = 'a4'): HTMLElement[] {
- // 创建目录容器
- const tocContainer = document.createElement('div');
+export function generatePaginatedTOC(contentContainer: HTMLElement, bookSize: string = 'a4'): HTMLElement[] {
+ const pageHeightMap = {
+ 'a4': 1123, 'a5': 794, 'b5': 945, '16k': 983, 'custom': 907
+ };
+ const maxHeight = pageHeightMap[bookSize as keyof typeof pageHeightMap] || 1000;
- const tocTitle = document.createElement('h2');
- tocTitle.textContent = '目录';
- tocContainer.appendChild(tocTitle);
-
- const tocList = document.createElement('ul');
- tocList.classList.add('toc-list');
- tocContainer.appendChild(tocList);
-
- // 查找所有标题元素和它们所在的页面
const headingsWithPages: Array<{
level: number;
text: string;
id: string;
pageNumber: number;
}> = [];
- const pages = Array.from(contentContainer.querySelectorAll('.book-page'));
+ // 提取标题与对应页码
+ const pages = Array.from(contentContainer.querySelectorAll('.book-page'));
pages.forEach((page, pageIndex) => {
- const headings = Array.from(page.querySelectorAll('h1, h2, h3, h4, h5, h6'));
+ const headings = Array.from(page.querySelectorAll('h1, h2, h3'));
headings.forEach((heading, index) => {
- // 优先使用自定义属性中的实际层级,如果没有则使用标签名中的数字
const level = (heading as HTMLElement).dataset.actualLevel
? parseInt((heading as HTMLElement).dataset.actualLevel || '1')
: parseInt(heading.tagName.substring(1));
const text = heading.textContent || '';
const id = `heading-${pageIndex}-${index}`;
-
- // 设置标题ID,用于锚点链接
heading.id = id;
-
headingsWithPages.push({
level,
text,
@@ -342,133 +336,75 @@ export function generatePaginatedTOC(container: HTMLElement, contentContainer: H
});
});
- // 为每个标题创建目录项
- headingsWithPages.forEach(({ level, text, id, pageNumber }) => {
+ const tocPages: HTMLElement[] = [];
+
+ // 工具函数:创建一页目录结构
+ const createTocPage = (): {
+ page: HTMLElement;
+ list: HTMLElement;
+ } => {
+ const tocPage = document.createElement('div');
+ tocPage.classList.add('book-page', 'toc-page', `book-size-${bookSize}`);
+
+ const tocContainer = document.createElement('div');
+ const tocTitle = document.createElement('h1');
+ tocTitle.textContent = '目录';
+ tocContainer.appendChild(tocTitle);
+
+ const tocList = document.createElement('div');
+ tocList.classList.add('toc-list');
+ tocContainer.appendChild(tocList);
+
+ tocPage.appendChild(tocContainer);
+ document.body.appendChild(tocPage); // 必须临时挂载才能测量 scrollHeight
+
+ return { page: tocPage, list: tocList };
+ };
+
+ let { page: currentPage, list: currentList } = createTocPage();
+ tocPages.push(currentPage);
+
+ for (let i = 0; i < headingsWithPages.length; ) {
+ const { level, text, id, pageNumber } = headingsWithPages[i];
+
+ // 构建目录项
const listItem = document.createElement('li');
listItem.classList.add(`toc-level-${level}`);
- // 创建链接和页码容器
const itemContent = document.createElement('div');
itemContent.classList.add('toc-item-content');
- // 添加链接
const link = document.createElement('a');
link.href = `#${id}`;
link.textContent = text;
itemContent.appendChild(link);
- // 添加页码
const pageRef = document.createElement('span');
pageRef.classList.add('toc-page-ref');
- pageRef.textContent = pageNumber.toString();
+ pageRef.textContent = pageNumber.toString().padStart(3, '0');
itemContent.appendChild(pageRef);
listItem.appendChild(itemContent);
- tocList.appendChild(listItem);
- });
+ currentList.appendChild(listItem);
- // 对目录进行分页处理
- const tocPages = [];
- const pageHeight = getPageHeightByBookSize(bookSize);
+ // 判断是否溢出
+ if (currentPage.scrollHeight > maxHeight) {
+ currentList.removeChild(listItem); // 撤回当前项
- // 创建第一个目录页
- let currentTocPage = document.createElement('div');
- currentTocPage.classList.add('book-page', 'toc-page', `book-size-${bookSize}`);
- currentTocPage.appendChild(tocContainer.cloneNode(true));
- tocPages.push(currentTocPage);
+ // 新建一页
+ const next = createTocPage();
+ currentPage = next.page;
+ currentList = next.list;
+ tocPages.push(currentPage);
- // 检查目录是否需要分页
- if (currentTocPage.scrollHeight > pageHeight) {
- // 重新创建目录,按项目分页
- tocPages.length = 0;
- currentTocPage = document.createElement('div');
- currentTocPage.classList.add('book-page', 'toc-page', `book-size-${bookSize}`);
+ continue; // 不推进 i,下次重新尝试这一条
+ }
- const newTocContainer = document.createElement('div');
- newTocContainer.classList.add('typography-toc');
- newTocContainer.classList.add(`book-size-${bookSize}`);
-
- const newTocTitle = document.createElement('h2');
- newTocTitle.textContent = '目录';
- newTocContainer.appendChild(newTocTitle);
-
- const newTocList = document.createElement('ul');
- newTocList.classList.add('toc-list');
- newTocContainer.appendChild(newTocList);
-
- currentTocPage.appendChild(newTocContainer);
- tocPages.push(currentTocPage);
-
- // 逐项添加目录项,检查是否需要创建新页
- headingsWithPages.forEach(({ level, text, id, pageNumber }) => {
- const listItem = document.createElement('li');
- listItem.classList.add(`toc-level-${level}`);
-
- // 创建链接和页码容器
- const itemContent = document.createElement('div');
- itemContent.classList.add('toc-item-content');
-
- // 添加链接
- const link = document.createElement('a');
- link.href = `#${id}`;
- link.textContent = text;
- itemContent.appendChild(link);
-
- // 添加页码
- const pageRef = document.createElement('span');
- pageRef.classList.add('toc-page-ref');
- pageRef.textContent = pageNumber.toString();
- itemContent.appendChild(pageRef);
-
- listItem.appendChild(itemContent);
-
- // 添加到当前目录页
- const currentTocList = currentTocPage.querySelector('.toc-list');
- if (currentTocList) {
- currentTocList.appendChild(listItem);
-
- // 检查是否溢出
- if (currentTocPage.scrollHeight > pageHeight) {
- // 移除刚添加的项
- currentTocList.removeChild(listItem);
-
- // 创建新的目录页
- const nextTocPage = document.createElement('div');
- nextTocPage.classList.add('book-page', 'toc-page', `book-size-${bookSize}`);
-
- const nextTocContainer = document.createElement('div');
- nextTocContainer.classList.add('typography-toc');
- nextTocContainer.classList.add(`book-size-${bookSize}`);
-
- const nextTocTitle = document.createElement('h2');
- nextTocTitle.textContent = '目录';
- nextTocContainer.appendChild(nextTocTitle);
-
- const nextTocList = document.createElement('ul');
- nextTocList.classList.add('toc-list');
- nextTocContainer.appendChild(nextTocList);
-
- nextTocPage.appendChild(nextTocContainer);
- nextTocList.appendChild(listItem);
-
- tocPages.push(nextTocPage);
- currentTocPage = nextTocPage;
- }
- }
- });
+ i++; // 成功添加才推进
}
+ // 清除挂载
+ tocPages.forEach(p => document.body.contains(p) && p.remove());
+
return tocPages;
-}
-
-// 辅助函数:根据开本大小获取页面高度
-function getPageHeightByBookSize(bookSize: string): number {
- switch (bookSize) {
- case 'a4': return 1000;
- case 'a5': return 800;
- case 'b5': return 900;
- case '16k': return 850;
- case 'custom': return 800;
- default: return 800;
- }
}
\ No newline at end of file