feat: add PDF export functionality (#3264)
* feature add: export article as pdf (issue: 1314) * reverted pnpm-lock.yaml, deleted unwanted code, ensured some checks * chore: update icon * update changelog --------- Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
This commit is contained in:
parent
831028a071
commit
1a9f273606
|
|
@ -3,6 +3,7 @@
|
|||
## New Features
|
||||
|
||||
- New Action Language setting
|
||||
- Export entry content to a PDF file
|
||||
|
||||
## Improvements
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "رابط RSS",
|
||||
"discover.select_placeholder": "اختر",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل في الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل في الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_obsidian": "فشل في الحفظ إلى Obsidian.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "رابط RSS",
|
||||
"discover.select_placeholder": "اختر",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل في الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل في الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_obsidian": "فشل في الحفظ إلى Obsidian.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "رابط RSS",
|
||||
"discover.select_placeholder": "اختر",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل في الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل في الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_obsidian": "فشل في الحفظ إلى Obsidian",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "رابط RSS",
|
||||
"discover.select_placeholder": "اختر",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_obsidian": "فشل الحفظ في Obsidian",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "عنوان URL لخلاصة RSS",
|
||||
"discover.select_placeholder": "اختيار",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل في الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل في الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_readwise": "فشل في الحفظ إلى Readwise.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "رابط RSS",
|
||||
"discover.select_placeholder": "اختر",
|
||||
"entry_actions.copy_link": "نسخ الرابط",
|
||||
"entry_actions.exported_notify": "تم تصدير المقال كملف PDF.",
|
||||
"entry_actions.export_as_pdf": "تصدير كملف PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "فشل في الحفظ إلى Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "فشل في الحفظ إلى Instapaper.",
|
||||
"entry_actions.failed_to_save_to_readwise": "فشل في الحفظ إلى Readwise.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "RSS-URL",
|
||||
"discover.select_placeholder": "Auswählen",
|
||||
"entry_actions.copy_link": "Link kopieren",
|
||||
"entry_actions.exported_notify": "Artikel als PDF exportiert.",
|
||||
"entry_actions.export_as_pdf": "Als PDF exportieren",
|
||||
"entry_actions.failed_to_save_to_eagle": "Speichern in Eagle fehlgeschlagen.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Speichern in Instapaper fehlgeschlagen.",
|
||||
"entry_actions.failed_to_save_to_obsidian": "Speichern in Obsidian fehlgeschlagen",
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@
|
|||
"entry_actions.image_gallery": "Image Gallery",
|
||||
"entry_actions.copied_notify": "{{which}} copied to clipboard.",
|
||||
"entry_actions.copy_link": "Copy Link",
|
||||
"entry_actions.exported_notify": "Exported article as PDF.",
|
||||
"entry_actions.export_as_pdf": "Export as PDF",
|
||||
"entry_actions.copy_title": "Copy Title",
|
||||
"entry_actions.delete": "Delete",
|
||||
"entry_actions.deleted": "Deleted.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "RSS URL",
|
||||
"discover.select_placeholder": "Seleccionar",
|
||||
"entry_actions.copy_link": "Copiar enlace",
|
||||
"entry_actions.exported_notify": "Artículo exportado como PDF.",
|
||||
"entry_actions.export_as_pdf": "Exportar como PDF",
|
||||
"entry_actions.failed_to_save_to_obsidian": "Error al guardar en Obsidian",
|
||||
"entry_actions.mark_as_read": "Marcar como leído",
|
||||
"entry_actions.mark_as_unread": "Marcar como no leído",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "RSS-URL",
|
||||
"discover.select_placeholder": "Valitse",
|
||||
"entry_actions.copy_link": "Kopioi linkki",
|
||||
"entry_actions.exported_notify": "Artikkeli vietiin PDF-muodossa.",
|
||||
"entry_actions.export_as_pdf": "Vie PDF:nä",
|
||||
"entry_actions.failed_to_save_to_eagle": "Tallennus Eagleen epäonnistui.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Tallennus Instapaperiin epäonnistui.",
|
||||
"entry_actions.failed_to_save_to_readwise": "Tallennus Readwiseen epäonnistui.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "URL du RSS",
|
||||
"discover.select_placeholder": "Sélectionner",
|
||||
"entry_actions.copy_link": "Copier le lien",
|
||||
"entry_actions.exported_notify": "Article exporté en PDF.",
|
||||
"entry_actions.export_as_pdf": "Exporter en PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "Échec de l'enregistrement sur Eagle.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Échec de l'enregistrement sur Instapaper.",
|
||||
"entry_actions.failed_to_save_to_readwise": "Échec de l'enregistrement sur Readwise.",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "URL RSS",
|
||||
"discover.select_placeholder": "Seleziona",
|
||||
"entry_actions.copy_link": "Copia link",
|
||||
"entry_actions.exported_notify": "Articolo esportato come PDF.",
|
||||
"entry_actions.export_as_pdf": "Esporta come PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "Salvataggio su Eagle non riuscito.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Salvataggio su Instapaper non riuscito.",
|
||||
"entry_actions.failed_to_save_to_readwise": "Salvataggio su Readwise non riuscito.",
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@
|
|||
"discover.target.lists": "リスト",
|
||||
"entry_actions.copied_notify": "{{which}} をクリップボードにコピーしました。",
|
||||
"entry_actions.copy_link": "リンクをコピー",
|
||||
"entry_actions.exported_notify": "記事をPDFとしてエクスポートしました。",
|
||||
"entry_actions.export_as_pdf": "PDFとしてエクスポート",
|
||||
"entry_actions.copy_title": "タイトルをコピー",
|
||||
"entry_actions.delete": "削除",
|
||||
"entry_actions.deleted": "削除済み",
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@
|
|||
"discover.target.lists": "목록",
|
||||
"entry_actions.copied_notify": "{{which}}이(가) 클립보드에 복사되었습니다",
|
||||
"entry_actions.copy_link": "링크 복사",
|
||||
"entry_actions.exported_notify": "PDF로 기사 내보내기 완료.",
|
||||
"entry_actions.export_as_pdf": "PDF로 내보내기",
|
||||
"entry_actions.copy_title": "제목 복사",
|
||||
"entry_actions.delete": "삭제",
|
||||
"entry_actions.deleted": "삭제됨",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"discover.rss_url": "RSS URL",
|
||||
"discover.select_placeholder": "選択",
|
||||
"entry_actions.copy_link": "リンクをコピー",
|
||||
"entry_actions.exported_notify": "Artigo exportado como PDF.",
|
||||
"entry_actions.export_as_pdf": "Exportar como PDF",
|
||||
"entry_actions.failed_to_save_to_eagle": "Eagle への保存に失敗しました。",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Instapaper への保存に失敗しました。",
|
||||
"entry_actions.failed_to_save_to_readwise": "Readwise への保存に失敗しました。",
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@
|
|||
"discover.target.lists": "Списки",
|
||||
"entry_actions.copied_notify": "{{which}} скопировано в буфер обмена.",
|
||||
"entry_actions.copy_link": "Копировать ссылку",
|
||||
"entry_actions.exported_notify": "Статья экспортирована в PDF.",
|
||||
"entry_actions.export_as_pdf": "Экспортировать в PDF",
|
||||
"entry_actions.copy_title": "Копировать заголовок",
|
||||
"entry_actions.delete": "Удалить",
|
||||
"entry_actions.deleted": "Удалено.",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
"discover.target.label": "Arama hedefi",
|
||||
"discover.target.lists": "Listeler",
|
||||
"entry_actions.copy_link": "Bağlantıyı kopyala",
|
||||
"entry_actions.exported_notify": "Makale PDF olarak dışa aktarıldı.",
|
||||
"entry_actions.export_as_pdf": "PDF olarak dışa aktar",
|
||||
"entry_actions.failed_to_save_to_eagle": "Eagle'a kaydetme başarısız oldu.",
|
||||
"entry_actions.failed_to_save_to_instapaper": "Instapaper'a kaydetme başarısız oldu.",
|
||||
"entry_actions.failed_to_save_to_readwise": "Readwise'a kaydetme başarısız oldu.",
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@
|
|||
"entry_actions.image_gallery": "图片库",
|
||||
"entry_actions.copied_notify": "{{which}}已复制到剪贴板。",
|
||||
"entry_actions.copy_link": "复制链接",
|
||||
"entry_actions.exported_notify": "文章已导出为 PDF。",
|
||||
"entry_actions.export_as_pdf": "导出为 PDF",
|
||||
"entry_actions.copy_title": "复制标题",
|
||||
"entry_actions.delete": "删除",
|
||||
"entry_actions.deleted": "已删除",
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@
|
|||
"entry_actions.image_gallery": "圖片庫",
|
||||
"entry_actions.copied_notify": "{{which}}已複製到剪貼簿",
|
||||
"entry_actions.copy_link": "複製連結",
|
||||
"entry_actions.exported_notify": "文章已匯出為 PDF。",
|
||||
"entry_actions.export_as_pdf": "匯出為 PDF",
|
||||
"entry_actions.copy_title": "複製標題",
|
||||
"entry_actions.delete": "刪除",
|
||||
"entry_actions.deleted": "已刪除",
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@
|
|||
"discover.target.lists": "列表",
|
||||
"entry_actions.copied_notify": "{{which}}已複製到剪貼簿",
|
||||
"entry_actions.copy_link": "複製連結",
|
||||
"entry_actions.exported_notify": "文章已匯出為 PDF。",
|
||||
"entry_actions.export_as_pdf": "匯出為 PDF",
|
||||
"entry_actions.copy_title": "複製標題",
|
||||
"entry_actions.delete": "刪除",
|
||||
"entry_actions.deleted": "已刪除。",
|
||||
|
|
|
|||
|
|
@ -146,6 +146,10 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
|
|||
hide: !entry?.entries.url,
|
||||
shortcut: shortcuts.entry.copyLink.key,
|
||||
},
|
||||
{
|
||||
id: COMMAND_ID.entry.exportAsPDF,
|
||||
onClick: runCmdFn(COMMAND_ID.entry.exportAsPDF, [{ entryId }]),
|
||||
},
|
||||
{
|
||||
id: COMMAND_ID.entry.openInBrowser,
|
||||
onClick: runCmdFn(COMMAND_ID.entry.openInBrowser, [{ entryId }]),
|
||||
|
|
|
|||
|
|
@ -175,6 +175,25 @@ export const useRegisterEntryCommands = () => {
|
|||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
id: COMMAND_ID.entry.exportAsPDF,
|
||||
label: t("entry_actions.export_as_pdf"),
|
||||
icon: <i className="i-mgc-pdf-cute-re" />,
|
||||
run: ({ entryId }) => {
|
||||
const entry = useEntryStore.getState().flatMapEntries[entryId]
|
||||
|
||||
if (!entry) {
|
||||
toast.error("Failed to export as pdf: entry is not available", { duration: 3000 })
|
||||
return
|
||||
}
|
||||
|
||||
window.print()
|
||||
|
||||
toast(t("entry_actions.exported_notify"), {
|
||||
duration: 1000,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
id: COMMAND_ID.entry.copyTitle,
|
||||
label: t("entry_actions.copy_title"),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export const COMMAND_ID = {
|
|||
read: "entry:read",
|
||||
toggleAISummary: "entry:toggle-ai-summary",
|
||||
toggleAITranslation: "entry:toggle-ai-translation",
|
||||
exportAsPDF: "entry:export-as-pdf",
|
||||
},
|
||||
integration: {
|
||||
saveToEagle: "integration:save-to-eagle",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ export type CopyLinkCommand = Command<{
|
|||
fn: (data: { entryId: string }) => void
|
||||
}>
|
||||
|
||||
export type ExportAsPDFCommand = Command<{
|
||||
id: typeof COMMAND_ID.entry.exportAsPDF
|
||||
fn: (data: { entryId: string }) => void
|
||||
}>
|
||||
|
||||
export type CopyTitleCommand = Command<{
|
||||
id: typeof COMMAND_ID.entry.copyTitle
|
||||
fn: (data: { entryId: string }) => void
|
||||
|
|
@ -65,6 +70,7 @@ export type EntryCommand =
|
|||
| StarCommand
|
||||
| DeleteCommand
|
||||
| CopyLinkCommand
|
||||
| ExportAsPDFCommand
|
||||
| CopyTitleCommand
|
||||
| OpenInBrowserCommand
|
||||
| ViewSourceContentCommand
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export const DEFAULT_ACTION_ORDER: ToolbarActionOrder = {
|
|||
[
|
||||
COMMAND_ID.entry.copyLink,
|
||||
COMMAND_ID.entry.openInBrowser,
|
||||
COMMAND_ID.entry.exportAsPDF,
|
||||
COMMAND_ID.entry.read,
|
||||
] as string[]
|
||||
).includes(id),
|
||||
|
|
|
|||
|
|
@ -2,3 +2,13 @@
|
|||
@import "./additional.css";
|
||||
@import "./scrollbar.css";
|
||||
@import "./cursor.css";
|
||||
|
||||
@media print {
|
||||
* {
|
||||
overflow: visible !important;
|
||||
page-break-after: avoid;
|
||||
page-break-before: avoid;
|
||||
break-inside: avoid;
|
||||
height: max-content;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import * as React from "react"
|
||||
import Svg, { Path } from "react-native-svg"
|
||||
|
||||
interface PdfCuteReIconProps {
|
||||
width?: number
|
||||
height?: number
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const PdfCuteReIcon = ({
|
||||
width = 24,
|
||||
height = 24,
|
||||
color = "#10161F",
|
||||
}: PdfCuteReIconProps) => {
|
||||
return (
|
||||
<Svg width={width} height={height} fill="none" viewBox="0 0 24 24">
|
||||
<Path
|
||||
stroke={color}
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M10.5 2.5a3 3 0 0 1 3 3v.6c0 .372 0 .557.025.713a2 2 0 0 0 1.662 1.662c.156.025.341.025.713.025h.6a3 3 0 0 1 3 3M12 12l-.074.468a6 6 0 0 1-2.156 3.734l-.368.298.442-.17a6 6 0 0 1 4.31 0l.444.17-.37-.298a6 6 0 0 1-2.155-3.733zm-1.036-9.5h-.296c-2.022 0-3.032 0-3.82.357a4 4 0 0 0-1.991 1.991C4.5 5.636 4.5 6.646 4.5 8.668V14c0 3.288 0 4.931.908 6.038a4 4 0 0 0 .554.554c1.107.908 2.75.908 6.038.908 3.287 0 4.931 0 6.038-.908.202-.166.388-.352.554-.554.908-1.107.908-2.75.908-6.038v-2.964c0-1.033 0-1.55-.082-2.042a6 6 0 0 0-1.033-2.492C18.095 6.095 17.73 5.73 17 5s-1.095-1.095-1.502-1.385a6 6 0 0 0-2.492-1.033c-.492-.082-1.009-.082-2.042-.082Z"
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path stroke="#10161F" stroke-linejoin="round" stroke-width="2" d="M10.5 2.5a3 3 0 0 1 3 3v.6c0 .372 0 .557.025.713a2 2 0 0 0 1.662 1.662c.156.025.341.025.713.025h.6a3 3 0 0 1 3 3M12 12l-.074.468a6 6 0 0 1-2.156 3.734l-.368.298.442-.17a6 6 0 0 1 4.31 0l.444.17-.37-.298a6 6 0 0 1-2.155-3.733zm-1.036-9.5h-.296c-2.022 0-3.032 0-3.82.357a4 4 0 0 0-1.991 1.991C4.5 5.636 4.5 6.646 4.5 8.668V14c0 3.288 0 4.931.908 6.038a4 4 0 0 0 .554.554c1.107.908 2.75.908 6.038.908 3.287 0 4.931 0 6.038-.908.202-.166.388-.352.554-.554.908-1.107.908-2.75.908-6.038v-2.964c0-1.033 0-1.55-.082-2.042a6 6 0 0 0-1.033-2.492C18.095 6.095 17.73 5.73 17 5s-1.095-1.095-1.502-1.385a6 6 0 0 0-2.492-1.033c-.492-.082-1.009-.082-2.042-.082Z"/></svg>
|
||||
|
After Width: | Height: | Size: 852 B |
Loading…
Reference in New Issue