@file-viewer/renderer-epub 2.1.1 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/epub.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { FileViewerRenderedInstance } from '@file-viewer/core';
2
- export default function renderEpub(buffer: ArrayBuffer, target: HTMLDivElement): Promise<FileViewerRenderedInstance>;
1
+ import { type FileRenderContext, type FileViewerRenderedInstance } from '@file-viewer/core';
2
+ export default function renderEpub(buffer: ArrayBuffer, target: HTMLDivElement, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
package/dist/epub.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createFileViewerTranslator, } from '@file-viewer/core';
1
2
  const epubStyle = `
2
3
  .epub-viewer{width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;background:#eef1f4;color:#172033;box-sizing:border-box}
3
4
  .epub-viewer *{box-sizing:border-box}
@@ -60,15 +61,15 @@ const normalizeLabel = (value, fallback) => {
60
61
  }
61
62
  return fallback;
62
63
  };
63
- const flattenToc = (items, depth = 0) => {
64
+ const flattenToc = (items, fallbackLabel, depth = 0) => {
64
65
  if (!Array.isArray(items)) {
65
66
  return [];
66
67
  }
67
68
  return items.flatMap((item, index) => {
68
69
  const node = item;
69
70
  const href = typeof node.href === 'string' ? node.href : '';
70
- const label = normalizeLabel(node.label || node.title, `章节 ${index + 1}`);
71
- const subitems = flattenToc(node.subitems || node.children, depth + 1);
71
+ const label = normalizeLabel(node.label || node.title, fallbackLabel(index + 1));
72
+ const subitems = flattenToc(node.subitems || node.children, fallbackLabel, depth + 1);
72
73
  if (!href) {
73
74
  return subitems;
74
75
  }
@@ -96,13 +97,14 @@ const pickInitialHref = (items) => {
96
97
  });
97
98
  return (readable === null || readable === void 0 ? void 0 : readable.href) || ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.href);
98
99
  };
99
- export default async function renderEpub(buffer, target) {
100
+ export default async function renderEpub(buffer, target, context) {
101
+ const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
100
102
  let book;
101
103
  let rendition;
102
104
  let disposed = false;
103
105
  let tocOpen = true;
104
106
  let status = 'loading';
105
- let title = 'EPUB 电子书';
107
+ let title = t('epub.title');
106
108
  let author = '';
107
109
  let tocItems = [];
108
110
  let currentHref = '';
@@ -115,16 +117,16 @@ export default async function renderEpub(buffer, target) {
115
117
  const toolbar = createElement('div', 'epub-toolbar');
116
118
  const tocButton = createElement('button', 'epub-icon-button');
117
119
  tocButton.type = 'button';
118
- tocButton.title = '目录';
120
+ tocButton.title = t('ebook.toc');
119
121
  tocButton.append(createElement('span'));
120
122
  const titleRoot = createElement('div', 'epub-title');
121
123
  const titleText = createElement('strong', undefined, title);
122
- const subtitleText = createElement('span', undefined, '阅读中');
124
+ const subtitleText = createElement('span', undefined, t('ebook.reading'));
123
125
  titleRoot.append(titleText, subtitleText);
124
126
  const actions = createElement('div', 'epub-actions');
125
- const prevButton = createElement('button', 'epub-button', '上一页');
126
- const progressText = createElement('span', 'epub-progress', '阅读中');
127
- const nextButton = createElement('button', 'epub-button', '下一页');
127
+ const prevButton = createElement('button', 'epub-button', t('epub.previousPage'));
128
+ const progressText = createElement('span', 'epub-progress', t('ebook.reading'));
129
+ const nextButton = createElement('button', 'epub-button', t('epub.nextPage'));
128
130
  prevButton.type = 'button';
129
131
  nextButton.type = 'button';
130
132
  actions.append(prevButton, progressText, nextButton);
@@ -132,13 +134,13 @@ export default async function renderEpub(buffer, target) {
132
134
  const body = createElement('div', 'epub-body');
133
135
  const toc = createElement('aside', 'epub-toc');
134
136
  const tocHead = createElement('div', 'epub-toc-head');
135
- const tocCount = createElement('span', undefined, '0 项');
136
- tocHead.append(createElement('strong', undefined, '目录'), tocCount);
137
+ const tocCount = createElement('span', undefined, t('ebook.itemCount', { count: 0 }));
138
+ tocHead.append(createElement('strong', undefined, t('ebook.toc')), tocCount);
137
139
  const tocList = createElement('div', 'epub-toc-list');
138
140
  toc.append(tocHead, tocList);
139
141
  const stageWrap = createElement('main', 'epub-stage-wrap');
140
142
  const stage = createElement('div', 'epub-stage');
141
- const state = createElement('div', 'epub-state', '正在解析 EPUB...');
143
+ const state = createElement('div', 'epub-state', t('epub.loading'));
142
144
  stageWrap.append(stage, state);
143
145
  body.append(toc, stageWrap);
144
146
  root.append(toolbar, body);
@@ -162,7 +164,7 @@ export default async function renderEpub(buffer, target) {
162
164
  if (typeof progress === 'number') {
163
165
  return `${progress}%`;
164
166
  }
165
- return currentChapter() || '阅读中';
167
+ return currentChapter() || t('ebook.reading');
166
168
  };
167
169
  const syncUi = () => {
168
170
  root.classList.toggle('epub-viewer--toc-hidden', !tocOpen);
@@ -175,7 +177,7 @@ export default async function renderEpub(buffer, target) {
175
177
  nextButton.disabled = status !== 'ready' || atEnd;
176
178
  state.hidden = status === 'ready';
177
179
  state.classList.toggle('error', status === 'error');
178
- tocCount.textContent = `${tocItems.length} 项`;
180
+ tocCount.textContent = t('ebook.itemCount', { count: tocItems.length });
179
181
  Array.from(tocList.querySelectorAll('.epub-toc-item')).forEach(button => {
180
182
  button.classList.toggle('active', button.dataset.href === currentHref);
181
183
  });
@@ -238,7 +240,7 @@ export default async function renderEpub(buffer, target) {
238
240
  };
239
241
  const openBook = async () => {
240
242
  status = 'loading';
241
- state.textContent = '正在解析 EPUB...';
243
+ state.textContent = t('epub.loading');
242
244
  syncUi();
243
245
  try {
244
246
  const { default: ePub } = await import('epubjs');
@@ -279,11 +281,11 @@ export default async function renderEpub(buffer, target) {
279
281
  title = normalizeLabel(metadata === null || metadata === void 0 ? void 0 : metadata.title, title);
280
282
  author = normalizeLabel(metadata === null || metadata === void 0 ? void 0 : metadata.creator, '');
281
283
  const navigation = await book.loaded.navigation.catch(() => undefined);
282
- tocItems = flattenToc(navigation === null || navigation === void 0 ? void 0 : navigation.toc);
284
+ tocItems = flattenToc(navigation === null || navigation === void 0 ? void 0 : navigation.toc, index => t('epub.chapterFallback', { index }));
283
285
  renderToc();
284
286
  await rendition.display(pickInitialHref(tocItems));
285
287
  if (!await waitForReadableFrame()) {
286
- throw new Error('EPUB 正文渲染未完成,请刷新后重试');
288
+ throw new Error(t('epub.renderIncomplete'));
287
289
  }
288
290
  if (disposed) {
289
291
  return;
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ if (!epubDefinition || !umdDefinition) {
6
6
  }
7
7
  export const ebookRendererDefinition = epubDefinition;
8
8
  export const umdRendererDefinition = umdDefinition;
9
- export const renderFileViewerEpub = (buffer, target) => import('./epub.js').then(({ default: renderEpub }) => renderEpub(buffer, target));
10
- export const renderFileViewerUmd = (buffer, target) => import('./umd.js').then(({ default: renderUmd }) => renderUmd(buffer, target));
9
+ export const renderFileViewerEpub = (buffer, target, _type, context) => import('./epub.js').then(({ default: renderEpub }) => renderEpub(buffer, target, context));
10
+ export const renderFileViewerUmd = (buffer, target, _type, context) => import('./umd.js').then(({ default: renderUmd }) => renderUmd(buffer, target, context));
11
11
  export const ebookRenderer = {
12
12
  id: 'file-viewer-renderer-epub',
13
13
  label: 'Flyfish File Viewer ebook renderer',
package/dist/umd.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { FileViewerRenderedInstance } from '@file-viewer/core';
2
- export default function renderUmd(buffer: ArrayBuffer, target: HTMLDivElement): Promise<FileViewerRenderedInstance>;
1
+ import type { FileRenderContext, FileViewerRenderedInstance } from '@file-viewer/core';
2
+ export default function renderUmd(buffer: ArrayBuffer, target: HTMLDivElement, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
package/dist/umd.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createFileViewerTranslator } from '@file-viewer/core';
1
2
  import { parseUmdBook, } from './umd/parser';
2
3
  const umdStyle = `
3
4
  .umd-viewer{width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;background:#eef1f4;color:#172033;box-sizing:border-box}
@@ -58,22 +59,55 @@ const createButton = (label, className) => {
58
59
  button.textContent = label;
59
60
  return button;
60
61
  };
61
- const buildMetaLine = (book, chapter) => {
62
+ const buildMetaLine = (book, readingText, chapter) => {
62
63
  if (!book) {
63
- return (chapter === null || chapter === void 0 ? void 0 : chapter.title) || '阅读中';
64
+ return (chapter === null || chapter === void 0 ? void 0 : chapter.title) || readingText;
64
65
  }
65
66
  return [
66
67
  book.author,
67
68
  book.category,
68
69
  book.publishedAt,
69
- ].filter(Boolean).join(' / ') || (chapter === null || chapter === void 0 ? void 0 : chapter.title) || '阅读中';
70
+ ].filter(Boolean).join(' / ') || (chapter === null || chapter === void 0 ? void 0 : chapter.title) || readingText;
71
+ };
72
+ const createUmdTextLocalizer = (t) => {
73
+ const localize = (value) => {
74
+ if (value === 'UMD 电子书') {
75
+ return t('umd.title');
76
+ }
77
+ const chapterMatch = /^章节\s+(\d+)$/.exec(value);
78
+ if (chapterMatch) {
79
+ return t('umd.chapterFallback', { index: chapterMatch[1] });
80
+ }
81
+ const galleryMatch = /^图集\s+(\d+)$/.exec(value);
82
+ if (galleryMatch) {
83
+ return t('umd.galleryFallback', { index: galleryMatch[1] });
84
+ }
85
+ if (value === 'UMD 正文长度小于声明长度,文件可能不完整') {
86
+ return t('umd.warningBodyTooShort');
87
+ }
88
+ if (value === 'UMD 文件结构不完整,读取时遇到意外结尾') {
89
+ return t('umd.error.unexpectedEnd');
90
+ }
91
+ if (value === '不是有效的 UMD 电子书文件') {
92
+ return t('umd.error.invalidFile');
93
+ }
94
+ return value;
95
+ };
96
+ return {
97
+ localize,
98
+ localizeJoinedWarnings(warnings) {
99
+ return warnings.filter(Boolean).map(localize).join(';');
100
+ },
101
+ };
70
102
  };
71
103
  const copyImageBytes = (image) => {
72
104
  const copy = new Uint8Array(image.bytes.byteLength);
73
105
  copy.set(image.bytes);
74
106
  return copy;
75
107
  };
76
- export default async function renderUmd(buffer, target) {
108
+ export default async function renderUmd(buffer, target, context) {
109
+ const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
110
+ const { localize, localizeJoinedWarnings } = createUmdTextLocalizer(t);
77
111
  const objectUrls = new Map();
78
112
  let book = null;
79
113
  let activeIndex = 0;
@@ -87,19 +121,19 @@ export default async function renderUmd(buffer, target) {
87
121
  const tocButton = document.createElement('button');
88
122
  tocButton.type = 'button';
89
123
  tocButton.className = 'umd-icon-button active';
90
- tocButton.title = '目录';
124
+ tocButton.title = t('ebook.toc');
91
125
  tocButton.appendChild(document.createElement('span'));
92
126
  const title = document.createElement('div');
93
127
  title.className = 'umd-title';
94
- const titleText = appendText(title, 'strong', 'UMD 电子书');
95
- const metaText = appendText(title, 'span', '阅读中');
128
+ const titleText = appendText(title, 'strong', t('umd.title'));
129
+ const metaText = appendText(title, 'span', t('ebook.reading'));
96
130
  const actions = document.createElement('div');
97
131
  actions.className = 'umd-actions';
98
- const prevButton = createButton('上一章', 'umd-button');
132
+ const prevButton = createButton(t('umd.previousChapter'), 'umd-button');
99
133
  const progress = document.createElement('span');
100
134
  progress.className = 'umd-progress';
101
135
  progress.textContent = '0/0';
102
- const nextButton = createButton('下一章', 'umd-button');
136
+ const nextButton = createButton(t('umd.nextChapter'), 'umd-button');
103
137
  actions.append(prevButton, progress, nextButton);
104
138
  toolbar.append(tocButton, title, actions);
105
139
  const body = document.createElement('div');
@@ -108,8 +142,8 @@ export default async function renderUmd(buffer, target) {
108
142
  toc.className = 'umd-toc';
109
143
  const tocHead = document.createElement('div');
110
144
  tocHead.className = 'umd-toc-head';
111
- appendText(tocHead, 'strong', '目录');
112
- const tocCount = appendText(tocHead, 'span', '0 项');
145
+ appendText(tocHead, 'strong', t('ebook.toc'));
146
+ const tocCount = appendText(tocHead, 'span', t('ebook.itemCount', { count: 0 }));
113
147
  const tocList = document.createElement('div');
114
148
  tocList.className = 'umd-toc-list';
115
149
  toc.append(tocHead, tocList);
@@ -119,7 +153,7 @@ export default async function renderUmd(buffer, target) {
119
153
  stage.className = 'umd-stage';
120
154
  const state = document.createElement('div');
121
155
  state.className = 'umd-state';
122
- state.textContent = '正在解析 UMD...';
156
+ state.textContent = t('umd.loading');
123
157
  stageWrap.append(stage, state);
124
158
  body.append(toc, stageWrap);
125
159
  root.append(toolbar, body);
@@ -144,14 +178,14 @@ export default async function renderUmd(buffer, target) {
144
178
  const updateChrome = () => {
145
179
  const chapter = getCurrentChapter();
146
180
  const total = (book === null || book === void 0 ? void 0 : book.chapters.length) || 0;
147
- titleText.textContent = (book === null || book === void 0 ? void 0 : book.title) || 'UMD 电子书';
148
- metaText.textContent = buildMetaLine(book, chapter);
181
+ titleText.textContent = (book === null || book === void 0 ? void 0 : book.title) ? localize(book.title) : t('umd.title');
182
+ metaText.textContent = localize(buildMetaLine(book, t('ebook.reading'), chapter));
149
183
  progress.textContent = total ? `${activeIndex + 1}/${total}` : '0/0';
150
184
  prevButton.disabled = !book || activeIndex <= 0;
151
185
  nextButton.disabled = !book || !total || activeIndex >= total - 1;
152
186
  tocButton.classList.toggle('active', tocOpen);
153
187
  root.classList.toggle('umd-viewer--toc-hidden', !tocOpen);
154
- tocCount.textContent = `${total} 项`;
188
+ tocCount.textContent = t('ebook.itemCount', { count: total });
155
189
  };
156
190
  const scrollToTop = () => {
157
191
  stage.scrollTo({ top: 0 });
@@ -159,7 +193,7 @@ export default async function renderUmd(buffer, target) {
159
193
  const renderToc = () => {
160
194
  tocList.replaceChildren();
161
195
  book === null || book === void 0 ? void 0 : book.chapters.forEach((chapter, index) => {
162
- const item = createButton(chapter.title, 'umd-toc-item');
196
+ const item = createButton(localize(chapter.title), 'umd-toc-item');
163
197
  item.classList.toggle('active', index === activeIndex);
164
198
  item.addEventListener('click', () => {
165
199
  activeIndex = index;
@@ -175,7 +209,7 @@ export default async function renderUmd(buffer, target) {
175
209
  return;
176
210
  }
177
211
  const coverUrl = getImageUrl(book.cover);
178
- const metaLine = buildMetaLine(book);
212
+ const metaLine = buildMetaLine(book, t('ebook.reading'));
179
213
  if (!coverUrl && !metaLine) {
180
214
  return;
181
215
  }
@@ -184,11 +218,11 @@ export default async function renderUmd(buffer, target) {
184
218
  if (coverUrl) {
185
219
  const image = document.createElement('img');
186
220
  image.src = coverUrl;
187
- image.alt = book.title;
221
+ image.alt = localize(book.title);
188
222
  head.appendChild(image);
189
223
  }
190
224
  const info = document.createElement('div');
191
- appendText(info, 'h1', book.title);
225
+ appendText(info, 'h1', localize(book.title));
192
226
  if (metaLine) {
193
227
  appendText(info, 'p', metaLine);
194
228
  }
@@ -203,7 +237,7 @@ export default async function renderUmd(buffer, target) {
203
237
  const section = document.createElement('section');
204
238
  section.className = 'umd-chapter';
205
239
  section.dataset.viewerAnchorId = chapter.id;
206
- appendText(section, 'h2', chapter.title);
240
+ appendText(section, 'h2', localize(chapter.title));
207
241
  if (chapter.images.length) {
208
242
  const images = document.createElement('div');
209
243
  images.className = 'umd-image-list';
@@ -211,7 +245,7 @@ export default async function renderUmd(buffer, target) {
211
245
  const figure = document.createElement('figure');
212
246
  const img = document.createElement('img');
213
247
  img.src = getImageUrl(image);
214
- img.alt = chapter.title;
248
+ img.alt = localize(chapter.title);
215
249
  figure.appendChild(img);
216
250
  images.appendChild(figure);
217
251
  });
@@ -221,7 +255,7 @@ export default async function renderUmd(buffer, target) {
221
255
  appendText(section, 'div', chapter.content, 'umd-text');
222
256
  }
223
257
  else if (!chapter.images.length) {
224
- appendText(section, 'div', '未解析到正文内容', 'umd-empty');
258
+ appendText(section, 'div', t('umd.emptyContent'), 'umd-empty');
225
259
  }
226
260
  return section;
227
261
  };
@@ -235,7 +269,7 @@ export default async function renderUmd(buffer, target) {
235
269
  if (chapter) {
236
270
  stage.appendChild(renderChapter(chapter));
237
271
  }
238
- const warningText = (book === null || book === void 0 ? void 0 : book.warnings.filter(Boolean).join(';')) || '';
272
+ const warningText = book ? localizeJoinedWarnings(book.warnings) : '';
239
273
  if (warningText) {
240
274
  appendText(stage, 'div', warningText, 'umd-warning');
241
275
  }
@@ -280,7 +314,7 @@ export default async function renderUmd(buffer, target) {
280
314
  catch (error) {
281
315
  if (!disposed) {
282
316
  console.error(error);
283
- renderError(error instanceof Error ? error.message : String(error));
317
+ renderError(error instanceof Error ? localize(error.message) : String(error));
284
318
  }
285
319
  }
286
320
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-epub",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone EPUB and UMD reader renderer plugin for Flyfish File Viewer powered by epubjs and pako.",
@@ -53,7 +53,7 @@
53
53
  "LICENSE"
54
54
  ],
55
55
  "dependencies": {
56
- "@file-viewer/core": "^2.1.1",
56
+ "@file-viewer/core": "^2.1.3",
57
57
  "epubjs": "^0.3.93",
58
58
  "pako": "^2.1.0"
59
59
  },