@jant/core 0.3.34 → 0.3.36
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/client/assets/module-RjUF93sV.js +716 -0
- package/dist/client/assets/native-48B9X9Wg.js +1 -0
- package/dist/client/assets/url-8Dj-5CLW.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +3109 -2294
- package/dist/index.js +3327 -3031
- package/package.json +13 -4
- package/src/__tests__/helpers/app.ts +1 -1
- package/src/__tests__/helpers/db.ts +6 -0
- package/src/app.tsx +1 -5
- package/src/{lib → client}/avatar-upload.ts +1 -1
- package/src/{lib → client}/collection-form-bridge.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
- package/src/{ui → client}/components/__tests__/jant-compose-dialog.test.ts +46 -14
- package/src/{ui → client}/components/__tests__/jant-compose-editor.test.ts +64 -24
- package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +24 -14
- package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +45 -0
- package/src/{ui → client}/components/collection-types.ts +3 -4
- package/src/{ui → client}/components/compose-types.ts +3 -1
- package/src/{ui → client}/components/jant-collection-form.ts +301 -182
- package/src/client/components/jant-collection-sidebar.ts +801 -0
- package/src/{ui → client}/components/jant-compose-dialog.ts +231 -1
- package/src/client/components/jant-compose-editor.ts +1249 -0
- package/src/client/components/jant-compose-fullscreen.ts +338 -0
- package/src/client/components/jant-media-lightbox.ts +257 -0
- package/src/{ui → client}/components/jant-nav-manager.ts +143 -84
- package/src/{ui → client}/components/jant-post-form.ts +57 -8
- package/src/{ui → client}/components/jant-settings-general.ts +2 -2
- package/src/{ui → client}/components/nav-manager-types.ts +3 -0
- package/src/{ui → client}/components/post-form-template.ts +35 -31
- package/src/{ui → client}/components/post-form-types.ts +7 -3
- package/src/{lib → client}/compose-bridge.ts +9 -7
- package/src/client/lazy-slugify.ts +51 -0
- package/src/{lib → client}/media-upload.ts +16 -3
- package/src/{lib → client}/nav-manager-bridge.ts +1 -1
- package/src/client/page-slug-bridge.ts +42 -0
- package/src/{lib → client}/post-form-bridge.ts +2 -2
- package/src/{lib → client}/settings-bridge.ts +3 -3
- package/src/client/tiptap/bubble-menu.ts +205 -0
- package/src/client/tiptap/create-editor.ts +40 -0
- package/src/client/tiptap/exitable-marks.ts +73 -0
- package/src/client/tiptap/extensions.ts +60 -0
- package/src/client/tiptap/image-node.ts +488 -0
- package/src/client/tiptap/link-toolbar.ts +371 -0
- package/src/client/tiptap/more-break.ts +50 -0
- package/src/client/tiptap/paste-image.ts +140 -0
- package/src/client/tiptap/slash-commands.ts +328 -0
- package/src/{types → client/types}/sortablejs.d.ts +1 -1
- package/src/client.ts +24 -17
- package/src/db/migrations/0012_add_tiptap_columns.sql +2 -0
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +8 -0
- package/src/db/schema.ts +6 -1
- package/src/i18n/locales/en.po +641 -215
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +642 -204
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +642 -204
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/__tests__/resolve-config.test.ts +2 -2
- package/src/lib/__tests__/schemas.test.ts +9 -6
- package/src/lib/__tests__/url.test.ts +2 -2
- package/src/lib/__tests__/view.test.ts +9 -9
- package/src/lib/emoji-catalog.ts +146 -0
- package/src/lib/feed.ts +1 -1
- package/src/lib/media-helpers.ts +10 -9
- package/src/lib/render.tsx +4 -3
- package/src/lib/resolve-config.ts +8 -1
- package/src/lib/schemas.ts +2 -3
- package/src/lib/summary.ts +92 -0
- package/src/lib/timeline.ts +2 -0
- package/src/lib/tiptap-render.ts +196 -0
- package/src/lib/upload.ts +97 -9
- package/src/lib/url.ts +7 -23
- package/src/lib/view.ts +33 -19
- package/src/middleware/error-handler.ts +3 -3
- package/src/preset.css +38 -0
- package/src/routes/api/collections.ts +20 -3
- package/src/routes/api/posts.ts +48 -33
- package/src/routes/api/upload.ts +7 -5
- package/src/routes/auth/reset.tsx +5 -4
- package/src/routes/auth/setup.tsx +26 -11
- package/src/routes/auth/signin.tsx +10 -7
- package/src/routes/compose.tsx +20 -11
- package/src/routes/dash/__tests__/settings-avatar.test.ts +43 -8
- package/src/routes/dash/index.tsx +7 -1
- package/src/routes/dash/media.tsx +3 -0
- package/src/routes/dash/pages.tsx +8 -2
- package/src/routes/dash/posts.tsx +6 -2
- package/src/routes/dash/redirects.tsx +15 -9
- package/src/routes/dash/settings.tsx +336 -32
- package/src/routes/feed/__tests__/rss.test.ts +245 -6
- package/src/routes/feed/rss.ts +70 -6
- package/src/routes/pages/__tests__/featured.test.ts +6 -7
- package/src/routes/pages/archive.tsx +11 -7
- package/src/routes/pages/collection.tsx +32 -15
- package/src/routes/pages/collections.tsx +11 -2
- package/src/routes/pages/featured.tsx +1 -1
- package/src/routes/pages/home.tsx +1 -1
- package/src/services/__tests__/post.test.ts +124 -33
- package/src/services/__tests__/settings.test.ts +3 -3
- package/src/services/page.ts +16 -3
- package/src/services/post.ts +96 -37
- package/src/services/search.ts +4 -2
- package/src/services/settings.ts +6 -2
- package/src/styles/components.css +240 -60
- package/src/styles/tokens.css +10 -0
- package/src/styles/ui.css +1157 -81
- package/src/types/bindings.ts +5 -0
- package/src/types/config.ts +23 -1
- package/src/types/constants.ts +3 -0
- package/src/types/entities.ts +9 -2
- package/src/types/operations.ts +9 -3
- package/src/types/props.ts +3 -3
- package/src/types/views.ts +3 -2
- package/src/ui/compose/ComposeDialog.tsx +24 -7
- package/src/ui/dash/PageForm.tsx +2 -0
- package/src/ui/dash/PostList.tsx +5 -5
- package/src/ui/dash/StatusBadge.tsx +13 -5
- package/src/ui/dash/appearance/AdvancedContent.tsx +52 -61
- package/src/ui/dash/appearance/ColorThemeContent.tsx +30 -35
- package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
- package/src/ui/dash/appearance/NavigationContent.tsx +107 -96
- package/src/ui/dash/media/MediaListContent.tsx +9 -4
- package/src/ui/dash/media/ViewMediaContent.tsx +2 -2
- package/src/ui/dash/pages/PagesContent.tsx +2 -1
- package/src/ui/dash/posts/PostForm.tsx +19 -7
- package/src/ui/dash/settings/AccountContent.tsx +133 -138
- package/src/ui/dash/settings/AvatarContent.tsx +70 -0
- package/src/ui/dash/settings/GeneralContent.tsx +3 -62
- package/src/ui/dash/settings/SettingsRootContent.tsx +236 -0
- package/src/ui/layouts/DashLayout.tsx +157 -75
- package/src/ui/layouts/SiteLayout.tsx +13 -13
- package/src/ui/pages/ArchivePage.tsx +10 -7
- package/src/ui/pages/CollectionPage.tsx +6 -35
- package/src/ui/pages/CollectionsPage.tsx +2 -1
- package/src/ui/pages/FeaturedPage.tsx +2 -1
- package/src/ui/pages/HomePage.tsx +1 -1
- package/src/ui/pages/SearchPage.tsx +1 -1
- package/src/ui/shared/CollectionsSidebar.tsx +228 -3
- package/src/ui/shared/MediaGallery.tsx +179 -41
- package/src/lib/collections-reorder.ts +0 -28
- package/src/routes/dash/appearance.tsx +0 -240
- package/src/routes/dash/collections.tsx +0 -211
- package/src/ui/components/jant-compose-editor.ts +0 -814
- package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
- package/src/ui/dash/collections/CollectionForm.tsx +0 -166
- package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
- package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
- package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
- package/src/ui/dash/settings/SettingsNav.tsx +0 -52
- /package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +0 -0
- /package/src/{ui → client}/components/jant-settings-avatar.ts +0 -0
- /package/src/{ui → client}/components/settings-types.ts +0 -0
- /package/src/{lib → client}/image-processor.ts +0 -0
- /package/src/{lib → client}/toast.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+7Wr2a\":[\"Edit: \",[\"title\"]],\"+AXdXp\":[\"標籤和網址是必填的\"],\"+MACwa\":[\"尚未有任何收藏。\"],\"+MH6k9\":[\"Add to nav\"],\"+bHzpy\":[\"Display text for the link\"],\"+owNNn\":[\"文章\"],\"+zy2Nq\":[\"類型\"],\"/0D1Xp\":[\"編輯收藏集\"],\"/DFKdU\":[\"輸入引用...\"],\"/R/sGB\":[\"密碼已成功更改。\"],\"/Rj5P4\":[\"您的姓名\"],\"/rkqRV\":[\"All pages are in your navigation.\"],\"07Epll\":[\"這將為您的網站和儀表板設置主題。所有顏色主題都支持深色模式。\"],\"0JkyS7\":[\"Create your first page\"],\"0a6MpL\":[\"新重定向\"],\"0ieXE7\":[\"最高評價\"],\"0yIy82\":[\"尚未有精選文章。\"],\"1CU1Td\":[\"URL-safe identifier (lowercase, numbers, hyphens)\"],\"1DBGsz\":[\"筆記\"],\"1Oj1sI\":[\"已保存順序\"],\"1o+wgo\":[\"e.g. The Verge, John Doe\"],\"2N0qpv\":[\"文章標題...\"],\"2cFU6q\":[\"網站頁腳\"],\"2fUwEY\":[\"選擇媒體\"],\"2q/Q7x\":[\"Visibility\"],\"2rJGtU\":[\"頁面標題...\"],\"3SAro+\":[\"Choose a font for your site. All options use system fonts for fast loading.\"],\"3Siwmw\":[\"More options\"],\"3Yvsaz\":[\"302(臨時)\"],\"3uSoGn\":[\"Header Nav Links\"],\"4/SFQS\":[\"查看網站\"],\"40TVQj\":[\"Custom Path (optional)\"],\"4JBD+x\":[\"保存失敗。請再試一次。\"],\"4KzVT6\":[\"刪除頁面\"],\"4Ml90q\":[\"SEO\"],\"4b3oEV\":[\"內容\"],\"4mDPGp\":[\"此頁面的 URL 路徑。使用小寫字母、數字和連字符。\"],\"538Vy5\":[\"尚未有導航項目。請在下方添加頁面、鏈接或啟用系統項目。\"],\"6C8dEg\":[\"附加文本\"],\"6WdDG7\":[\"頁面\"],\"6YtxFj\":[\"名稱\"],\"6tU2jr\":[\"找不到任何收藏。\"],\"71Xwww\":[\"Invalid request\"],\"7G4SBz\":[\"頁面內容(支持Markdown)...\"],\"7Mk+/h\":[\"更新收藏集\"],\"7Q1KKN\":[\"來源路徑\"],\"7aECQB\":[\"無效或已過期的連結\"],\"7aYVPs\":[\"所有頁面都在導航中\"],\"7nGhhM\":[\"你在想什麼?\"],\"7p5kLi\":[\"儀表板\"],\"7vhWI8\":[\"新密碼\"],\"87a/t/\":[\"標籤\"],\"8HgKQc\":[\"SEO 設定已成功儲存。\"],\"8WX0J+\":[\"您的想法(可選)\"],\"8WtVZw\":[\"無法保存帖子。請再試一次。\"],\"8ZsakT\":[\"密碼\"],\"8qX8Jl\":[\"選擇一個字體搭配以供您的網站使用。所有選項均使用系統字體以加快加載速度。\"],\"8tM8+a\":[\"儲存為草稿\"],\"8xE385\":[\"添加到導航\"],\"9+vGLh\":[\"自訂 CSS\"],\"90Luob\":[[\"count\"],\" replies\"],\"A1taO8\":[\"搜尋\"],\"AeXO77\":[\"帳戶\"],\"AyHO4m\":[\"這個收藏是關於什麼的?\"],\"Az4JB1\":[\"Use Featured as default home view\"],\"B373X+\":[\"編輯文章\"],\"B495Gs\":[\"檔案館\"],\"BjF0Jv\":[\"僅限小寫字母、數字和連字符\"],\"Cl55aD\":[\"當前密碼不正確。\"],\"D3uuEX\":[\"尚未選擇任何媒體。\"],\"D9Oea+\":[\"永久鏈接\"],\"DCKkhU\":[\"當前密碼\"],\"DHhJ7s\":[\"上一頁\"],\"DPfwMq\":[\"完成\"],\"DVljCN\":[\"Choose a page…\"],\"DoJzLz\":[\"收藏夾\"],\"E80cJw\":[\"刪除此媒體將永久從存儲中移除它。\"],\"EEYbdt\":[\"發佈\"],\"EGwzOK\":[\"完成設置\"],\"EdQY6l\":[\"None\"],\"EkH9pt\":[\"更新\"],\"Eq6YVV\":[\"分數\"],\"FESYvt\":[\"為視障人士描述這個...\"],\"FGrimz\":[\"新帖子\"],\"FkMol5\":[\"精選\"],\"Fxf4jq\":[\"描述(可選)\"],\"GA5A5H\":[\"Delete Collection\"],\"GBDTf5\":[\"身份驗證未配置\"],\"GHg6h/\":[\"post\"],\"GTPbOX\":[\"Your site navigation\"],\"GX2VMa\":[\"建立您的管理員帳戶。\"],\"GbVAnd\":[\"此密碼重設連結無效或已過期。請生成一個新的連結。\"],\"GlEzsR\":[\"為搜尋引擎和訂閱閱讀器提供的簡短介紹。僅限純文字。\"],\"GorKul\":[\"歡迎來到 Jant\"],\"GrZ6fH\":[\"新頁面\"],\"GxkJXS\":[\"上傳中...\"],\"H29JXm\":[\"+ ALT\"],\"HfyyXl\":[\"My Blog\"],\"HiETwV\":[\"Quiet (normal)\"],\"Hzi9AA\":[\"未找到任何帖子。\"],\"I6gXOa\":[\"Path\"],\"I8hDlV\":[\"At least 1 image required for image posts.\"],\"IUwGEM\":[\"Save Changes\"],\"IagCbF\":[\"網址\"],\"J4FNfC\":[\"此集合中沒有帖子。\"],\"J6bLeg\":[\"添加自定義連結到任何 URL\"],\"JIBC/T\":[\"Supported formats: JPEG, PNG, GIF, WebP, SVG. Max size: 10MB.\"],\"Jed1wB\":[\"需要幫助嗎?請訪問<0>文檔</0>。\"],\"JiP4aa\":[\"Published pages are accessible via their path. Drafts are not visible.\"],\"K0r7TC\":[\"What's new?\"],\"K9NcLu\":[\"使用此 URL 將媒體嵌入到您的帖子中。\"],\"KbS2K9\":[\"重設密碼\"],\"KdSsVl\":[\"作者(可選)\"],\"KiJn9B\":[\"備註\"],\"KmGXnO\":[\"您確定要刪除這篇文章嗎?這個操作無法撤銷。\"],\"KsIZ3c\":[\"Footer saved successfully.\"],\"KuCcWu\":[\"顯示在所有文章和頁面的底部。支持Markdown。\"],\"L85WcV\":[\"縮略名\"],\"LdyooL\":[\"鏈接\"],\"LkA8jz\":[\"添加替代文字\"],\"LkvLQe\":[\"No pages yet.\"],\"M1RvTd\":[\"點擊圖片以查看完整大小\"],\"M2kIWU\":[\"字型主題\"],\"M6CbAU\":[\"切換編輯面板\"],\"M8kJqa\":[\"草稿\"],\"M8lheL\":[\"最大可見導航連結\"],\"M9xgHy\":[\"重定向\"],\"MHrjPM\":[\"標題\"],\"MLSRl9\":[\"引用文本\"],\"MWBOxm\":[\"收藏(可選)\"],\"MZbQHL\":[\"未找到結果。\"],\"MdMyne\":[\"來源連結(選填)\"],\"Mhf/H/\":[\"建立重定向\"],\"MnbH31\":[\"頁面\"],\"MqghUt\":[\"搜尋帖子...\"],\"N0APCr\":[\"This is displayed above your blog posts on your default home page. This is also used for the meta description on your home page.\"],\"N40H+G\":[\"所有\"],\"NU2Fqi\":[\"儲存 CSS\"],\"Naqg3G\":[\"未提供檔案\"],\"O3oNi5\":[\"電子郵件\"],\"OCNZaU\":[\"重定向來源的路徑\"],\"ODiSoW\":[\"尚未有帖子。\"],\"ONWvwQ\":[\"上傳\"],\"OVSkIF\":[\"敏捷的棕色狐狸跳過懶惰的狗。\"],\"OeUWA7\":[\"新增頁面\"],\"P/XNX0\":[\"This is used for your favicon.\"],\"PJnyHS\":[\"Max visible links saved\"],\"PZ7HJ8\":[\"部落格頭像\"],\"Pbm2/N\":[\"創建收藏夾\"],\"QEbNBb\":[\"Path (e.g. /archive) or full URL (e.g. https://example.com)\"],\"QLkhbH\":[\"被引用的文本...\"],\"Qjlym2\":[\"無法創建帳戶\"],\"RDjuBN\":[\"Setup\"],\"Rj01Fz\":[\"連結\"],\"RwGhWy\":[\"包含 \",[\"count\"],\" 則帖子的主題\"],\"RxsRD6\":[\"時區\"],\"SGJDS5\":[\"儲存未配置\"],\"SJmfuf\":[\"網站名稱\"],\"ST+lN2\":[\"尚未上傳任何媒體。\"],\"T0bsor\":[\"設置已成功保存。\"],\"TNFigk\":[\"預設首頁視圖\"],\"Tt5T6+\":[\"Articles\"],\"TxE+Mj\":[\"1 reply\"],\"Tz0i8g\":[\"設定\"],\"U5v6Gh\":[\"編輯頁面\"],\"UDMjsP\":[\"快速操作\"],\"UGT5vp\":[\"Save Settings\"],\"UeOiKl\":[\"無效的輸入\"],\"Ui5/i3\":[\"允許搜尋引擎索引我的網站是可以的\"],\"Uj/btJ\":[\"在我的網站標頭中顯示頭像\"],\"UxKoFf\":[\"導航\"],\"UzGRD9\":[\"Home view saved\"],\"V4WsyL\":[\"新增連結\"],\"VUSy8D\":[\"Search failed. Please try again.\"],\"VgozPa\":[\"頭像顯示設置已成功保存。\"],\"VhMDMg\":[\"更改密碼\"],\"Vn3jYy\":[\"導航項目\"],\"WDcQq9\":[\"Unlisted\"],\"Weq9zb\":[\"一般設定\"],\"WmZ/rP\":[\"到路徑\"],\"XrnWzN\":[\"已發佈!\"],\"Y+7JGK\":[\"創建頁面\"],\"Y75ho6\":[\"Other pages\"],\"YIix5Y\":[\"搜尋...\"],\"Z2lfX1\":[\"選擇圖示\"],\"Z3FXyt\":[\"載入中...\"],\"Z6NwTi\":[\"儲存為草稿\"],\"ZQKLI1\":[\"危險區域\"],\"ZUpE9/\":[\"This is displayed at the bottom of all of your posts and pages. Markdown is supported.\"],\"ZhhOwV\":[\"引用\"],\"ZmUkwN\":[\"Add custom link to navigation\"],\"aAIQg2\":[\"外觀\"],\"aHTB7P\":[\"附加在您帖子上的補充內容\"],\"aT4jc4\":[\"無效的電子郵件或密碼\"],\"aaGV/9\":[\"New Link\"],\"ajBsih\":[\"發佈文章成功。\"],\"alKG0+\":[\"字型主題\"],\"an5hVd\":[\"Images\"],\"anibOb\":[\"關於這個部落格\"],\"b+/jO6\":[\"301(永久)\"],\"b+FyBD\":[\"Add page to navigation\"],\"b+JhJf\":[\"Max visible links\"],\"b4VwHs\":[\"未提供檔案。\"],\"bDqhXY\":[\"刪除失敗。請再試一次。\"],\"bG4pfW\":[\"Displayed above your blog posts on the home page. Also used as the meta description. Markdown supported.\"],\"bHYIks\":[\"登出\"],\"bcE7Mx\":[\"無法更新個人資料。\"],\"biOepV\":[\"← Back to home\"],\"bzSI52\":[\"丟棄\"],\"cTUByn\":[\"最新的在前\"],\"ccaIM9\":[\"更多連結\"],\"cnGeoo\":[\"刪除\"],\"dEgA5A\":[\"取消\"],\"dStw5E\":[\"將現有頁面添加到您的導航中\"],\"dmCcPs\":[\"這是用於您的網站圖標和蘋果觸控圖標。為了獲得最佳效果,請上傳至少 180x180 像素的正方形圖片。\"],\"dtQNkT\":[\"選擇的字體主題無效。\"],\"e6Jr7Q\":[\"← 返回收藏夾\"],\"ePK91l\":[\"編輯\"],\"eWLklq\":[\"引用\"],\"eneWvv\":[\"草稿\"],\"er8+x7\":[\"示範帳戶已預填。只需點擊登入。\"],\"f/bxrN\":[\"名稱是必填的。\"],\"f6e0Ry\":[\"Article\"],\"fDGOiR\":[\"密碼不匹配。\"],\"fG7BxZ\":[\"Upload images via the API: POST /api/upload with a file form field.\"],\"fttd2R\":[\"我的收藏\"],\"g3mKmM\":[\"Un-nav\"],\"gDx5MG\":[\"Edit Link\"],\"gJH6Bs\":[\"替代文字改善可及性\"],\"gOwwEy\":[\"儲存空間未配置。\"],\"hDRU5q\":[\"找到 \",[\"0\"],\" 個結果\"],\"hG89Ed\":[\"Image\"],\"hQAbqI\":[\"尚未有頁面。創建您的第一個頁面以開始使用。\"],\"hWOZIv\":[\"請輸入您的新密碼。\"],\"hXzOVo\":[\"下一頁\"],\"he3ygx\":[\"複製\"],\"heSQoS\":[\"粘貼一個網址...\"],\"hmXTCY\":[\"選擇的主題無效。\"],\"hrL0Be\":[\"圖示(可選)\"],\"i0vDGK\":[\"排序順序\"],\"iBc+/N\":[\"Custom URL path. Leave empty to use default /p/ID format.\"],\"iDAqU6\":[\"網址(可選)\"],\"iEUzMn\":[\"系統\"],\"iEqmSU\":[\"自訂 CSS 已成功儲存。\"],\"iH8pgl\":[\"返回\"],\"iPHeYN\":[\"Posting...\"],\"ig4hg2\":[\"Let's set up your site.\"],\"jSRrXo\":[\"已發佈的頁面可以通過其標識符訪問。草稿不可見。\"],\"jUV7CU\":[\"上傳頭像\"],\"jVUmOK\":[\"支援Markdown\"],\"jpctdh\":[\"查看\"],\"jt/Ow/\":[\"posts\"],\"jvyYZG\":[\"你在想什麼...\"],\"k1ifdL\":[\"處理中...\"],\"kI1qVD\":[\"格式\"],\"kL1h6U\":[\"移除分隔線\"],\"kNiQp6\":[\"置頂\"],\"kPMIr+\":[\"給它一個標題...\"],\"kd7eBB\":[\"Create Link\"],\"kj6ppi\":[\"條目\"],\"kyNTQ2\":[\"Emoji or icon name\"],\"l/6JHD\":[\"搜尋圖示...\"],\"l6ANt9\":[\"最低評價\"],\"lO1Oow\":[\"上傳成功!\"],\"m16xKo\":[\"新增\"],\"mO5HMZ\":[\"All pages are already in navigation.\"],\"mTOYla\":[\"View all posts →\"],\"mnkknn\":[\"Collection (optional)\"],\"n1ekoW\":[\"登入\"],\"n6QD94\":[\"最舊的在前\"],\"o21Y+P\":[\"條目\"],\"o4dofa\":[\"AUTH_SECRET 未配置\"],\"oJFOZk\":[\"Source Name (optional)\"],\"oKOOsY\":[\"顏色主題\"],\"oSiRP0\":[\"系統連結\"],\"oYPBa0\":[\"更新頁面\"],\"p2/GCq\":[\"確認密碼\"],\"pB0OKE\":[\"新分隔線\"],\"pI2MWS\":[\"Search pages…\"],\"pRhYH2\":[\"收藏中的帖子 (\",[\"count\"],\")\"],\"pZq3aX\":[\"上傳失敗。請再試一次。\"],\"pnve/d\":[\"個人資料已成功儲存。\"],\"q+hNag\":[\"集合\"],\"qMyM2u\":[\"Source URL (optional)\"],\"qdxmd4\":[\"Attached text\"],\"qiXmlF\":[\"添加媒體\"],\"qt89I8\":[\"草稿已保存。\"],\"quFPTj\":[\"Custom Slug (optional)\"],\"r1MpXi\":[\"Quiet\"],\"rFmBG3\":[\"顏色主題\"],\"rdUucN\":[\"預覽\"],\"rzNUSl\":[\"包含 1 則貼文的主題\"],\"sDGoxy\":[\"切換內建導航項目。啟用的項目會與頁面和連結一起顯示在您的導航中。\"],\"sGajR7\":[\"線程開始\"],\"smzF8S\":[\"顯示 \",[\"remainingCount\"],\" 個更多 \",[\"0\"]],\"ssqvZi\":[\"保存個人資料\"],\"sxkWRg\":[\"進階\"],\"t/YqKh\":[\"移除\"],\"tfDRzk\":[\"保存\"],\"tfQNeI\":[\"No pages found.\"],\"tfrt7B\":[\"未配置任何重定向。\"],\"tiq7kl\":[\"頁面 \",[\"page\"]],\"u2f7vd\":[\"Site Description\"],\"u3wRF+\":[\"已發佈\"],\"u6Hp4N\":[\"Markdown\"],\"uAQUqI\":[\"狀態\"],\"vERlcd\":[\"個人資料\"],\"vXIe7J\":[\"語言\"],\"vh0C9b\":[\"No navigation links yet. Add pages to navigation or create links.\"],\"vmQmHx\":[\"添加自定義 CSS 以覆蓋任何樣式。使用數據屬性,如 [data-page]、[data-post]、[data-format] 來針對特定元素。\"],\"vzU4k9\":[\"新收藏集\"],\"w8Rv8T\":[\"標籤是必需的\"],\"wEF6Ix\":[\"目的地路徑或 URL\"],\"wK4OTM\":[\"標題(選填)\"],\"wL3cK8\":[\"最新\"],\"wM5UXj\":[\"刪除媒體\"],\"wRR604\":[\"頁面\"],\"wc+17X\":[\"/* 您的自訂 CSS 在這裡 */\"],\"wdGjkd\":[\"No navigation links configured.\"],\"wja8aL\":[\"無標題\"],\"x+doid\":[\"圖片會自動優化:調整大小至最大 1920 像素,轉換為 WebP 格式,並去除元數據。\"],\"x0mzE0\":[\"創建你的第一篇帖子\"],\"x4RuFo\":[\"Back to home\"],\"xYilR2\":[\"媒體\"],\"y0R9F0\":[\"帖子已成功更新。\"],\"y28hnO\":[\"文章\"],\"yQ2kGp\":[\"載入更多\"],\"yjkELF\":[\"確認新密碼\"],\"yz7wBu\":[\"關閉\"],\"yzF66j\":[\"連結\"],\"z1U/Fh\":[\"Rating\"],\"z8ajIE\":[\"找到 1 個結果\"],\"zBFr9G\":[\"粘貼一篇長文章、AI 回應或任何文本...\\n\\nMarkdown 格式將被保留。\"],\"zH6KqE\":[\"Found \",[\"count\"],\" results\"],\"zennIg\":[\"URL安全識別碼(小寫、數字、連字符)。對於CJK標題,slug將在伺服器上自動生成。\"],\"zucql+\":[\"菜單\"]}")as Messages;
|
|
1
|
+
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+7Wr2a\":[\"Edit: \",[\"title\"]],\"+AXdXp\":[\"標籤和網址是必填的\"],\"+MACwa\":[\"尚未有任何收藏。\"],\"+MH6k9\":[\"Add to nav\"],\"+bHzpy\":[\"Display text for the link\"],\"+owNNn\":[\"文章\"],\"+zy2Nq\":[\"類型\"],\"/0D1Xp\":[\"編輯收藏集\"],\"/DFKdU\":[\"輸入引用...\"],\"/R/sGB\":[\"密碼已成功更改。\"],\"/Rj5P4\":[\"您的姓名\"],\"/eDNhN\":[\"Open with Featured posts\"],\"/rkqRV\":[\"All pages are in your navigation.\"],\"07Epll\":[\"這將為您的網站和儀表板設置主題。所有顏色主題都支持深色模式。\"],\"0JkyS7\":[\"Create your first page\"],\"0OGSSc\":[\"Avatar display updated.\"],\"0a6MpL\":[\"新重定向\"],\"0ieXE7\":[\"最高評價\"],\"0yIy82\":[\"尚未有精選文章。\"],\"1Bp4MW\":[\"Images are automatically optimized (resized, converted to WebP). Video, audio, and PDF files are uploaded as-is (max 200MB).\"],\"1CU1Td\":[\"URL-safe identifier (lowercase, numbers, hyphens)\"],\"1DBGsz\":[\"筆記\"],\"1Jp3EJ\":[\"Your media library is empty. Upload your first file to get started.\"],\"1Oj1sI\":[\"已保存順序\"],\"1o+wgo\":[\"e.g. The Verge, John Doe\"],\"1u8a3u\":[\"Search emojis...\"],\"2N0qpv\":[\"文章標題...\"],\"2TgGnr\":[\"Name, description, language\"],\"2cFU6q\":[\"網站頁腳\"],\"2fUwEY\":[\"選擇媒體\"],\"2q/Q7x\":[\"Visibility\"],\"2rJGtU\":[\"頁面標題...\"],\"2z9Tm/\":[\"Nothing published yet. Write your first post to get started.\"],\"3SAro+\":[\"Choose a font for your site. All options use system fonts for fast loading.\"],\"3Siwmw\":[\"More options\"],\"3Yvsaz\":[\"302(臨時)\"],\"3uSoGn\":[\"Header Nav Links\"],\"3wKq0C\":[\"Couldn't save. Try again in a moment.\"],\"4/SFQS\":[\"查看網站\"],\"40TVQj\":[\"Custom Path (optional)\"],\"4JBD+x\":[\"保存失敗。請再試一次。\"],\"4KzVT6\":[\"刪除頁面\"],\"4Ml90q\":[\"SEO\"],\"4WO1Hp\":[\"No matching collections.\"],\"4XdN4s\":[\"Profile, password\"],\"4b3oEV\":[\"內容\"],\"4mDPGp\":[\"此頁面的 URL 路徑。使用小寫字母、數字和連字符。\"],\"538Vy5\":[\"尚未有導航項目。請在下方添加頁面、鏈接或啟用系統項目。\"],\"6C8dEg\":[\"附加文本\"],\"6EwmNQ\":[\"Favicon and header icon\"],\"6WdDG7\":[\"頁面\"],\"6YtxFj\":[\"名稱\"],\"6tU2jr\":[\"找不到任何收藏。\"],\"71Xwww\":[\"Invalid request\"],\"7G4SBz\":[\"頁面內容(支持Markdown)...\"],\"7MZxzw\":[\"Password changed.\"],\"7Mk+/h\":[\"更新收藏集\"],\"7Q1KKN\":[\"來源路徑\"],\"7aECQB\":[\"無效或已過期的連結\"],\"7aYVPs\":[\"所有頁面都在導航中\"],\"7nGhhM\":[\"你在想什麼?\"],\"7p5kLi\":[\"儀表板\"],\"7vhWI8\":[\"新密碼\"],\"81nFIS\":[\"Passwords don't match. Make sure both fields are identical.\"],\"87a/t/\":[\"標籤\"],\"89Upyo\":[\"That theme isn't available. Pick another one.\"],\"8HgKQc\":[\"SEO 設定已成功儲存。\"],\"8WX0J+\":[\"您的想法(可選)\"],\"8WtVZw\":[\"無法保存帖子。請再試一次。\"],\"8ZsakT\":[\"密碼\"],\"8bpHix\":[\"Couldn't create your account. Check the details and try again.\"],\"8qX8Jl\":[\"選擇一個字體搭配以供您的網站使用。所有選項均使用系統字體以加快加載速度。\"],\"8tM8+a\":[\"儲存為草稿\"],\"8xE385\":[\"添加到導航\"],\"9+vGLh\":[\"自訂 CSS\"],\"90Luob\":[[\"count\"],\" replies\"],\"9rS2kh\":[\"Add More\"],\"A1taO8\":[\"搜尋\"],\"AeXO77\":[\"帳戶\"],\"Ap948/\":[\"Listed\"],\"AyHO4m\":[\"這個收藏是關於什麼的?\"],\"Az4JB1\":[\"Use Featured as default home view\"],\"B373X+\":[\"編輯文章\"],\"B495Gs\":[\"檔案館\"],\"BjF0Jv\":[\"僅限小寫字母、數字和連字符\"],\"Cl55aD\":[\"當前密碼不正確。\"],\"D3uuEX\":[\"尚未選擇任何媒體。\"],\"D558v3\":[\"A display name is required.\"],\"D9Oea+\":[\"永久鏈接\"],\"DCKkhU\":[\"當前密碼\"],\"DHhJ7s\":[\"上一頁\"],\"DMvU/i\":[\"Write your first post\"],\"DPfwMq\":[\"完成\"],\"DVljCN\":[\"Choose a page…\"],\"DoJzLz\":[\"收藏夾\"],\"E80cJw\":[\"刪除此媒體將永久從存儲中移除它。\"],\"EB4HRn\":[\"No pages yet. Create one to add static content to your site.\"],\"EEYbdt\":[\"發佈\"],\"EGwzOK\":[\"完成設置\"],\"EO3I6h\":[\"Upload didn't go through. Try again in a moment.\"],\"EdQY6l\":[\"None\"],\"EkH9pt\":[\"更新\"],\"FESYvt\":[\"為視障人士描述這個...\"],\"FGrimz\":[\"新帖子\"],\"FdE+3J\":[\"No matching pages.\"],\"FkMol5\":[\"精選\"],\"Fxf4jq\":[\"描述(可選)\"],\"GA5A5H\":[\"Delete Collection\"],\"GBDTf5\":[\"身份驗證未配置\"],\"GHg6h/\":[\"post\"],\"GTPbOX\":[\"Your site navigation\"],\"GX2VMa\":[\"建立您的管理員帳戶。\"],\"GbVAnd\":[\"此密碼重設連結無效或已過期。請生成一個新的連結。\"],\"GlEzsR\":[\"為搜尋引擎和訂閱閱讀器提供的簡短介紹。僅限純文字。\"],\"GorKul\":[\"歡迎來到 Jant\"],\"GrZ6fH\":[\"新頁面\"],\"GvJQun\":[\"No featured posts. Mark a post as featured to highlight it here.\"],\"GxkJXS\":[\"上傳中...\"],\"H29JXm\":[\"+ ALT\"],\"H4lgRd\":[\"Authentication isn't set up. Check your server config.\"],\"HNEHJP\":[\"Demo credentials are pre-filled — hit Sign In to continue.\"],\"HfyyXl\":[\"My Blog\"],\"HiETwV\":[\"Quiet (normal)\"],\"Hzi9AA\":[\"未找到任何帖子。\"],\"I6gXOa\":[\"Path\"],\"I8hDlV\":[\"At least 1 image required for image posts.\"],\"IUwGEM\":[\"Save Changes\"],\"IagCbF\":[\"網址\"],\"IdYvsa\":[\"Post updated.\"],\"J4FNfC\":[\"此集合中沒有帖子。\"],\"J6bLeg\":[\"添加自定義連結到任何 URL\"],\"JIBC/T\":[\"Supported formats: JPEG, PNG, GIF, WebP, SVG. Max size: 10MB.\"],\"JcD7qf\":[\"More actions\"],\"Jed1wB\":[\"需要幫助嗎?請訪問<0>文檔</0>。\"],\"JiP4aa\":[\"Published pages are accessible via their path. Drafts are not visible.\"],\"JuN5GC\":[\"No file selected. Choose a file to upload.\"],\"K0r7TC\":[\"What's new?\"],\"K9NcLu\":[\"使用此 URL 將媒體嵌入到您的帖子中。\"],\"KbS2K9\":[\"重設密碼\"],\"KdSsVl\":[\"作者(可選)\"],\"KiJn9B\":[\"備註\"],\"KmGXnO\":[\"您確定要刪除這篇文章嗎?這個操作無法撤銷。\"],\"KsIZ3c\":[\"Footer saved successfully.\"],\"KuCcWu\":[\"顯示在所有文章和頁面的底部。支持Markdown。\"],\"L0gres\":[\"The rest will be tucked into a ··· menu\"],\"L85WcV\":[\"縮略名\"],\"LdyooL\":[\"鏈接\"],\"LkA8jz\":[\"添加替代文字\"],\"LkvLQe\":[\"No pages yet.\"],\"LsskIi\":[\"URL redirects\"],\"M1RvTd\":[\"點擊圖片以查看完整大小\"],\"M2kIWU\":[\"字型主題\"],\"M6CbAU\":[\"切換編輯面板\"],\"M8kJqa\":[\"草稿\"],\"M8lheL\":[\"最大可見導航連結\"],\"M9xgHy\":[\"重定向\"],\"MHrjPM\":[\"標題\"],\"MLSRl9\":[\"引用文本\"],\"MWBOxm\":[\"收藏(可選)\"],\"MZbQHL\":[\"未找到結果。\"],\"MdMyne\":[\"來源連結(選填)\"],\"Mhf/H/\":[\"建立重定向\"],\"MnbH31\":[\"頁面\"],\"MqghUt\":[\"搜尋帖子...\"],\"N0APCr\":[\"This is displayed above your blog posts on your default home page. This is also used for the meta description on your home page.\"],\"N40H+G\":[\"所有\"],\"NU2Fqi\":[\"儲存 CSS\"],\"Naqg3G\":[\"未提供檔案\"],\"O3oNi5\":[\"電子郵件\"],\"OCNZaU\":[\"重定向來源的路徑\"],\"ODiSoW\":[\"尚未有帖子。\"],\"ONWvwQ\":[\"上傳\"],\"OVSkIF\":[\"敏捷的棕色狐狸跳過懶惰的狗。\"],\"OeUWA7\":[\"新增頁面\"],\"P/XNX0\":[\"This is used for your favicon.\"],\"PJnyHS\":[\"Max visible links saved\"],\"PKhdhq\":[\"Links shown in header\"],\"PZ7HJ8\":[\"部落格頭像\"],\"Pbm2/N\":[\"創建收藏夾\"],\"QEbNBb\":[\"Path (e.g. /archive) or full URL (e.g. https://example.com)\"],\"QLkhbH\":[\"被引用的文本...\"],\"Qjlym2\":[\"無法創建帳戶\"],\"QyDt3L\":[\"File uploaded.\"],\"RDjuBN\":[\"Setup\"],\"Rj01Fz\":[\"連結\"],\"RwGhWy\":[\"包含 \",[\"count\"],\" 則帖子的主題\"],\"RxsRD6\":[\"時區\"],\"S79Ozx\":[\"Profile updated.\"],\"SGJDS5\":[\"儲存未配置\"],\"SJmfuf\":[\"網站名稱\"],\"ST+lN2\":[\"尚未上傳任何媒體。\"],\"T0bsor\":[\"設置已成功保存。\"],\"TEhb9O\":[\"Add Divider\"],\"TNFigk\":[\"預設首頁視圖\"],\"Tt5T6+\":[\"Articles\"],\"TxE+Mj\":[\"1 reply\"],\"Tz0i8g\":[\"設定\"],\"U5v6Gh\":[\"編輯頁面\"],\"UDMjsP\":[\"快速操作\"],\"UGT5vp\":[\"Save Settings\"],\"UIMXHD\":[\"Remove Divider\"],\"UeOiKl\":[\"無效的輸入\"],\"Ui5/i3\":[\"允許搜尋引擎索引我的網站是可以的\"],\"Uj/btJ\":[\"在我的網站標頭中顯示頭像\"],\"UxKoFf\":[\"導航\"],\"UzGRD9\":[\"Home view saved\"],\"V4WsyL\":[\"新增連結\"],\"VUSy8D\":[\"Search failed. Please try again.\"],\"VgozPa\":[\"頭像顯示設置已成功保存。\"],\"VhMDMg\":[\"更改密碼\"],\"Vn3jYy\":[\"導航項目\"],\"WDcQq9\":[\"Unlisted\"],\"Weq9zb\":[\"一般設定\"],\"WmZ/rP\":[\"到路徑\"],\"WpXcBJ\":[\"Nothing here yet.\"],\"X+8FMk\":[\"Current password doesn't match. Try again.\"],\"X1G9eY\":[\"Navigation Preview\"],\"XRuL0i\":[\"Create page\"],\"XrnWzN\":[\"已發佈!\"],\"XxJL62\":[\"Custom styling\"],\"Y+7JGK\":[\"創建頁面\"],\"Y75ho6\":[\"Other pages\"],\"YIix5Y\":[\"搜尋...\"],\"Z2lfX1\":[\"選擇圖示\"],\"Z3FXyt\":[\"載入中...\"],\"Z6NwTi\":[\"儲存為草稿\"],\"ZGs2so\":[\"Delete this collection permanently? Posts inside won't be removed.\"],\"ZM33w8\":[\"Visit Blog\"],\"ZQKLI1\":[\"危險區域\"],\"ZUpE9/\":[\"This is displayed at the bottom of all of your posts and pages. Markdown is supported.\"],\"ZhhOwV\":[\"引用\"],\"ZmUkwN\":[\"Add custom link to navigation\"],\"aAIQg2\":[\"外觀\"],\"aHTB7P\":[\"附加在您帖子上的補充內容\"],\"aT4jc4\":[\"無效的電子郵件或密碼\"],\"aaGV/9\":[\"New Link\"],\"ajBsih\":[\"發佈文章成功。\"],\"alKG0+\":[\"字型主題\"],\"an5hVd\":[\"Images\"],\"anibOb\":[\"關於這個部落格\"],\"b+/jO6\":[\"301(永久)\"],\"b+FyBD\":[\"Add page to navigation\"],\"b+JhJf\":[\"Max visible links\"],\"b4VwHs\":[\"未提供檔案。\"],\"bDqhXY\":[\"刪除失敗。請再試一次。\"],\"bG4pfW\":[\"Displayed above your blog posts on the home page. Also used as the meta description. Markdown supported.\"],\"bHYIks\":[\"登出\"],\"bcE7Mx\":[\"無法更新個人資料。\"],\"biOepV\":[\"← Back to home\"],\"bzSI52\":[\"丟棄\"],\"c9l3fG\":[\"Are you sure you want to delete this collection?\"],\"cSDy01\":[\"Custom CSS updated.\"],\"cTUByn\":[\"最新的在前\"],\"ccaIM9\":[\"更多連結\"],\"cnGeoo\":[\"刪除\"],\"d3LAu6\":[\"No media attached.\"],\"dEgA5A\":[\"取消\"],\"dStw5E\":[\"將現有頁面添加到您的導航中\"],\"dmCcPs\":[\"這是用於您的網站圖標和蘋果觸控圖標。為了獲得最佳效果,請上傳至少 180x180 像素的正方形圖片。\"],\"dtQNkT\":[\"選擇的字體主題無效。\"],\"dyJ1Db\":[\"Back to site\"],\"e6Jr7Q\":[\"← 返回收藏夾\"],\"eKSKaB\":[\"No posts match this filter.\"],\"ePK91l\":[\"編輯\"],\"eWLklq\":[\"引用\"],\"ebQKK7\":[\"Site\"],\"eneWvv\":[\"草稿\"],\"er8+x7\":[\"示範帳戶已預填。只需點擊登入。\"],\"etJ+C8\":[\"No redirects yet. Create one to forward traffic from old URLs.\"],\"etgedT\":[\"Emojis\"],\"f/bxrN\":[\"名稱是必填的。\"],\"f6e0Ry\":[\"Article\"],\"f8fH8W\":[\"Design\"],\"fDGOiR\":[\"密碼不匹配。\"],\"fG7BxZ\":[\"Upload images via the API: POST /api/upload with a file form field.\"],\"fqDzSu\":[\"評分\"],\"fttd2R\":[\"我的收藏\"],\"g3mKmM\":[\"Un-nav\"],\"g98BZB\":[\"Header links, featured\"],\"gDx5MG\":[\"Edit Link\"],\"gJH6Bs\":[\"替代文字改善可及性\"],\"gOwwEy\":[\"儲存空間未配置。\"],\"gj52YE\":[\"This collection is empty. Add posts from the editor.\"],\"gpaPhA\":[\"Helps screen readers describe the image\"],\"hDRU5q\":[\"找到 \",[\"0\"],\" 個結果\"],\"hG89Ed\":[\"Image\"],\"hGiKlz\":[\"Settings updated.\"],\"hQAbqI\":[\"尚未有頁面。創建您的第一個頁面以開始使用。\"],\"hWOZIv\":[\"請輸入您的新密碼。\"],\"hXzOVo\":[\"下一頁\"],\"he3ygx\":[\"複製\"],\"heSQoS\":[\"粘貼一個網址...\"],\"hmXTCY\":[\"選擇的主題無效。\"],\"hqwRVF\":[\"Couldn't update your profile. Try again in a moment.\"],\"hrL0Be\":[\"圖示(可選)\"],\"i0vDGK\":[\"排序順序\"],\"i6nDCI\":[\"Choose a new password.\"],\"iBc+/N\":[\"Custom URL path. Leave empty to use default /p/ID format.\"],\"iDAqU6\":[\"網址(可選)\"],\"iEUzMn\":[\"系統\"],\"iEqmSU\":[\"自訂 CSS 已成功儲存。\"],\"iH8pgl\":[\"返回\"],\"iPHeYN\":[\"Posting...\"],\"idD8Ev\":[\"Saved\"],\"ig4hg2\":[\"Let's set up your site.\"],\"jSRrXo\":[\"已發佈的頁面可以通過其標識符訪問。草稿不可見。\"],\"jUV7CU\":[\"上傳頭像\"],\"jVUmOK\":[\"支援Markdown\"],\"jpctdh\":[\"查看\"],\"jt/Ow/\":[\"posts\"],\"jvyYZG\":[\"你在想什麼...\"],\"k1ifdL\":[\"處理中...\"],\"kI1qVD\":[\"格式\"],\"kL1h6U\":[\"移除分隔線\"],\"kNiQp6\":[\"置頂\"],\"kPMIr+\":[\"給它一個標題...\"],\"kd7eBB\":[\"Create Link\"],\"kfcRb0\":[\"Avatar\"],\"kj6ppi\":[\"條目\"],\"kr39oD\":[\"No collections yet. Start one to organize posts by topic.\"],\"kyNTQ2\":[\"Emoji or icon name\"],\"l/6JHD\":[\"搜尋圖示...\"],\"l6ANt9\":[\"最低評價\"],\"lO1Oow\":[\"上傳成功!\"],\"m16K6M\":[\"Click to retry all\"],\"m16xKo\":[\"新增\"],\"mO5HMZ\":[\"All pages are already in navigation.\"],\"mTOYla\":[\"View all posts →\"],\"mcmuCe\":[\"Icons\"],\"mnkknn\":[\"Collection (optional)\"],\"n1ekoW\":[\"登入\"],\"n6QD94\":[\"最舊的在前\"],\"nFukaP\":[\"Wrong email or password. Check your credentials and try again.\"],\"nWRfmt\":[\"Typography\"],\"o21Y+P\":[\"條目\"],\"o4dofa\":[\"AUTH_SECRET 未配置\"],\"oJFOZk\":[\"Source Name (optional)\"],\"oKOOsY\":[\"顏色主題\"],\"oSiRP0\":[\"系統連結\"],\"oYPBa0\":[\"更新頁面\"],\"p2/GCq\":[\"確認密碼\"],\"pB0OKE\":[\"新分隔線\"],\"pI2MWS\":[\"Search pages…\"],\"pRhYH2\":[\"收藏中的帖子 (\",[\"count\"],\")\"],\"pZq3aX\":[\"上傳失敗。請再試一次。\"],\"pnve/d\":[\"個人資料已成功儲存。\"],\"psoxDF\":[\"That font theme isn't available. Pick another one.\"],\"q+hNag\":[\"集合\"],\"qMyM2u\":[\"Source URL (optional)\"],\"qdxmd4\":[\"Attached text\"],\"qiXmlF\":[\"添加媒體\"],\"qt89I8\":[\"草稿已保存。\"],\"quFPTj\":[\"Custom Slug (optional)\"],\"r1MpXi\":[\"Quiet\"],\"rFmBG3\":[\"顏色主題\"],\"rTP4rB\":[\"This file will be permanently removed from storage. Posts using it will show a broken link.\"],\"rdUucN\":[\"預覽\"],\"rlonmB\":[\"Couldn't delete. Try again in a moment.\"],\"rzNUSl\":[\"包含 1 則貼文的主題\"],\"sDGoxy\":[\"切換內建導航項目。啟用的項目會與頁面和連結一起顯示在您的導航中。\"],\"sGajR7\":[\"線程開始\"],\"smzF8S\":[\"顯示 \",[\"remainingCount\"],\" 個更多 \",[\"0\"]],\"ssqvZi\":[\"保存個人資料\"],\"sxkWRg\":[\"進階\"],\"t/YqKh\":[\"移除\"],\"t/gII1\":[\"Delete this post permanently? This can't be undone.\"],\"tKlWWY\":[\"Emoji\"],\"tQCppt\":[\"Couldn't save your post. Try again in a moment.\"],\"tb99Fd\":[\"Post published.\"],\"tfDRzk\":[\"保存\"],\"tfQNeI\":[\"No pages found.\"],\"tfrt7B\":[\"未配置任何重定向。\"],\"thAGHG\":[\"SEO settings updated.\"],\"tiq7kl\":[\"頁面 \",[\"page\"]],\"toJdZA\":[\"Reorder\"],\"u2f7vd\":[\"Site Description\"],\"u3wRF+\":[\"已發佈\"],\"u6Hp4N\":[\"Markdown\"],\"uAQUqI\":[\"狀態\"],\"vERlcd\":[\"個人資料\"],\"vGjmyl\":[\"Deleted\"],\"vXCC6J\":[\"Something doesn't look right. Check the form and try again.\"],\"vXIe7J\":[\"語言\"],\"vh0C9b\":[\"No navigation links yet. Add pages to navigation or create links.\"],\"vmQmHx\":[\"添加自定義 CSS 以覆蓋任何樣式。使用數據屬性,如 [data-page]、[data-post]、[data-format] 來針對特定元素。\"],\"vpSPA1\":[\"Auth secret is missing. Check your environment variables.\"],\"vzU4k9\":[\"新收藏集\"],\"w8Rv8T\":[\"標籤是必需的\"],\"wEF6Ix\":[\"目的地路徑或 URL\"],\"wK4H1r\":[\"Visit Site\"],\"wK4OTM\":[\"標題(選填)\"],\"wL3cK8\":[\"最新\"],\"wM5UXj\":[\"刪除媒體\"],\"wRR604\":[\"頁面\"],\"wc+17X\":[\"/* 您的自訂 CSS 在這裡 */\"],\"wdGjkd\":[\"No navigation links configured.\"],\"wja8aL\":[\"無標題\"],\"x+doid\":[\"圖片會自動優化:調整大小至最大 1920 像素,轉換為 WebP 格式,並去除元數據。\"],\"x0mzE0\":[\"創建你的第一篇帖子\"],\"x4RuFo\":[\"Back to home\"],\"xCWek4\":[\"File storage isn't set up. Check your server config.\"],\"xVvw1i\":[\"This reset link is no longer valid. Request a new one to continue.\"],\"xYilR2\":[\"媒體\"],\"y0R9F0\":[\"帖子已成功更新。\"],\"y28hnO\":[\"文章\"],\"y2o/Y0\":[\"This Link Has Expired\"],\"yQ2kGp\":[\"載入更多\"],\"ycM1Xg\":[\"No results. Try different keywords.\"],\"yjkELF\":[\"確認新密碼\"],\"yz7wBu\":[\"關閉\"],\"yzF66j\":[\"連結\"],\"z1U/Fh\":[\"Rating\"],\"z8ajIE\":[\"找到 1 個結果\"],\"zBFr9G\":[\"粘貼一篇長文章、AI 回應或任何文本...\\n\\nMarkdown 格式將被保留。\"],\"zH6KqE\":[\"Found \",[\"count\"],\" results\"],\"zennIg\":[\"URL安全識別碼(小寫、數字、連字符)。對於CJK標題,slug將在伺服器上自動生成。\"],\"zl926n\":[\"When off, visitors see your latest posts first\"],\"zucql+\":[\"菜單\"]}")as Messages;
|
|
@@ -173,9 +173,9 @@ describe("resolveConfig", () => {
|
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
it("resolves headerNavMaxVisible with default, DB override, and clamping", () => {
|
|
176
|
-
// Default is
|
|
176
|
+
// Default is 2
|
|
177
177
|
const config1 = resolveConfig(makeEnv(), {});
|
|
178
|
-
expect(config1.headerNavMaxVisible).toBe(
|
|
178
|
+
expect(config1.headerNavMaxVisible).toBe(2);
|
|
179
179
|
|
|
180
180
|
// DB override works
|
|
181
181
|
const config2 = resolveConfig(makeEnv(), { HEADER_NAV_MAX_VISIBLE: "5" });
|
|
@@ -231,14 +231,17 @@ describe("CreatePostSchema", () => {
|
|
|
231
231
|
).toThrow();
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
it("accepts
|
|
235
|
-
const
|
|
236
|
-
|
|
234
|
+
it("accepts visibility values", () => {
|
|
235
|
+
for (const v of ["listed", "featured", "unlisted"]) {
|
|
236
|
+
const result = CreatePostSchema.parse({ ...validPost, visibility: v });
|
|
237
|
+
expect(result.visibility).toBe(v);
|
|
238
|
+
}
|
|
237
239
|
});
|
|
238
240
|
|
|
239
|
-
it("
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
it("rejects invalid visibility", () => {
|
|
242
|
+
expect(() =>
|
|
243
|
+
CreatePostSchema.parse({ ...validPost, visibility: "hidden" }),
|
|
244
|
+
).toThrow();
|
|
242
245
|
});
|
|
243
246
|
|
|
244
247
|
it("accepts pinned as boolean", () => {
|
|
@@ -132,8 +132,8 @@ describe("slugify", () => {
|
|
|
132
132
|
expect(slugify("")).toBe("");
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
it("
|
|
136
|
-
expect(slugify("café & résumé")).toBe("
|
|
135
|
+
it("transliterates accented characters", () => {
|
|
136
|
+
expect(slugify("café & résumé")).toBe("cafe-and-resume");
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
it("converts Chinese characters to pinyin", () => {
|
|
@@ -32,7 +32,7 @@ function makePost(overrides: Partial<Post> = {}): Post {
|
|
|
32
32
|
id: 1,
|
|
33
33
|
format: "note",
|
|
34
34
|
status: "published",
|
|
35
|
-
|
|
35
|
+
visibility: "listed" as const,
|
|
36
36
|
pinned: 0,
|
|
37
37
|
path: null,
|
|
38
38
|
title: null,
|
|
@@ -236,31 +236,31 @@ describe("toPostView", () => {
|
|
|
236
236
|
expect(view.quoteText).toBe("Something wise");
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
it("maps format, status,
|
|
239
|
+
it("maps format, status, visibility, and pinned correctly", () => {
|
|
240
240
|
const view = toPostView(
|
|
241
241
|
makePostWithMedia({
|
|
242
242
|
format: "link",
|
|
243
243
|
status: "draft",
|
|
244
|
-
|
|
244
|
+
visibility: "featured",
|
|
245
245
|
pinned: 1,
|
|
246
246
|
}),
|
|
247
247
|
EMPTY_CTX,
|
|
248
248
|
);
|
|
249
249
|
expect(view.format).toBe("link");
|
|
250
250
|
expect(view.status).toBe("draft");
|
|
251
|
-
expect(view.
|
|
251
|
+
expect(view.visibility).toBe("featured");
|
|
252
252
|
expect(view.pinned).toBe(true);
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
-
it("
|
|
255
|
+
it("maps default visibility and pinned=0", () => {
|
|
256
256
|
const view = toPostView(
|
|
257
257
|
makePostWithMedia({
|
|
258
|
-
|
|
258
|
+
visibility: "listed",
|
|
259
259
|
pinned: 0,
|
|
260
260
|
}),
|
|
261
261
|
EMPTY_CTX,
|
|
262
262
|
);
|
|
263
|
-
expect(view.
|
|
263
|
+
expect(view.visibility).toBe("listed");
|
|
264
264
|
expect(view.pinned).toBe(false);
|
|
265
265
|
});
|
|
266
266
|
|
|
@@ -464,7 +464,7 @@ describe("toSearchResultView", () => {
|
|
|
464
464
|
id: 10,
|
|
465
465
|
format: "link",
|
|
466
466
|
status: "published",
|
|
467
|
-
|
|
467
|
+
visibility: "featured",
|
|
468
468
|
pinned: 0,
|
|
469
469
|
url: "https://example.com",
|
|
470
470
|
path: "my-link",
|
|
@@ -474,7 +474,7 @@ describe("toSearchResultView", () => {
|
|
|
474
474
|
const view = toSearchResultView(result, EMPTY_CTX);
|
|
475
475
|
expect(view.post.format).toBe("link");
|
|
476
476
|
expect(view.post.status).toBe("published");
|
|
477
|
-
expect(view.post.
|
|
477
|
+
expect(view.post.visibility).toBe("featured");
|
|
478
478
|
expect(view.post.pinned).toBe(false);
|
|
479
479
|
expect(view.post.url).toBe("https://example.com");
|
|
480
480
|
expect(view.post.permalink).toBe("/my-link");
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emoji Catalog
|
|
3
|
+
*
|
|
4
|
+
* Curated subset of emojis organized by category.
|
|
5
|
+
* Used by the collection icon picker alongside the icon catalog.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Curated emojis organized by category */
|
|
9
|
+
export const EMOJI_CATALOG: Record<string, string[]> = {
|
|
10
|
+
smileys: [
|
|
11
|
+
"😀",
|
|
12
|
+
"😊",
|
|
13
|
+
"😎",
|
|
14
|
+
"🥳",
|
|
15
|
+
"😍",
|
|
16
|
+
"🤩",
|
|
17
|
+
"😇",
|
|
18
|
+
"🤔",
|
|
19
|
+
"😏",
|
|
20
|
+
"🫠",
|
|
21
|
+
"🥹",
|
|
22
|
+
"😤",
|
|
23
|
+
"🤯",
|
|
24
|
+
"😴",
|
|
25
|
+
"🫡",
|
|
26
|
+
],
|
|
27
|
+
nature: [
|
|
28
|
+
"🌸",
|
|
29
|
+
"🌻",
|
|
30
|
+
"🌺",
|
|
31
|
+
"🌿",
|
|
32
|
+
"🍀",
|
|
33
|
+
"🌴",
|
|
34
|
+
"🌲",
|
|
35
|
+
"🍁",
|
|
36
|
+
"🌊",
|
|
37
|
+
"🔥",
|
|
38
|
+
"⭐",
|
|
39
|
+
"🌙",
|
|
40
|
+
"☀️",
|
|
41
|
+
"🌈",
|
|
42
|
+
"❄️",
|
|
43
|
+
],
|
|
44
|
+
animals: [
|
|
45
|
+
"🐶",
|
|
46
|
+
"🐱",
|
|
47
|
+
"🐻",
|
|
48
|
+
"🦊",
|
|
49
|
+
"🐼",
|
|
50
|
+
"🐨",
|
|
51
|
+
"🦁",
|
|
52
|
+
"🐸",
|
|
53
|
+
"🐧",
|
|
54
|
+
"🦋",
|
|
55
|
+
"🐝",
|
|
56
|
+
"🐬",
|
|
57
|
+
"🦅",
|
|
58
|
+
"🐢",
|
|
59
|
+
"🐙",
|
|
60
|
+
],
|
|
61
|
+
food: [
|
|
62
|
+
"☕",
|
|
63
|
+
"🍕",
|
|
64
|
+
"🍔",
|
|
65
|
+
"🍣",
|
|
66
|
+
"🍰",
|
|
67
|
+
"🍎",
|
|
68
|
+
"🍇",
|
|
69
|
+
"🍓",
|
|
70
|
+
"🥑",
|
|
71
|
+
"🌮",
|
|
72
|
+
"🍜",
|
|
73
|
+
"🧁",
|
|
74
|
+
"🍩",
|
|
75
|
+
"🫖",
|
|
76
|
+
"🍷",
|
|
77
|
+
],
|
|
78
|
+
activities: [
|
|
79
|
+
"⚽",
|
|
80
|
+
"🏀",
|
|
81
|
+
"🎾",
|
|
82
|
+
"🎮",
|
|
83
|
+
"🎯",
|
|
84
|
+
"🎨",
|
|
85
|
+
"🎭",
|
|
86
|
+
"🎵",
|
|
87
|
+
"🎬",
|
|
88
|
+
"📸",
|
|
89
|
+
"🏆",
|
|
90
|
+
"🥇",
|
|
91
|
+
"🎪",
|
|
92
|
+
"🧩",
|
|
93
|
+
"♟️",
|
|
94
|
+
],
|
|
95
|
+
travel: [
|
|
96
|
+
"✈️",
|
|
97
|
+
"🚀",
|
|
98
|
+
"🚗",
|
|
99
|
+
"🚲",
|
|
100
|
+
"⛵",
|
|
101
|
+
"🏠",
|
|
102
|
+
"🏔️",
|
|
103
|
+
"🏖️",
|
|
104
|
+
"🗺️",
|
|
105
|
+
"🌍",
|
|
106
|
+
"🗼",
|
|
107
|
+
"🏛️",
|
|
108
|
+
"⛺",
|
|
109
|
+
"🎡",
|
|
110
|
+
"🚂",
|
|
111
|
+
],
|
|
112
|
+
objects: [
|
|
113
|
+
"📚",
|
|
114
|
+
"📝",
|
|
115
|
+
"💡",
|
|
116
|
+
"🔑",
|
|
117
|
+
"💎",
|
|
118
|
+
"🎁",
|
|
119
|
+
"📦",
|
|
120
|
+
"🔔",
|
|
121
|
+
"💻",
|
|
122
|
+
"📱",
|
|
123
|
+
"⌚",
|
|
124
|
+
"🔧",
|
|
125
|
+
"🧲",
|
|
126
|
+
"💰",
|
|
127
|
+
"📌",
|
|
128
|
+
],
|
|
129
|
+
symbols: [
|
|
130
|
+
"❤️",
|
|
131
|
+
"💜",
|
|
132
|
+
"💙",
|
|
133
|
+
"💚",
|
|
134
|
+
"🧡",
|
|
135
|
+
"💛",
|
|
136
|
+
"✅",
|
|
137
|
+
"❌",
|
|
138
|
+
"⚡",
|
|
139
|
+
"💫",
|
|
140
|
+
"🎯",
|
|
141
|
+
"♻️",
|
|
142
|
+
"🔮",
|
|
143
|
+
"🏴",
|
|
144
|
+
"🚩",
|
|
145
|
+
],
|
|
146
|
+
};
|
package/src/lib/feed.ts
CHANGED
|
@@ -122,7 +122,7 @@ export function defaultSitemapRenderer(data: SitemapData): string {
|
|
|
122
122
|
.map((post) => {
|
|
123
123
|
const loc = `${siteUrl}${post.permalink}`;
|
|
124
124
|
const lastmod = post.updatedAt.split("T")[0];
|
|
125
|
-
const priority = post.featured ? "0.8" : "0.6";
|
|
125
|
+
const priority = post.visibility === "featured" ? "0.8" : "0.6";
|
|
126
126
|
|
|
127
127
|
return `
|
|
128
128
|
<url>
|
package/src/lib/media-helpers.ts
CHANGED
|
@@ -43,20 +43,21 @@ export function buildMediaMap(
|
|
|
43
43
|
r2PublicUrl,
|
|
44
44
|
s3PublicUrl,
|
|
45
45
|
);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
getMediaUrl(m.storageKey, publicUrl),
|
|
51
|
-
imageTransformUrl,
|
|
52
|
-
{
|
|
46
|
+
const mediaUrl = getMediaUrl(m.storageKey, publicUrl);
|
|
47
|
+
// Only apply image transforms for image MIME types
|
|
48
|
+
const previewUrl = m.mimeType.startsWith("image/")
|
|
49
|
+
? getImageUrl(mediaUrl, imageTransformUrl, {
|
|
53
50
|
width: 1200,
|
|
54
51
|
height: 768,
|
|
55
52
|
quality: 80,
|
|
56
53
|
format: "auto",
|
|
57
54
|
fit: "scale-down",
|
|
58
|
-
}
|
|
59
|
-
|
|
55
|
+
})
|
|
56
|
+
: mediaUrl;
|
|
57
|
+
return {
|
|
58
|
+
id: m.id,
|
|
59
|
+
url: mediaUrl,
|
|
60
|
+
previewUrl,
|
|
60
61
|
alt: m.alt,
|
|
61
62
|
blurhash: m.blurhash,
|
|
62
63
|
width: m.width,
|
package/src/lib/render.tsx
CHANGED
|
@@ -48,6 +48,9 @@ export function renderPublicPage(c: Context, options: RenderPublicPageOptions) {
|
|
|
48
48
|
// Use siteDescription as meta description fallback when not explicitly provided
|
|
49
49
|
const metaDescription = description || navData.siteDescription || undefined;
|
|
50
50
|
|
|
51
|
+
// Read favicon, version, and noindex from appConfig
|
|
52
|
+
const appConfig = c.get("appConfig");
|
|
53
|
+
|
|
51
54
|
const layoutProps: SiteLayoutProps = {
|
|
52
55
|
siteName: navData.siteName,
|
|
53
56
|
links: navData.links,
|
|
@@ -60,10 +63,8 @@ export function renderPublicPage(c: Context, options: RenderPublicPageOptions) {
|
|
|
60
63
|
showHeaderAvatar: navData.showHeaderAvatar,
|
|
61
64
|
siteFooterHtml: navData.siteFooterHtml,
|
|
62
65
|
sidebar,
|
|
66
|
+
uploadMaxFileSize: appConfig.uploadMaxFileSize,
|
|
63
67
|
};
|
|
64
|
-
|
|
65
|
-
// Read favicon, version, and noindex from appConfig
|
|
66
|
-
const appConfig = c.get("appConfig");
|
|
67
68
|
const faviconUrl = appConfig.siteAvatarUrl || undefined;
|
|
68
69
|
const faviconVersion = appConfig.faviconVersion || undefined;
|
|
69
70
|
const noindex = appConfig.noindex;
|
|
@@ -117,7 +117,7 @@ export function resolveConfig(
|
|
|
117
117
|
resolve("HEADER_NAV_MAX_VISIBLE", allSettings, env),
|
|
118
118
|
10,
|
|
119
119
|
);
|
|
120
|
-
return Math.max(0, Math.min(5, isNaN(parsed) ?
|
|
120
|
+
return Math.max(0, Math.min(5, isNaN(parsed) ? 2 : parsed));
|
|
121
121
|
})(),
|
|
122
122
|
timeZone: resolve("TIME_ZONE", allSettings, env),
|
|
123
123
|
siteFooter: resolve("SITE_FOOTER", allSettings, env),
|
|
@@ -133,6 +133,13 @@ export function resolveConfig(
|
|
|
133
133
|
s3PublicUrl,
|
|
134
134
|
imageTransformUrl,
|
|
135
135
|
|
|
136
|
+
// Upload (ENV only)
|
|
137
|
+
uploadMaxFileSize: parseInt(env.UPLOAD_MAX_FILE_SIZE ?? "500", 10) || 500,
|
|
138
|
+
|
|
139
|
+
// Summary extraction (ENV only)
|
|
140
|
+
summaryMaxParagraphs: parseInt(env.SUMMARY_MAX_PARAGRAPHS ?? "5", 10) || 5,
|
|
141
|
+
summaryMaxChars: parseInt(env.SUMMARY_MAX_CHARS ?? "500", 10) || 500,
|
|
142
|
+
|
|
136
143
|
// Pagination/Feed (ENV only)
|
|
137
144
|
pageSize: parseInt(env.PAGE_SIZE ?? "20", 10) || 20,
|
|
138
145
|
rssFeedLimit: parseInt(env.RSS_FEED_LIMIT ?? "50", 10) || 50,
|
package/src/lib/schemas.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { z } from "zod";
|
|
|
12
12
|
import {
|
|
13
13
|
FORMATS,
|
|
14
14
|
STATUSES,
|
|
15
|
+
VISIBILITIES,
|
|
15
16
|
SORT_ORDERS,
|
|
16
17
|
NAV_ITEM_TYPES,
|
|
17
18
|
MAX_MEDIA_ATTACHMENTS,
|
|
@@ -71,9 +72,7 @@ export const CreatePostSchema = z.object({
|
|
|
71
72
|
title: z.string().optional(),
|
|
72
73
|
body: z.string().optional(),
|
|
73
74
|
status: StatusSchema.optional(),
|
|
74
|
-
|
|
75
|
-
.union([z.boolean(), z.literal("on").transform(() => true)])
|
|
76
|
-
.optional(),
|
|
75
|
+
visibility: z.enum(VISIBILITIES).optional(),
|
|
77
76
|
pinned: z
|
|
78
77
|
.union([z.boolean(), z.literal("on").transform(() => true)])
|
|
79
78
|
.optional(),
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary Extraction from Tiptap JSON
|
|
3
|
+
*
|
|
4
|
+
* Extracts a plain-text summary from a Tiptap JSON document for use
|
|
5
|
+
* in feeds, meta descriptions, and article previews.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface TiptapNode {
|
|
9
|
+
type: string;
|
|
10
|
+
content?: TiptapNode[];
|
|
11
|
+
text?: string;
|
|
12
|
+
marks?: unknown[];
|
|
13
|
+
attrs?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Recursively extracts plain text from a Tiptap node, ignoring marks.
|
|
18
|
+
*/
|
|
19
|
+
function extractPlainText(node: TiptapNode): string {
|
|
20
|
+
if (node.type === "text") return node.text ?? "";
|
|
21
|
+
if (node.type === "hardBreak") return "\n";
|
|
22
|
+
if (!node.content) return "";
|
|
23
|
+
return node.content.map(extractPlainText).join("");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Extracts a plain-text summary from a Tiptap JSON body string.
|
|
28
|
+
*
|
|
29
|
+
* Algorithm:
|
|
30
|
+
* 1. If a `moreBreak` node is found, collect all paragraph text before it
|
|
31
|
+
* 2. Otherwise, accumulate paragraph nodes until limits are reached
|
|
32
|
+
* 3. Skip headings, images, code blocks, blockquotes, lists, horizontal rules
|
|
33
|
+
*
|
|
34
|
+
* @param bodyJson - Tiptap JSON string
|
|
35
|
+
* @param maxParagraphs - Maximum number of paragraphs to include
|
|
36
|
+
* @param maxChars - Maximum total character count
|
|
37
|
+
* @returns Plain text summary, or null if no paragraphs found
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const summary = extractSummary(body, 5, 500);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function extractSummary(
|
|
45
|
+
bodyJson: string,
|
|
46
|
+
maxParagraphs: number,
|
|
47
|
+
maxChars: number,
|
|
48
|
+
): string | null {
|
|
49
|
+
let doc: TiptapNode;
|
|
50
|
+
try {
|
|
51
|
+
doc = JSON.parse(bodyJson) as TiptapNode;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (doc.type !== "doc" || !doc.content) return null;
|
|
57
|
+
|
|
58
|
+
const nodes = doc.content;
|
|
59
|
+
|
|
60
|
+
// Check for moreBreak — collect paragraph text before it
|
|
61
|
+
const moreBreakIdx = nodes.findIndex((n) => n.type === "moreBreak");
|
|
62
|
+
if (moreBreakIdx !== -1) {
|
|
63
|
+
const paragraphs: string[] = [];
|
|
64
|
+
for (let i = 0; i < moreBreakIdx; i++) {
|
|
65
|
+
const node = nodes[i];
|
|
66
|
+
if (!node) continue;
|
|
67
|
+
if (node.type === "paragraph") {
|
|
68
|
+
const text = extractPlainText(node).trim();
|
|
69
|
+
if (text) paragraphs.push(text);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return paragraphs.length > 0 ? paragraphs.join("\n\n") : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// No moreBreak — accumulate paragraphs up to limits
|
|
76
|
+
const paragraphs: string[] = [];
|
|
77
|
+
let totalChars = 0;
|
|
78
|
+
|
|
79
|
+
for (const node of nodes) {
|
|
80
|
+
if (node.type !== "paragraph") continue;
|
|
81
|
+
|
|
82
|
+
const text = extractPlainText(node).trim();
|
|
83
|
+
if (!text) continue;
|
|
84
|
+
|
|
85
|
+
if (paragraphs.length >= maxParagraphs || totalChars >= maxChars) break;
|
|
86
|
+
|
|
87
|
+
paragraphs.push(text);
|
|
88
|
+
totalChars += text.length;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return paragraphs.length > 0 ? paragraphs.join("\n\n") : null;
|
|
92
|
+
}
|
package/src/lib/timeline.ts
CHANGED
|
@@ -51,6 +51,7 @@ export async function assembleTimeline(
|
|
|
51
51
|
const totalCount = await c.var.services.posts.count({
|
|
52
52
|
status: "published",
|
|
53
53
|
excludeReplies: true,
|
|
54
|
+
excludeUnlisted: true,
|
|
54
55
|
});
|
|
55
56
|
const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
|
|
56
57
|
|
|
@@ -58,6 +59,7 @@ export async function assembleTimeline(
|
|
|
58
59
|
const posts = await c.var.services.posts.list({
|
|
59
60
|
status: "published",
|
|
60
61
|
excludeReplies: true,
|
|
62
|
+
excludeUnlisted: true,
|
|
61
63
|
limit: pageSize,
|
|
62
64
|
offset,
|
|
63
65
|
});
|