@jant/core 0.3.44 → 0.3.46

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.
Files changed (62) hide show
  1. package/bin/commands/import-site.js +40 -39
  2. package/dist/app-CM7sb3xO.js +5 -0
  3. package/dist/{app-CtJDxZBb.js → app-DB-P66E5.js} +147 -203
  4. package/dist/client/.vite/manifest.json +3 -3
  5. package/dist/client/_assets/client-DDs6NzB3.css +2 -0
  6. package/dist/client/_assets/{client-auth-CXILhW1b.js → client-auth-BLCUje4M.js} +193 -174
  7. package/dist/client/_assets/{client-D95FNDg5.js → client-dSfWfMe9.js} +7 -7
  8. package/dist/{github-sync-7y_nTXx1.js → github-sync-CQ1x271f.js} +3 -0
  9. package/dist/index.js +4 -87
  10. package/dist/node.js +3 -3
  11. package/package.json +1 -1
  12. package/src/__tests__/import-site-command.test.ts +18 -0
  13. package/src/client/components/jant-compose-dialog.ts +94 -15
  14. package/src/client/components/jant-compose-editor.ts +11 -6
  15. package/src/client/components/jant-post-menu.ts +23 -5
  16. package/src/client/compose-bridge.ts +2 -1
  17. package/src/client/random-uuid.ts +23 -0
  18. package/src/client/toast.ts +29 -2
  19. package/src/client/upload-session.ts +1 -1
  20. package/src/db/migrations/0020_free_zaladane.sql +1 -0
  21. package/src/db/migrations/meta/0020_snapshot.json +2129 -0
  22. package/src/db/migrations/meta/_journal.json +7 -0
  23. package/src/db/migrations/pg/0018_red_warlock.sql +1 -0
  24. package/src/db/migrations/pg/meta/0018_snapshot.json +2739 -0
  25. package/src/db/migrations/pg/meta/_journal.json +7 -0
  26. package/src/db/pg/schema.ts +0 -30
  27. package/src/db/schema.ts +0 -39
  28. package/src/i18n/locales/public/en.po +10 -5
  29. package/src/i18n/locales/public/en.ts +1 -1
  30. package/src/i18n/locales/public/zh-Hans.po +10 -5
  31. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  32. package/src/i18n/locales/public/zh-Hant.po +10 -5
  33. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  34. package/src/index.ts +0 -3
  35. package/src/lib/__tests__/resolve-config.test.ts +4 -4
  36. package/src/lib/__tests__/startup-config.test.ts +27 -2
  37. package/src/lib/constants.ts +1 -0
  38. package/src/lib/github-sync-trigger.ts +7 -51
  39. package/src/lib/startup-config.ts +53 -6
  40. package/src/routes/api/github-sync.tsx +36 -14
  41. package/src/routes/pages/home.tsx +2 -0
  42. package/src/routes/pages/latest.tsx +2 -0
  43. package/src/runtime/__tests__/readiness.test.ts +34 -0
  44. package/src/runtime/readiness.ts +8 -4
  45. package/src/services/__tests__/collection.test.ts +13 -11
  46. package/src/services/github-sync.ts +6 -0
  47. package/src/styles/components.css +14 -0
  48. package/src/styles/ui.css +97 -0
  49. package/src/types/bindings.ts +0 -2
  50. package/src/types/config.ts +1 -1
  51. package/src/types/props.ts +2 -0
  52. package/src/ui/__tests__/font-themes.test.ts +2 -2
  53. package/src/ui/dash/settings/SettingsRootContent.tsx +17 -17
  54. package/src/ui/font-themes.ts +17 -17
  55. package/src/ui/pages/HomePage.tsx +18 -5
  56. package/dist/app-BI9bnCkO.js +0 -5
  57. package/dist/client/_assets/client-BQH7AQ24.css +0 -2
  58. package/src/lib/github-sync-queue-handler.ts +0 -69
  59. package/src/lib/github-sync-worker.ts +0 -72
  60. package/src/lib/job-queue-cf.ts +0 -18
  61. package/src/lib/job-queue-db.ts +0 -149
  62. package/src/lib/job-queue.ts +0 -35
@@ -1,5 +1,5 @@
1
1
  import { a as getSitePathPrefix, c as normalizePath, d as sanitizeUrl, f as slugify, g as toPublicPath, h as toPublicHref, i as getSiteOrigin, m as toAbsoluteSiteUrl, n as extractDisplayDomain, o as isFullUrl, p as stripSitePathPrefix, r as extractDomain, s as isSafeInternalRedirect, t as buildSiteUrl, u as normalizeSiteUrl, v as __exportAll } from "./url-umUptr5z.js";
2
- import { A as JANT_BRAND_PACK_FILENAME, B as getJantLogoFills, C as formatTime, D as toISOString, F as getJantBrandPackHref, G as arrayBufferToBase64, H as getJantPositiveLogoPngHref, I as getJantBundledAsset, K as base64ToUint8Array, L as getJantIconFilename, M as JANT_REPO_URL, N as getDefaultJantAppleTouchIconBytes, O as HOME_BRANDING_LINK_LABEL, P as getDefaultJantFaviconIcoBytes, R as getJantIconHref, S as formatRelativeTime, T as now, U as JANT_LOGO_PATH_DATA, V as getJantLogoHref, W as JANT_LOGO_VIEW_BOX, _ as getImageUrl, a as tiptapJsonToMarkdown, b as formatDate, c as render, d as extractSummary, f as extractSummaryHtml, g as escapeHtml, h as trimTiptapBody, i as createExportService, j as JANT_POSITIVE_LOGO_PNG_FILENAME, k as HOME_BRANDING_PREFIX, l as toPlainText, m as renderTiptapJson, n as createGitHubSyncService, o as markdownToTiptapJson, p as renderTiptapDocument, u as extractBodyText, v as getMediaUrl, w as formatYearMonth, x as formatRelativeAge, y as getPublicUrlForProvider, z as getJantLogoFilename } from "./github-sync-7y_nTXx1.js";
2
+ import { A as JANT_BRAND_PACK_FILENAME, B as getJantLogoFills, C as formatTime, D as toISOString, F as getJantBrandPackHref, G as arrayBufferToBase64, H as getJantPositiveLogoPngHref, I as getJantBundledAsset, K as base64ToUint8Array, L as getJantIconFilename, M as JANT_REPO_URL, N as getDefaultJantAppleTouchIconBytes, O as HOME_BRANDING_LINK_LABEL, P as getDefaultJantFaviconIcoBytes, R as getJantIconHref, S as formatRelativeTime, T as now, U as JANT_LOGO_PATH_DATA, V as getJantLogoHref, W as JANT_LOGO_VIEW_BOX, _ as getImageUrl, a as tiptapJsonToMarkdown, b as formatDate, c as render, d as extractSummary, f as extractSummaryHtml, g as escapeHtml, h as trimTiptapBody, i as createExportService, j as JANT_POSITIVE_LOGO_PNG_FILENAME, k as HOME_BRANDING_PREFIX, l as toPlainText, m as renderTiptapJson, n as createGitHubSyncService, o as markdownToTiptapJson, p as renderTiptapDocument, u as extractBodyText, v as getMediaUrl, w as formatYearMonth, x as formatRelativeAge, y as getPublicUrlForProvider, z as getJantLogoFilename } from "./github-sync-CQ1x271f.js";
3
3
  import { C as coalesceDisplayText, S as shouldUseSecureCookies, _ as getInternalAdminToken, a as getConfiguredSingleSiteUrl, b as getSiteResolutionMode, c as getDevApiToken, d as getHostedControlPlaneBaseUrl, f as getHostedControlPlaneDomainCheckSecret, g as getHostedControlPlaneSsoSecret, h as getHostedControlPlaneProviderLabel$1, i as getConfiguredSingleSitePathPrefix, l as getEnvString, m as getHostedControlPlaneInternalToken, n as getAuthSecret, o as getConfiguredStorageDriver, p as getHostedControlPlaneInternalBaseUrl, r as getConfiguredSingleSiteOrigin, s as getCorsOrigins, u as getGitHubAppConfig, v as getLocalStoragePath } from "./env-CgaH9Mut.js";
4
4
  import { a as listInstallationReposPage, n as getInstallation, o as searchInstallationRepos, t as buildInstallUrl } from "./github-app-WeadXMb8.js";
5
5
  import { r as parseRepoSlug, t as createGitHubClient } from "./github-api-BkRWnqMx.js";
@@ -1848,7 +1848,7 @@ var Hono = class extends Hono$1 {
1848
1848
  }
1849
1849
  //#endregion
1850
1850
  //#region src/i18n/locales/public/en.ts
1851
- var messages$3 = JSON.parse("{\"+4u2g6\":[\"A ready-made 1:1 PNG for decks, mockups, directories, and other square placements.\"],\"+DPYOZ\":[\"Add a link to your main RSS feed. Change what /feed returns in General.\"],\"+G8qqW\":[\"Collection saved.\"],\"+IJm1Z\":[\"Muted\"],\"+Irvp3\":[\"Everything on this page is ready to use for articles, launch posts, directories, and product coverage.\"],\"+Qaboy\":[\"Favicon\"],\"+fWu2O\":[\"A calmer, warmer accent makes the default theme feel quieter and more intentional.\"],\"+nHhRH\":[\"Use \",[\"brandColorName\"]],\"+siMqD\":[\"Journal\"],\"/DFKdU\":[\"Type the quote...\"],\"/PfPLc\":[\"Label (optional)\"],\"/PoNoq\":[\"Edit link\"],\"/Ui2OV\":[\"Use the reverse logo on dark backgrounds.\"],\"/Ybds4\":[\"Primary Jant logo for websites, docs, press coverage, and editorial layouts.\"],\"/rTz0M\":[\"Audio\"],\"0EcUWz\":[\"Discard changes?\"],\"0Lj7or\":[\"Save text attachment?\"],\"0XDp7X\":[\"Links should read clearly without glowing against the page.\"],\"0ieXE7\":[\"Highest rated\"],\"11h9eK\":[\"Includes\"],\"15++NM\":[\"Inline emphasis\"],\"1DBGsz\":[\"Notes\"],\"1NeeWI\":[\"Square assets for avatars, apps, browsers, and shared links\"],\"1THMr2\":[\"Brand pack\"],\"1njn7W\":[\"Light\"],\"2B7HLH\":[\"New post\"],\"2C7mSG\":[\"Collection link\"],\"2ETv7R\":[\"Tune color in a real reading context\"],\"2HbvFp\":[\"Real post components\"],\"2MXb5X\":[\"Field notes on quiet design\"],\"2koDOQ\":[\"Thread accents\"],\"2q/Q7x\":[\"Visibility\"],\"2sCqzD\":[\"Use this for websites, docs, articles, and other light or neutral surfaces.\"],\"33DClx\":[\"Link to your latest posts. If it comes before Featured, the homepage opens here.\"],\"3Cw1AI\":[\"Add Collection\"],\"3lJk5u\":[\"Keep the artwork unchanged.\"],\"3mdteM\":[\"before deciding whether the accent is carrying too much product energy.\"],\"3neqtf\":[\"Thread accent\"],\"3qkggm\":[\"Fullscreen\"],\"3vMdv3\":[\"This link is reserved. Choose something else.\"],\"3wKq0C\":[\"Couldn't save. Try again in a moment.\"],\"3xi01/\":[\"Look at the footer metadata last, to make sure the accent is not fighting the typography.\"],\"47iMgt\":[\"Editorial interfaces worth borrowing from\"],\"4D09NB\":[\"Link to your collections page\"],\"4HLTdq\":[\"without media\"],\"4J/OYU\":[\"Collection created.\"],\"4eiXo+\":[\"Leave blank to generate one automatically.\"],\"4pV0kE\":[\"Avatar-ready\"],\"51EYZX\":[\"without title\"],\"5dcjwM\":[\"Choose today or an earlier date, or leave it blank to publish now.\"],\"5pAjd8\":[\"Accent should feel present, not loud.\"],\"5sEkBi\":[\"Open raw asset\"],\"6UTABI\":[\"Collection order updated.\"],\"6WAK+2\":[\"Use current date\"],\"6Y4BBO\":[\"An abstract editorial layout in warm paper colors\"],\"6cjUDB\":[\"Brand assets\"],\"6lGV3K\":[\"Show less\"],\"6p0JeQ\":[\"to make sure both still feel like they belong to the same product.\"],\"6sVyMq\":[\"Add a URL before posting this link.\"],\"6yCv8j\":[\"Save these changes to the text attachment, discard them, or keep editing.\"],\"74kJNs\":[\"View earlier notes in this thread\"],\"7DvUqV\":[\"Read the page from top to bottom without looking at the swatches.\"],\"7aris6\":[\"March 15\"],\"7d1a0d\":[\"Public\"],\"7hYXO0\":[\"Use this on dark backgrounds, image-backed surfaces, and any placement where the green logo would lose contrast.\"],\"7kMW54\":[\"Open raw SVG\"],\"7nGhhM\":[\"What's on your mind?\"],\"7vhWI8\":[\"New Password\"],\"87a/t/\":[\"Label\"],\"8Btgys\":[\"Draft deleted.\"],\"8WX0J+\":[\"Your thoughts (optional)\"],\"8ZsakT\":[\"Password\"],\"8bpHix\":[\"Couldn't create your account. Check the details and try again.\"],\"8eC78s\":[\"A calmer accent makes\"],\"8tM8+a\":[\"Save as draft\"],\"90IRF2\":[\"This article is here to answer a specific question: does the default accent still feel calm once it has to carry a full reading experience?\"],\"9SHZas\":[\"Shows 'Settings' when logged in, 'Sign in' when logged out\"],\"9aloPG\":[\"References\"],\"9dr9Nh\":[\"Open external link\"],\"9qWoxS\":[\"Feed\"],\"A1D8Yt\":[\"What the accent should do\"],\"A1taO8\":[\"Search\"],\"A2Vg/u\":[\"Navigation and reading states\"],\"AjHkcv\":[\"Default preview image for social shares and link unfurls.\"],\"AyHO4m\":[\"What's this collection about?\"],\"B1FFMj\":[\"Download Brand Pack\"],\"B495Gs\":[\"Archive\"],\"BdjLtf\":[\"thread\"],\"Bmaby2\":[\"All formats\"],\"C+9df9\":[\"Quoted or highlighted passages should feel like annotations, not warnings.\"],\"C0/57J\":[\"This is the last part of the collection link.\"],\"CAh1km\":[\"collections\"],\"CH3bgf\":[\"RSS feed for this view\"],\"CT7H2e\":[\"Link to your featured posts. If it comes before Latest, the homepage opens here.\"],\"CmBCXY\":[\"Link updated.\"],\"D4em/+\":[\"Logos\"],\"DHhJ7s\":[\"Previous\"],\"DJLY+/\":[\" and try again.\"],\"DOx286\":[\"Draft restored.\"],\"DPfwMq\":[\"Done\"],\"DSJXZM\":[\"Enter a valid date.\"],\"DYlMYF\":[\"Built-in background\"],\"DoJzLz\":[\"Collections\"],\"Du2B9f\":[\"The default accent should support reading first. Start by comparing it against the\"],\"DxwUcG\":[\"Read the palette as content first\"],\"E3NcGH\":[\"Square logo PNG\"],\"EEYbdt\":[\"Publish\"],\"EGwzOK\":[\"Complete Setup\"],\"EHWwm1\":[\"The default accent should feel written, not branded.\"],\"EO3I6h\":[\"Upload didn't go through. Try again in a moment.\"],\"EQNPYo\":[\"Featured on \",[\"date\"],\" at \",[\"time\"]],\"EQtz4D\":[\"Open a few links and check whether they still feel native to the page.\"],\"EU3tBD\":[\"Link removed.\"],\"EetoJL\":[\"Guide the eye without taking over the layout.\"],\"Eiv3bO\":[\"Buttons can stay steady, but links, thread markers, and subtle emphasis should feel closer to ink on paper than dashboard chrome.\"],\"ElTnWL\":[\"Published on\"],\"EmQw8O\":[\"If this article still feels like a page you want to keep reading, the palette is probably close.\"],\"EsJdRp\":[\"Save theme\"],\"FESYvt\":[\"Describe this for people with visual impairments...\"],\"FEr96N\":[\"Theme\"],\"FGySZL\":[\"The default accent works best when it reads like a fountain-pen underline. Compare it against the\"],\"FM+KeU\":[\"No drafts yet. Save a draft to find it here.\"],\"Fdv5k7\":[\"What to look for while tuning it\"],\"FkMol5\":[\"Featured\"],\"Fxf4jq\":[\"Description (optional)\"],\"G2u/aQ\":[\"Download official Jant logos, icons, and preview assets.\"],\"GBJzTZ\":[\"Archive\"],\"GX2VMa\":[\"Create your admin account.\"],\"GY/1J4\":[\"Jant fallback canary string\"],\"GbIOhd\":[\"Reply quietly\"],\"GiRWtR\":[\"Why the default accent should feel written, not branded\"],\"GkpIs2\":[\"Remove this link from Collections? The destination won't change.\"],\"GorKul\":[\"Welcome to Jant\"],\"GxkJXS\":[\"Uploading...\"],\"H29JXm\":[\"+ ALT\"],\"H4lgRd\":[\"Authentication isn't set up. Check your server config.\"],\"HFPGej\":[\"No threads match these filters. Try adjusting your selection or clear all filters.\"],\"HG79RB\":[\"Post as Private\"],\"HNEHJP\":[\"Demo credentials are pre-filled — hit Sign In to continue.\"],\"HbAIQc\":[\"A reference link for checking whether the accent feels editorial instead of promotional.\"],\"Ht1V3q\":[\"For the same reason, inline code should stay neutral. Something like theme.siteAccent = soften(green, 12%) should not suddenly become the loudest thing on the page.\"],\"I22eN0\":[\"Shared links\"],\"I6zLrz\":[\"Use these when you need a transparent square logo, a shaped tile with a built-in background, a browser icon, or a default preview image.\"],\"ICsA6P\":[\"You have unsaved changes\"],\"IUX7p+\":[\"White logo on the Jant green rounded tile for app icon mockups, touch icons, directory listings, and other square placements that should feel softer.\"],\"IagCbF\":[\"URL\"],\"IjnQHI\":[\"with title\"],\"ImOQa9\":[\"Reply\"],\"IsI3kE\":[\"Nothing here yet. Add posts to one of these collections to fill this view.\"],\"J+2Rls\":[\"Leave blank to publish now. Use an earlier date when importing older posts.\"],\"J4tAHl\":[\"Headings should keep their hierarchy even when the accent gets softer.\"],\"JYj5R2\":[\"Browse files\"],\"JcD7qf\":[\"More actions\"],\"JqJ5Xv\":[\"Latest\"],\"JuN5GC\":[\"No file selected. Choose a file to upload.\"],\"JwLPQ/\":[\"This sign-in link has expired. Return to \"],\"KOqvXP\":[\"Do not recolor, stretch, rotate, outline, or add effects to the logo.\"],\"KbS2K9\":[\"Reset Password\"],\"KdSsVl\":[\"Author (optional)\"],\"Khu3PV\":[\"Publish settings\"],\"KiJn9B\":[\"Note\"],\"KlZ+t+\":[\"%name% + %count% more\"],\"KsvRin\":[\"Hide from Latest\"],\"KzmC5L\":[\"Controls\"],\"L7svJg\":[\"Reading\"],\"Lbkbwy\":[\"A quote card for judging accent color against softer, citation-heavy content.\"],\"LcvzvX\":[\"Tap to retry\"],\"LkA8jz\":[\"Add alt text\"],\"LxRg6f\":[\"live theme controls\"],\"M4tzVU\":[\"Latest posts\"],\"M8kJqa\":[\"Drafts\"],\"MHrjPM\":[\"Title\"],\"MILa7n\":[\"Square tile\"],\"MSc/Yq\":[\"Do you want to publish your changes or discard them?\"],\"Mc7+6G\":[\"Enter a valid URL starting with http://, https://, or mailto:.\"],\"MdMyne\":[\"Source link (optional)\"],\"MiMY3Q\":[\"Apple touch icon\"],\"MiyoI7\":[\"default note sample\"],\"MqghUt\":[\"Search posts...\"],\"Myqkib\":[\"Create a collection to get started.\"],\"N8UzTV\":[\"Replies\"],\"NAFbuE\":[\"Search snippet\"],\"NH9Z1R\":[\"Start here\"],\"NqsRbb\":[\"Jant logo\"],\"NvXuWk\":[\"Won't move the thread to the top of latest.\"],\"O1367B\":[\"All collections\"],\"O3oNi5\":[\"Email\"],\"OEdMhi\":[\"The best default color is the one you notice only after reading for a while.\"],\"OEt/to\":[\"Guidelines\"],\"OJxdgi\":[\"Keep this link under 200 characters.\"],\"OaoJcz\":[\"Social preview\"],\"OmfDbR\":[\"Site accent\"],\"Ovks1h\":[\"A softer blue feels more like ink than product chrome.\"],\"P/sHNL\":[\"Use this page to judge buttons, links, cards, forms, thread accents, and quiet surfaces before changing a theme globally.\"],\"Q2mGA7\":[\"Clear filter\"],\"QBqVyM\":[\"Home screen icon for iPhone and iPad shortcuts.\"],\"QebAts\":[\"Link added.\"],\"Qgbxdw\":[\"Designing a calmer default accent for Jant\"],\"Qn9Ao8\":[\"Circle tile\"],\"QyDt3L\":[\"File uploaded.\"],\"R5CMuK\":[\"Jant looks best when the accent feels editorial. Buttons can stay sturdy, but inline emphasis should feel like a pen mark, not a dashboard highlight.\"],\"R8AthW\":[\"Divider\"],\"R9Khdg\":[\"Auto\"],\"RAv3u7\":[\"Compare it against the theme controls\"],\"ROa4Ti\":[\"Interfaces for reading should guide the eye, not keep asking for attention.\"],\"RZOWDv\":[\"Add a custom shortcut to any page or site.\"],\"RdmNnl\":[\"Browser tab\"],\"RfGczC\":[\"Square logo\"],\"Rj01Fz\":[\"Links\"],\"S37om9\":[\"Included assets\"],\"S8NCfs\":[\"Save to drafts to edit and post at a later time.\"],\"SJGVAw\":[\"Feel editorial and slightly quieter.\"],\"SJmfuf\":[\"Site Name\"],\"SaNhJE\":[\"feel deliberate instead of washed out.\"],\"SpTWH3\":[\"Download SVG\"],\"SvRuJt\":[\"Field Notes on Interface Tone\"],\"T/R+Qz\":[\"Primary\"],\"TNZKpI\":[\"Danger\"],\"TvaTxw\":[\"Doesn't appear in Latest. Still appears in collections you add it to.\"],\"UIMXHD\":[\"Remove Divider\"],\"UaZwcz\":[\"More options are available after you create it.\"],\"Uc5y7o\":[\"Choose the standard logo for websites, docs, directories, and editorial layouts.\"],\"V18SVO\":[\"Use the logo on light backgrounds.\"],\"V4WsyL\":[\"Add Link\"],\"VCA6B2\":[\"These are actual feed components with real footers, summaries, and inline links. Use this section to judge whether the theme still feels calm once it is applied to realistic content.\"],\"VNqFYa\":[\"Loading post...\"],\"WCOanD\":[\"This reference is useful because it treats links and citations as part of the reading rhythm. Keep that in mind while tuning the\"],\"WbIbzR\":[\"Checking link...\"],\"WcWS//\":[\"Download file\"],\"WhsN3P\":[\"A good default accent in Jant should feel like editorial structure, not product branding. That means links, emphasis, and thread cues can be visible without turning the page into UI chrome.\"],\"Wn+/rH\":[\"Transparent square\"],\"WpXcBJ\":[\"Nothing here yet.\"],\"XU7b+L\":[\"Primary logo files\"],\"XV1mAn\":[\"Only visible when signed in.\"],\"XrnWzN\":[\"Published!\"],\"YIix5Y\":[\"Search...\"],\"YUglt2\":[\"Generating a link...\"],\"YXiA6e\":[\"Primary button\"],\"Ygx3Yl\":[\"Small browser icon used in tabs and bookmarks.\"],\"Z6NwTi\":[\"Save as Draft\"],\"ZGs2so\":[\"Delete this collection permanently? Posts inside won't be removed.\"],\"ZV5ykW\":[\"Download PNG\"],\"ZhhOwV\":[\"Quote\"],\"ZmSeP+\":[\"Save to drafts?\"],\"ZxFuun\":[[\"count\",\"plural\",{\"one\":[\"Found \",\"#\",\" result\"],\"other\":[\"Found \",\"#\",\" results\"]}]],\"a5j82I\":[\"No collections match that search. Try a different name.\"],\"aHTB7P\":[\"Supplementary content attached to your post\"],\"aMEyv0\":[\"Stay sturdy and readable.\"],\"aN6wx0\":[\"Nothing in Featured yet. Mark a post as featured to show it here.\"],\"aYpXKS\":[\"and checking whether the accent is guiding attention or pulling too hard.\"],\"aaGV/9\":[\"New Link\"],\"af+9p6\":[\"Quiet metadata\"],\"an5hVd\":[\"Images\"],\"ao77hr\":[[\"count\",\"plural\",{\"one\":[\"#\",\" hidden post\"],\"other\":[\"#\",\" hidden posts\"]}]],\"auFlOr\":[\"Icons and previews\"],\"avuFKG\":[\"threads\"],\"bFpC86\":[\"Everything in one download\"],\"bGtMpA\":[\"Add a label and URL.\"],\"bHOiy1\":[\"Password changes are off in demo mode. Sign in with the shared demo credentials.\"],\"bbdNeX\":[\"Sign in\"],\"bfCbdi\":[\"Current post\"],\"bkBJmZ\":[\"This is useful as a color check because it puts the accent next to quotation styling, metadata, and a quieter explanatory paragraph. Compare it back to the\"],\"bzSI52\":[\"Discard\"],\"c2JRUS\":[\"Generate automatically\"],\"cIoW7X\":[\"Inline link\"],\"cTUByn\":[\"Newest first\"],\"cb7FR8\":[\"White logo on the Jant green square tile for platforms and layouts that expect a true edge-to-edge square.\"],\"cgmi4V\":[\"Delete Draft\"],\"cnGeoo\":[\"Delete\"],\"d+F4pf\":[\"The image should sit quietly inside the article instead of feeling like a card preview.\"],\"d/o/BH\":[\"Couldn't publish. Saved as draft.\"],\"dD7NPy\":[\"Outline\"],\"dEgA5A\":[\"Cancel\"],\"dUsGbd\":[\"The right accent should disappear into the writing until you need it.\"],\"dXoieq\":[\"Summary\"],\"dYKrp3\":[\"Hidden from Latest\"],\"dbUuAj\":[\"Appears in Latest.\"],\"df4a/r\":[\"Couldn't load this post. Try again.\"],\"ePK91l\":[\"Edit\"],\"eWLklq\":[\"Quotes\"],\"f4MAoA\":[\"Some uploads failed. Saved as draft.\"],\"f5s9EI\":[\"Press N to write\"],\"f6Hub0\":[\"Sort\"],\"f8fH8W\":[\"Design\"],\"fD+f7T\":[\"RSS feed\"],\"fKrDxS\":[\"Brand tile\"],\"fMPkxb\":[\"Show more\"],\"fqDzSu\":[\"Rate\"],\"fttd2R\":[\"My Collection\"],\"gCcxP/\":[\"Threads can include up to \",[\"count\"],\" posts.\"],\"gFdWl+\":[\"A long-form article sample for checking the default palette in a true reading context.\"],\"gNKz6Z\":[\"Collection deleted.\"],\"gXH9r/\":[\"Open raw PNG\"],\"gj52YE\":[\"This collection is empty. Add posts from the editor.\"],\"gpaPhA\":[\"Helps screen readers describe the image\"],\"h5RcXU\":[\"Post hidden\"],\"hLlWo5\":[\"A few simple rules.\"],\"hWpUeY\":[\"Auto link\"],\"hXzOVo\":[\"Next\"],\"heSQoS\":[\"Paste a URL...\"],\"hrkGms\":[\"Search\"],\"i0vDGK\":[\"Sort Order\"],\"i5+Y7d\":[\"Download the official Jant logo, icons, and preview files.\"],\"i6kro6\":[\"Edit custom link\"],\"i6nDCI\":[\"Choose a new password.\"],\"iG7KNr\":[\"Logo\"],\"iH8pgl\":[\"Back\"],\"ilSmIt\":[\"Hard edge\"],\"iu7tUI\":[\"Breadcrumb\"],\"jAXE5p\":[\"Reverse logo\"],\"jAqB/k\":[\"Post privately\"],\"jQflRT\":[\"This uses the real single-post detail rendering with a longer article, inline image, tables, lists, quotes, and code. The content column stays at the same width as the live site.\"],\"jd+8Mm\":[\"Social preview image\"],\"jdJOV1\":[\"Settings\"],\"ji7oVU\":[\"Edit post\"],\"jpctdh\":[\"View\"],\"jvyYZG\":[\"What's on your mind...\"],\"k3Iw35\":[\"Switch to the white logo when the standard green version would lose contrast.\"],\"kPMIr+\":[\"Give it a title...\"],\"kj6ppi\":[\"entry\"],\"kr39oD\":[\"No collections yet. Start one to organize posts by topic.\"],\"kzvWob\":[\"Link to the post archive\"],\"laT1IJ\":[\"iOS home screen\"],\"lb+Xwx\":[\"Custom link\"],\"m16xKo\":[\"Add\"],\"mKT7g0\":[\"Text attachment\"],\"mc/vLq\":[\"This link is already in use. Choose something else.\"],\"muKqfV\":[\"Featured\"],\"n1ekoW\":[\"Sign In\"],\"n3ReIn\":[\"Collections\"],\"n6QD94\":[\"Oldest first\"],\"nFukaP\":[\"Wrong email or password. Check your credentials and try again.\"],\"nV6twc\":[\"Organize\"],\"nd8Puv\":[\"White logo on the Jant green circle for profile images, badges, and other round placements where you want a ready-made asset.\"],\"ndrEYW\":[\"When the accent is slightly warmer and less literal, the whole page feels more like a writing space and less like product UI.\"],\"o21Y+P\":[\"entries\"],\"oO0hKx\":[[\"count\",\"plural\",{\"one\":[\"#\",\" more post\"],\"other\":[\"#\",\" more posts\"]}]],\"oTu7Wt\":[\"Combined Collections\"],\"ode0+L\":[\"Theme sample\"],\"ogssnn\":[\"with media\"],\"ovBPCi\":[\"Default\"],\"p1Z67P\":[\"When primary is too rigid, the whole page starts reading like product UI instead of writing space.\"],\"p2/GCq\":[\"Confirm Password\"],\"pB0OKE\":[\"New Divider\"],\"pBHx39\":[\"Dark backgrounds\"],\"pVrU5x\":[\"If this page feels too branded, the first place to soften is the default theme’s site accent, not the border or body text.\"],\"pvnfJD\":[\"Dark\"],\"q+hNag\":[\"Collection\"],\"q5YRzz\":[\"Color check\"],\"q8RviX\":[\"Titled\"],\"qcawwg\":[\"Publish now\"],\"qiN9NB\":[\"Surface\"],\"qt89I8\":[\"Draft saved.\"],\"quvfGs\":[\"instead of judging it as an isolated swatch.\"],\"r7kcaA\":[\"Drag collections, links, and dividers into the order you want.\"],\"rA2TFI\":[\"Switch the palette and mode without opening settings or changing the active site theme.\"],\"rV8ZnP\":[\"Edit publish date\"],\"rdUucN\":[\"Preview\"],\"s8G5Or\":[\"This upload would exceed your shared hosted media limit. Remove files or upgrade storage to continue.\"],\"s9gHf5\":[\"your-post-link\"],\"sER+bs\":[\"Files\"],\"sQpDn6\":[\"Exit fullscreen\"],\"sgr2wQ\":[\"collection\"],\"slujBW\":[\"Use lowercase letters, numbers, and hyphens only.\"],\"syiAKf\":[\"note treatment\"],\"t42hIC\":[\"Everything most people need is in one ZIP.\"],\"tCctex\":[\"The brand pack includes SVG logos, a transparent square PNG, rounded, square, and circle tiles, plus favicon, Apple touch icon, and the default social preview image.\"],\"tKlWWY\":[\"Emoji\"],\"tSWVu5\":[\"Published on \",[\"date\"],\" at \",[\"time\"]],\"tfDRzk\":[\"Save\"],\"tgSBSE\":[\"Remove Link\"],\"uowbPn\":[\"Remove attachment\"],\"v3E8iS\":[\"A practical checklist\"],\"vSJd18\":[\"Video\"],\"vSYKYI\":[\"Main feed\"],\"vXCC6J\":[\"Something doesn't look right. Check the form and try again.\"],\"vcpc5o\":[\"Close menu\"],\"vdFnYM\":[\"Reset link\"],\"vdvpU5\":[\"/archive?format=quote or https://example.com\"],\"vgpfCi\":[\"Save draft\"],\"vpSPA1\":[\"Auth secret is missing. Check your environment variables.\"],\"vzU4k9\":[\"New Collection\"],\"w0Emel\":[\"Suggested link\"],\"w6mlns\":[\"Article detail page\"],\"wJ+GRy\":[\"All visibility\"],\"wL3cK8\":[\"Latest\"],\"wja8aL\":[\"Untitled\"],\"wlnK1t\":[\"A single ZIP with the main logo, reverse logo, square PNG, rounded, square, and circle tiles, plus favicon, Apple touch icon, and social preview image.\"],\"wm3Zlr\":[\"All years\"],\"xCWek4\":[\"File storage isn't set up. Check your server config.\"],\"xVrkxi\":[\"quiet design\"],\"xVvw1i\":[\"This reset link is no longer valid. Request a new one to continue.\"],\"xYilR2\":[\"Media\"],\"xeiujy\":[\"Text\"],\"xhTx3y\":[\"Choose the standard logo for most placements and the reverse logo when you need more contrast.\"],\"y28hnO\":[\"Post\"],\"y2o/Y0\":[\"This Link Has Expired\"],\"yGZVl1\":[\"More\"],\"yQ2kGp\":[\"Load more\"],\"yUtAh2\":[\"New Thread\"],\"ycM1Xg\":[\"No results. Try different keywords.\"],\"ynMAhG\":[\"Default logo\"],\"yzF66j\":[\"Link\"],\"zBFr9G\":[\"Paste a long article, AI response, or any text...\\n\\nMarkdown formatting will be preserved.\"],\"zJDAbh\":[\"Don't save\"],\"zcDmsG\":[\"Featured posts\"],\"zoK+eO\":[\"Add a title before posting this link.\"],\"zucql+\":[\"Menu\"],\"zwBp5t\":[\"Private\"]}");
1851
+ var messages$3 = JSON.parse("{\"+4u2g6\":[\"A ready-made 1:1 PNG for decks, mockups, directories, and other square placements.\"],\"+DPYOZ\":[\"Add a link to your main RSS feed. Change what /feed returns in General.\"],\"+G8qqW\":[\"Collection saved.\"],\"+IJm1Z\":[\"Muted\"],\"+Irvp3\":[\"Everything on this page is ready to use for articles, launch posts, directories, and product coverage.\"],\"+Qaboy\":[\"Favicon\"],\"+fWu2O\":[\"A calmer, warmer accent makes the default theme feel quieter and more intentional.\"],\"+nHhRH\":[\"Use \",[\"brandColorName\"]],\"+siMqD\":[\"Journal\"],\"/DFKdU\":[\"Type the quote...\"],\"/PfPLc\":[\"Label (optional)\"],\"/PoNoq\":[\"Edit link\"],\"/Ui2OV\":[\"Use the reverse logo on dark backgrounds.\"],\"/Ybds4\":[\"Primary Jant logo for websites, docs, press coverage, and editorial layouts.\"],\"/rTz0M\":[\"Audio\"],\"0EcUWz\":[\"Discard changes?\"],\"0Lj7or\":[\"Save text attachment?\"],\"0XDp7X\":[\"Links should read clearly without glowing against the page.\"],\"0ieXE7\":[\"Highest rated\"],\"11h9eK\":[\"Includes\"],\"15++NM\":[\"Inline emphasis\"],\"1DBGsz\":[\"Notes\"],\"1NeeWI\":[\"Square assets for avatars, apps, browsers, and shared links\"],\"1THMr2\":[\"Brand pack\"],\"1njn7W\":[\"Light\"],\"2B7HLH\":[\"New post\"],\"2C7mSG\":[\"Collection link\"],\"2ETv7R\":[\"Tune color in a real reading context\"],\"2HbvFp\":[\"Real post components\"],\"2MXb5X\":[\"Field notes on quiet design\"],\"2koDOQ\":[\"Thread accents\"],\"2q/Q7x\":[\"Visibility\"],\"2sCqzD\":[\"Use this for websites, docs, articles, and other light or neutral surfaces.\"],\"33DClx\":[\"Link to your latest posts. If it comes before Featured, the homepage opens here.\"],\"3Cw1AI\":[\"Add Collection\"],\"3lJk5u\":[\"Keep the artwork unchanged.\"],\"3mdteM\":[\"before deciding whether the accent is carrying too much product energy.\"],\"3neqtf\":[\"Thread accent\"],\"3qkggm\":[\"Fullscreen\"],\"3vMdv3\":[\"This link is reserved. Choose something else.\"],\"3wKq0C\":[\"Couldn't save. Try again in a moment.\"],\"3xi01/\":[\"Look at the footer metadata last, to make sure the accent is not fighting the typography.\"],\"47iMgt\":[\"Editorial interfaces worth borrowing from\"],\"4D09NB\":[\"Link to your collections page\"],\"4HLTdq\":[\"without media\"],\"4J/OYU\":[\"Collection created.\"],\"4eiXo+\":[\"Leave blank to generate one automatically.\"],\"4pV0kE\":[\"Avatar-ready\"],\"51EYZX\":[\"without title\"],\"5dcjwM\":[\"Choose today or an earlier date, or leave it blank to publish now.\"],\"5pAjd8\":[\"Accent should feel present, not loud.\"],\"5sEkBi\":[\"Open raw asset\"],\"6UTABI\":[\"Collection order updated.\"],\"6WAK+2\":[\"Use current date\"],\"6Y4BBO\":[\"An abstract editorial layout in warm paper colors\"],\"6cjUDB\":[\"Brand assets\"],\"6lGV3K\":[\"Show less\"],\"6p0JeQ\":[\"to make sure both still feel like they belong to the same product.\"],\"6sVyMq\":[\"Add a URL before posting this link.\"],\"6yCv8j\":[\"Save these changes to the text attachment, discard them, or keep editing.\"],\"74kJNs\":[\"View earlier notes in this thread\"],\"7DvUqV\":[\"Read the page from top to bottom without looking at the swatches.\"],\"7aris6\":[\"March 15\"],\"7d1a0d\":[\"Public\"],\"7hYXO0\":[\"Use this on dark backgrounds, image-backed surfaces, and any placement where the green logo would lose contrast.\"],\"7kMW54\":[\"Open raw SVG\"],\"7nGhhM\":[\"What's on your mind?\"],\"7vhWI8\":[\"New Password\"],\"87a/t/\":[\"Label\"],\"8Btgys\":[\"Draft deleted.\"],\"8WX0J+\":[\"Your thoughts (optional)\"],\"8ZsakT\":[\"Password\"],\"8bpHix\":[\"Couldn't create your account. Check the details and try again.\"],\"8eC78s\":[\"A calmer accent makes\"],\"8tM8+a\":[\"Save as draft\"],\"90IRF2\":[\"This article is here to answer a specific question: does the default accent still feel calm once it has to carry a full reading experience?\"],\"9SHZas\":[\"Shows 'Settings' when logged in, 'Sign in' when logged out\"],\"9aloPG\":[\"References\"],\"9dr9Nh\":[\"Open external link\"],\"9qWoxS\":[\"Feed\"],\"A1D8Yt\":[\"What the accent should do\"],\"A1taO8\":[\"Search\"],\"A2Vg/u\":[\"Navigation and reading states\"],\"AjHkcv\":[\"Default preview image for social shares and link unfurls.\"],\"AyHO4m\":[\"What's this collection about?\"],\"B1FFMj\":[\"Download Brand Pack\"],\"B495Gs\":[\"Archive\"],\"BdjLtf\":[\"thread\"],\"Bmaby2\":[\"All formats\"],\"C+9df9\":[\"Quoted or highlighted passages should feel like annotations, not warnings.\"],\"C0/57J\":[\"This is the last part of the collection link.\"],\"CAh1km\":[\"collections\"],\"CH3bgf\":[\"RSS feed for this view\"],\"CT7H2e\":[\"Link to your featured posts. If it comes before Latest, the homepage opens here.\"],\"CmBCXY\":[\"Link updated.\"],\"D4em/+\":[\"Logos\"],\"DHhJ7s\":[\"Previous\"],\"DJLY+/\":[\" and try again.\"],\"DOx286\":[\"Draft restored.\"],\"DPfwMq\":[\"Done\"],\"DSJXZM\":[\"Enter a valid date.\"],\"DYlMYF\":[\"Built-in background\"],\"DoJzLz\":[\"Collections\"],\"Du2B9f\":[\"The default accent should support reading first. Start by comparing it against the\"],\"DxwUcG\":[\"Read the palette as content first\"],\"E3NcGH\":[\"Square logo PNG\"],\"EEYbdt\":[\"Publish\"],\"EGwzOK\":[\"Complete Setup\"],\"EHWwm1\":[\"The default accent should feel written, not branded.\"],\"EO3I6h\":[\"Upload didn't go through. Try again in a moment.\"],\"EQNPYo\":[\"Featured on \",[\"date\"],\" at \",[\"time\"]],\"EQtz4D\":[\"Open a few links and check whether they still feel native to the page.\"],\"EU3tBD\":[\"Link removed.\"],\"EetoJL\":[\"Guide the eye without taking over the layout.\"],\"Eiv3bO\":[\"Buttons can stay steady, but links, thread markers, and subtle emphasis should feel closer to ink on paper than dashboard chrome.\"],\"ElTnWL\":[\"Published on\"],\"EmQw8O\":[\"If this article still feels like a page you want to keep reading, the palette is probably close.\"],\"EsJdRp\":[\"Save theme\"],\"FESYvt\":[\"Describe this for people with visual impairments...\"],\"FEr96N\":[\"Theme\"],\"FGySZL\":[\"The default accent works best when it reads like a fountain-pen underline. Compare it against the\"],\"FM+KeU\":[\"No drafts yet. Save a draft to find it here.\"],\"Fdv5k7\":[\"What to look for while tuning it\"],\"FkMol5\":[\"Featured\"],\"Fxf4jq\":[\"Description (optional)\"],\"G2u/aQ\":[\"Download official Jant logos, icons, and preview assets.\"],\"GBJzTZ\":[\"Archive\"],\"GX2VMa\":[\"Create your admin account.\"],\"GY/1J4\":[\"Jant fallback canary string\"],\"GbIOhd\":[\"Reply quietly\"],\"GiRWtR\":[\"Why the default accent should feel written, not branded\"],\"GkpIs2\":[\"Remove this link from Collections? The destination won't change.\"],\"GorKul\":[\"Welcome to Jant\"],\"GxkJXS\":[\"Uploading...\"],\"H29JXm\":[\"+ ALT\"],\"H4lgRd\":[\"Authentication isn't set up. Check your server config.\"],\"HFPGej\":[\"No threads match these filters. Try adjusting your selection or clear all filters.\"],\"HG79RB\":[\"Post as Private\"],\"HNEHJP\":[\"Demo credentials are pre-filled — hit Sign In to continue.\"],\"HbAIQc\":[\"A reference link for checking whether the accent feels editorial instead of promotional.\"],\"Ht1V3q\":[\"For the same reason, inline code should stay neutral. Something like theme.siteAccent = soften(green, 12%) should not suddenly become the loudest thing on the page.\"],\"I22eN0\":[\"Shared links\"],\"I6zLrz\":[\"Use these when you need a transparent square logo, a shaped tile with a built-in background, a browser icon, or a default preview image.\"],\"ICsA6P\":[\"You have unsaved changes\"],\"IUX7p+\":[\"White logo on the Jant green rounded tile for app icon mockups, touch icons, directory listings, and other square placements that should feel softer.\"],\"IagCbF\":[\"URL\"],\"IjnQHI\":[\"with title\"],\"ImOQa9\":[\"Reply\"],\"IsI3kE\":[\"Nothing here yet. Add posts to one of these collections to fill this view.\"],\"J+2Rls\":[\"Leave blank to publish now. Use an earlier date when importing older posts.\"],\"J4tAHl\":[\"Headings should keep their hierarchy even when the accent gets softer.\"],\"JYj5R2\":[\"Browse files\"],\"JcD7qf\":[\"More actions\"],\"JqJ5Xv\":[\"Latest\"],\"JuN5GC\":[\"No file selected. Choose a file to upload.\"],\"JwLPQ/\":[\"This sign-in link has expired. Return to \"],\"KOqvXP\":[\"Do not recolor, stretch, rotate, outline, or add effects to the logo.\"],\"KbS2K9\":[\"Reset Password\"],\"KdSsVl\":[\"Author (optional)\"],\"Khu3PV\":[\"Publish settings\"],\"KiJn9B\":[\"Note\"],\"KlZ+t+\":[\"%name% + %count% more\"],\"KsvRin\":[\"Hide from Latest\"],\"KzmC5L\":[\"Controls\"],\"L7svJg\":[\"Reading\"],\"Lbkbwy\":[\"A quote card for judging accent color against softer, citation-heavy content.\"],\"LcvzvX\":[\"Tap to retry\"],\"LkA8jz\":[\"Add alt text\"],\"LxRg6f\":[\"live theme controls\"],\"M4tzVU\":[\"Latest posts\"],\"M8kJqa\":[\"Drafts\"],\"MHrjPM\":[\"Title\"],\"MILa7n\":[\"Square tile\"],\"MSc/Yq\":[\"Do you want to publish your changes or discard them?\"],\"Mc7+6G\":[\"Enter a valid URL starting with http://, https://, or mailto:.\"],\"MdMyne\":[\"Source link (optional)\"],\"MiMY3Q\":[\"Apple touch icon\"],\"MiyoI7\":[\"default note sample\"],\"MqghUt\":[\"Search posts...\"],\"Myqkib\":[\"Create a collection to get started.\"],\"N8UzTV\":[\"Replies\"],\"NAFbuE\":[\"Search snippet\"],\"NH9Z1R\":[\"Start here\"],\"NqsRbb\":[\"Jant logo\"],\"NvXuWk\":[\"Won't move the thread to the top of latest.\"],\"O1367B\":[\"All collections\"],\"O3oNi5\":[\"Email\"],\"OEdMhi\":[\"The best default color is the one you notice only after reading for a while.\"],\"OEt/to\":[\"Guidelines\"],\"OJxdgi\":[\"Keep this link under 200 characters.\"],\"OaoJcz\":[\"Social preview\"],\"OmfDbR\":[\"Site accent\"],\"Ovks1h\":[\"A softer blue feels more like ink than product chrome.\"],\"P/sHNL\":[\"Use this page to judge buttons, links, cards, forms, thread accents, and quiet surfaces before changing a theme globally.\"],\"Q/uoSA\":[\"Quiet here for now.\"],\"Q2mGA7\":[\"Clear filter\"],\"QBqVyM\":[\"Home screen icon for iPhone and iPad shortcuts.\"],\"QebAts\":[\"Link added.\"],\"Qgbxdw\":[\"Designing a calmer default accent for Jant\"],\"Qn9Ao8\":[\"Circle tile\"],\"QyDt3L\":[\"File uploaded.\"],\"R5CMuK\":[\"Jant looks best when the accent feels editorial. Buttons can stay sturdy, but inline emphasis should feel like a pen mark, not a dashboard highlight.\"],\"R8AthW\":[\"Divider\"],\"R9Khdg\":[\"Auto\"],\"RAv3u7\":[\"Compare it against the theme controls\"],\"ROa4Ti\":[\"Interfaces for reading should guide the eye, not keep asking for attention.\"],\"RZOWDv\":[\"Add a custom shortcut to any page or site.\"],\"RdmNnl\":[\"Browser tab\"],\"RfGczC\":[\"Square logo\"],\"Rj01Fz\":[\"Links\"],\"S37om9\":[\"Included assets\"],\"S8NCfs\":[\"Save to drafts to edit and post at a later time.\"],\"SJGVAw\":[\"Feel editorial and slightly quieter.\"],\"SJmfuf\":[\"Site Name\"],\"SaNhJE\":[\"feel deliberate instead of washed out.\"],\"SpTWH3\":[\"Download SVG\"],\"SvRuJt\":[\"Field Notes on Interface Tone\"],\"T/R+Qz\":[\"Primary\"],\"TNZKpI\":[\"Danger\"],\"TvaTxw\":[\"Doesn't appear in Latest. Still appears in collections you add it to.\"],\"UIMXHD\":[\"Remove Divider\"],\"UaZwcz\":[\"More options are available after you create it.\"],\"Uc5y7o\":[\"Choose the standard logo for websites, docs, directories, and editorial layouts.\"],\"V18SVO\":[\"Use the logo on light backgrounds.\"],\"V4WsyL\":[\"Add Link\"],\"VCA6B2\":[\"These are actual feed components with real footers, summaries, and inline links. Use this section to judge whether the theme still feels calm once it is applied to realistic content.\"],\"VNqFYa\":[\"Loading post...\"],\"WCOanD\":[\"This reference is useful because it treats links and citations as part of the reading rhythm. Keep that in mind while tuning the\"],\"WbIbzR\":[\"Checking link...\"],\"WcWS//\":[\"Download file\"],\"WhsN3P\":[\"A good default accent in Jant should feel like editorial structure, not product branding. That means links, emphasis, and thread cues can be visible without turning the page into UI chrome.\"],\"Wn+/rH\":[\"Transparent square\"],\"XU7b+L\":[\"Primary logo files\"],\"XV1mAn\":[\"Only visible when signed in.\"],\"XrnWzN\":[\"Published!\"],\"YIix5Y\":[\"Search...\"],\"YUglt2\":[\"Generating a link...\"],\"YXiA6e\":[\"Primary button\"],\"Ygx3Yl\":[\"Small browser icon used in tabs and bookmarks.\"],\"Z6NwTi\":[\"Save as Draft\"],\"ZGs2so\":[\"Delete this collection permanently? Posts inside won't be removed.\"],\"ZV5ykW\":[\"Download PNG\"],\"ZhhOwV\":[\"Quote\"],\"ZmSeP+\":[\"Save to drafts?\"],\"ZxFuun\":[[\"count\",\"plural\",{\"one\":[\"Found \",\"#\",\" result\"],\"other\":[\"Found \",\"#\",\" results\"]}]],\"a5j82I\":[\"No collections match that search. Try a different name.\"],\"aHTB7P\":[\"Supplementary content attached to your post\"],\"aMEyv0\":[\"Stay sturdy and readable.\"],\"aN6wx0\":[\"Nothing in Featured yet. Mark a post as featured to show it here.\"],\"aYpXKS\":[\"and checking whether the accent is guiding attention or pulling too hard.\"],\"aaGV/9\":[\"New Link\"],\"af+9p6\":[\"Quiet metadata\"],\"an5hVd\":[\"Images\"],\"ao77hr\":[[\"count\",\"plural\",{\"one\":[\"#\",\" hidden post\"],\"other\":[\"#\",\" hidden posts\"]}]],\"auFlOr\":[\"Icons and previews\"],\"avuFKG\":[\"threads\"],\"bFpC86\":[\"Everything in one download\"],\"bGtMpA\":[\"Add a label and URL.\"],\"bHOiy1\":[\"Password changes are off in demo mode. Sign in with the shared demo credentials.\"],\"bbdNeX\":[\"Sign in\"],\"bfCbdi\":[\"Current post\"],\"bkBJmZ\":[\"This is useful as a color check because it puts the accent next to quotation styling, metadata, and a quieter explanatory paragraph. Compare it back to the\"],\"bzSI52\":[\"Discard\"],\"c2JRUS\":[\"Generate automatically\"],\"cIoW7X\":[\"Inline link\"],\"cTUByn\":[\"Newest first\"],\"cb7FR8\":[\"White logo on the Jant green square tile for platforms and layouts that expect a true edge-to-edge square.\"],\"cgmi4V\":[\"Delete Draft\"],\"cnGeoo\":[\"Delete\"],\"d+F4pf\":[\"The image should sit quietly inside the article instead of feeling like a card preview.\"],\"d/o/BH\":[\"Couldn't publish. Saved as draft.\"],\"dD7NPy\":[\"Outline\"],\"dEgA5A\":[\"Cancel\"],\"dUsGbd\":[\"The right accent should disappear into the writing until you need it.\"],\"dXoieq\":[\"Summary\"],\"dYKrp3\":[\"Hidden from Latest\"],\"dbUuAj\":[\"Appears in Latest.\"],\"df4a/r\":[\"Couldn't load this post. Try again.\"],\"ePK91l\":[\"Edit\"],\"eWLklq\":[\"Quotes\"],\"f4MAoA\":[\"Some uploads failed. Saved as draft.\"],\"f5s9EI\":[\"Press N to write\"],\"f6Hub0\":[\"Sort\"],\"f8fH8W\":[\"Design\"],\"fD+f7T\":[\"RSS feed\"],\"fKrDxS\":[\"Brand tile\"],\"fMPkxb\":[\"Show more\"],\"fqDzSu\":[\"Rate\"],\"fttd2R\":[\"My Collection\"],\"gCcxP/\":[\"Threads can include up to \",[\"count\"],\" posts.\"],\"gFdWl+\":[\"A long-form article sample for checking the default palette in a true reading context.\"],\"gNKz6Z\":[\"Collection deleted.\"],\"gXH9r/\":[\"Open raw PNG\"],\"gj52YE\":[\"This collection is empty. Add posts from the editor.\"],\"gpaPhA\":[\"Helps screen readers describe the image\"],\"h5RcXU\":[\"Post hidden\"],\"hLlWo5\":[\"A few simple rules.\"],\"hWpUeY\":[\"Auto link\"],\"hXzOVo\":[\"Next\"],\"heSQoS\":[\"Paste a URL...\"],\"hrkGms\":[\"Search\"],\"i0vDGK\":[\"Sort Order\"],\"i5+Y7d\":[\"Download the official Jant logo, icons, and preview files.\"],\"i6kro6\":[\"Edit custom link\"],\"i6nDCI\":[\"Choose a new password.\"],\"iG7KNr\":[\"Logo\"],\"iH8pgl\":[\"Back\"],\"ilSmIt\":[\"Hard edge\"],\"iu7tUI\":[\"Breadcrumb\"],\"jAXE5p\":[\"Reverse logo\"],\"jAqB/k\":[\"Post privately\"],\"jQflRT\":[\"This uses the real single-post detail rendering with a longer article, inline image, tables, lists, quotes, and code. The content column stays at the same width as the live site.\"],\"jd+8Mm\":[\"Social preview image\"],\"jdJOV1\":[\"Settings\"],\"ji7oVU\":[\"Edit post\"],\"jpctdh\":[\"View\"],\"jvyYZG\":[\"What's on your mind...\"],\"k3Iw35\":[\"Switch to the white logo when the standard green version would lose contrast.\"],\"kPMIr+\":[\"Give it a title...\"],\"kj6ppi\":[\"entry\"],\"kr39oD\":[\"No collections yet. Start one to organize posts by topic.\"],\"kzvWob\":[\"Link to the post archive\"],\"laT1IJ\":[\"iOS home screen\"],\"lb+Xwx\":[\"Custom link\"],\"m16xKo\":[\"Add\"],\"mKT7g0\":[\"Text attachment\"],\"mc/vLq\":[\"This link is already in use. Choose something else.\"],\"muKqfV\":[\"Featured\"],\"n1ekoW\":[\"Sign In\"],\"n3ReIn\":[\"Collections\"],\"n6QD94\":[\"Oldest first\"],\"nFukaP\":[\"Wrong email or password. Check your credentials and try again.\"],\"nV6twc\":[\"Organize\"],\"nd8Puv\":[\"White logo on the Jant green circle for profile images, badges, and other round placements where you want a ready-made asset.\"],\"ndrEYW\":[\"When the accent is slightly warmer and less literal, the whole page feels more like a writing space and less like product UI.\"],\"nfU386\":[\"Sign in if this is your space.\"],\"o21Y+P\":[\"entries\"],\"oO0hKx\":[[\"count\",\"plural\",{\"one\":[\"#\",\" more post\"],\"other\":[\"#\",\" more posts\"]}]],\"oTu7Wt\":[\"Combined Collections\"],\"ode0+L\":[\"Theme sample\"],\"ogssnn\":[\"with media\"],\"ovBPCi\":[\"Default\"],\"p1Z67P\":[\"When primary is too rigid, the whole page starts reading like product UI instead of writing space.\"],\"p2/GCq\":[\"Confirm Password\"],\"pB0OKE\":[\"New Divider\"],\"pBHx39\":[\"Dark backgrounds\"],\"pVrU5x\":[\"If this page feels too branded, the first place to soften is the default theme’s site accent, not the border or body text.\"],\"pvnfJD\":[\"Dark\"],\"q+hNag\":[\"Collection\"],\"q5YRzz\":[\"Color check\"],\"q8RviX\":[\"Titled\"],\"qcawwg\":[\"Publish now\"],\"qiN9NB\":[\"Surface\"],\"qt89I8\":[\"Draft saved.\"],\"quvfGs\":[\"instead of judging it as an isolated swatch.\"],\"r7kcaA\":[\"Drag collections, links, and dividers into the order you want.\"],\"rA2TFI\":[\"Switch the palette and mode without opening settings or changing the active site theme.\"],\"rV8ZnP\":[\"Edit publish date\"],\"rdUucN\":[\"Preview\"],\"s8G5Or\":[\"This upload would exceed your shared hosted media limit. Remove files or upgrade storage to continue.\"],\"s9gHf5\":[\"your-post-link\"],\"sER+bs\":[\"Files\"],\"sQpDn6\":[\"Exit fullscreen\"],\"sgr2wQ\":[\"collection\"],\"slujBW\":[\"Use lowercase letters, numbers, and hyphens only.\"],\"syiAKf\":[\"note treatment\"],\"t42hIC\":[\"Everything most people need is in one ZIP.\"],\"tCctex\":[\"The brand pack includes SVG logos, a transparent square PNG, rounded, square, and circle tiles, plus favicon, Apple touch icon, and the default social preview image.\"],\"tKlWWY\":[\"Emoji\"],\"tSWVu5\":[\"Published on \",[\"date\"],\" at \",[\"time\"]],\"tfDRzk\":[\"Save\"],\"tgSBSE\":[\"Remove Link\"],\"uowbPn\":[\"Remove attachment\"],\"v3E8iS\":[\"A practical checklist\"],\"vSJd18\":[\"Video\"],\"vSYKYI\":[\"Main feed\"],\"vXCC6J\":[\"Something doesn't look right. Check the form and try again.\"],\"vcpc5o\":[\"Close menu\"],\"vdFnYM\":[\"Reset link\"],\"vdvpU5\":[\"/archive?format=quote or https://example.com\"],\"vgpfCi\":[\"Save draft\"],\"vpSPA1\":[\"Auth secret is missing. Check your environment variables.\"],\"vzU4k9\":[\"New Collection\"],\"w0Emel\":[\"Suggested link\"],\"w6mlns\":[\"Article detail page\"],\"wJ+GRy\":[\"All visibility\"],\"wL3cK8\":[\"Latest\"],\"wja8aL\":[\"Untitled\"],\"wlnK1t\":[\"A single ZIP with the main logo, reverse logo, square PNG, rounded, square, and circle tiles, plus favicon, Apple touch icon, and social preview image.\"],\"wm3Zlr\":[\"All years\"],\"xCWek4\":[\"File storage isn't set up. Check your server config.\"],\"xVrkxi\":[\"quiet design\"],\"xVvw1i\":[\"This reset link is no longer valid. Request a new one to continue.\"],\"xYilR2\":[\"Media\"],\"xeiujy\":[\"Text\"],\"xhTx3y\":[\"Choose the standard logo for most placements and the reverse logo when you need more contrast.\"],\"y28hnO\":[\"Post\"],\"y2o/Y0\":[\"This Link Has Expired\"],\"yGZVl1\":[\"More\"],\"yQ2kGp\":[\"Load more\"],\"yUtAh2\":[\"New Thread\"],\"ycM1Xg\":[\"No results. Try different keywords.\"],\"ynMAhG\":[\"Default logo\"],\"yzF66j\":[\"Link\"],\"zBFr9G\":[\"Paste a long article, AI response, or any text...\\n\\nMarkdown formatting will be preserved.\"],\"zJDAbh\":[\"Don't save\"],\"zcDmsG\":[\"Featured posts\"],\"zoK+eO\":[\"Add a title before posting this link.\"],\"zucql+\":[\"Menu\"],\"zwBp5t\":[\"Private\"]}");
1852
1852
  //#endregion
1853
1853
  //#region src/i18n/locales/settings/en.ts
1854
1854
  var messages$2 = JSON.parse("{\"+9JI/F\":[\"Connecting will sync your site onto \",[\"repo\"],\"'s default branch on top of its existing history. Existing files outside Jant's managed paths are kept. This can't be undone.\"],\"+AXdXp\":[\"Label and URL are required\"],\"+K0AvT\":[\"Disconnect\"],\"+zy2Nq\":[\"Type\"],\"/3H2/s\":[\"This hosted site signs in through \",[\"providerLabel\"],\". Manage password and hosted access there.\"],\"/JnyjR\":[\"Toggle built-in navigation items. Their order controls what shows in the header and which feed the homepage opens first.\"],\"0OGSSc\":[\"Avatar display updated.\"],\"0UzCUX\":[\"Update the password you use to sign in\"],\"10UtuM\":[\"CJK Font\"],\"14BEca\":[\"Read why\"],\"1F6Mzc\":[\"No navigation items yet. Add links or enable system items below.\"],\"1njn7W\":[\"Light\"],\"2B7t+s\":[\"Sessions and password\"],\"2DoBvq\":[\"Feeds\"],\"2FYpfJ\":[\"More\"],\"2MXb5X\":[\"Field notes on quiet design\"],\"2PTjMB\":[\"I want to delete \",[\"siteName\"]],\"2cFU6q\":[\"Site Footer\"],\"2oWZo7\":[\"Last commit\"],\"2uuy4H\":[\"Connected via Personal Access Token\"],\"35x8eZ\":[\"Showing \",[\"shown\"],\" of \",[\"total\"]],\"3Cw1AI\":[\"Add Collection\"],\"3VrybB\":[\"Redirect\"],\"3Yvsaz\":[\"302 (Temporary)\"],\"3n0zbB\":[\"Session management is off in demo mode. Use the shared demo session instead.\"],\"3sYJi5\":[\"Download a Hugo-compatible archive — host it statically or move to another Jant.\"],\"3wKq0C\":[\"Couldn't save. Try again in a moment.\"],\"49Bsal\":[\"Feed settings updated.\"],\"4Jge8E\":[\"Active Sessions\"],\"4KIa+q\":[\"Export downloaded.\"],\"4cEClj\":[\"Sessions\"],\"4zGJ5E\":[\"Delete Account Permanently\"],\"5QlUIt\":[\"Empty repository. Ready to connect.\"],\"5VQnR3\":[\"Use these when you want a feed URL that never changes.\"],\"5dpcN1\":[\"type to search all\"],\"69OXZB\":[\"Delete Hosted Site\"],\"6ArdBh\":[\"Uses featured posts for /feed.\"],\"6DjeBT\":[\"Demo sites always stay hidden from search engines.\"],\"6FFB7q\":[\"Uses the latest public posts for /feed.\"],\"6K1Vef\":[\"Delete this blog permanently? This cannot be undone.\"],\"6NpNLc\":[\"This repository has existing content.\"],\"6V3Ea3\":[\"Copied\"],\"746NHh\":[\"this blog\"],\"7811AW\":[\"This repository is already backing up this site.\"],\"7FaY4u\":[\"Usage\"],\"7G9YLi\":[\"Allow search engines to index my site\"],\"7MZxzw\":[\"Password changed.\"],\"7vhWI8\":[\"New Password\"],\"7z05Pf\":[\"Open the hosted site controls in \",[\"providerLabel\"],\" to cancel billing or permanently delete this site.\"],\"81nFIS\":[\"Passwords don't match. Make sure both fields are identical.\"],\"87a/t/\":[\"Label\"],\"89Upyo\":[\"That theme isn't available. Pick another one.\"],\"8BfEpW\":[\"Hosted account\"],\"8N/Mcp\":[\"Archive filter parameters (e.g. format=note&view=list)\"],\"8U2Z7f\":[\"New Custom URL\"],\"8ZsakT\":[\"Password\"],\"9+vGLh\":[\"Custom CSS\"],\"9As8Nu\":[\"Create one on GitHub\"],\"9Lsvt5\":[\"Signed in \",[\"date\"]],\"9T7Cwm\":[\"Redirects, vanity paths, and URL control\"],\"9aUyym\":[\"See where you're signed in and revoke old sessions\"],\"A1taO8\":[\"Search\"],\"AeXO77\":[\"Account\"],\"AnY+O9\":[\"Show \\\"Build with Jant\\\" at the bottom of the home page\"],\"ApZDMk\":[\"This is used for your favicon and apple-touch-icon. For best results, upload a square PNG with a solid background at least 512x512 pixels.\"],\"B495Gs\":[\"Archive\"],\"B4ESok\":[\"API reference\"],\"CTAEes\":[\"Select a repository\"],\"CjZZgz\":[\"This repository already has commits\"],\"DCKkhU\":[\"Current Password\"],\"DKKKeF\":[\"Manage password and hosted access in \",[\"providerLabel\"]],\"ECIBO2\":[\"Controls the language of the dashboard and settings. Public pages stay in English.\"],\"EO3I6h\":[\"Upload didn't go through. Try again in a moment.\"],\"Enslfm\":[\"Destination\"],\"F7FKwe\":[\"Anything you paste here has full access to your visitors' browsers. Only use code from sources you trust.\"],\"FkMol5\":[\"Featured\"],\"G/1oP+\":[\"Remove the webhook and stop syncing. Your repository content will not be deleted.\"],\"G0qJsQ\":[\"Security token missing. Refresh the page and try again.\"],\"G39wnK\":[\"Back up and sync content with a GitHub repository\"],\"GMMWcy\":[\"Name, metadata, language, and search defaults\"],\"GXsAby\":[\"Revoke\"],\"GxkJXS\":[\"Uploading...\"],\"GzKzUa\":[\"Demo limits\"],\"HKH+W+\":[\"Data\"],\"Hp1l6f\":[\"Current\"],\"HxlY7t\":[\"Changing this updates what subscribers get from /feed.\"],\"HxuOlm\":[\"Site Header\"],\"I6gXOa\":[\"Path\"],\"ID38tA\":[\"Account deletion is off in demo mode. The shared demo resets separately.\"],\"IF9tPu\":[\"When to use site export, database backups, and recovery drills.\"],\"IW5PBo\":[\"Copy Token\"],\"IagCbF\":[\"URL\"],\"IreQBq\":[\"Repository\"],\"J6bLeg\":[\"Add a custom link to any URL\"],\"JL7LF5\":[\"available CSS variables, data attributes, and examples.\"],\"JTviaO\":[\"Manage sign-in security, exports, and irreversible actions.\"],\"JcD7qf\":[\"More actions\"],\"JjX0OO\":[\"Copy your token now — it won't be shown again.\"],\"JrFTcr\":[\"Connecting…\"],\"JuN5GC\":[\"No file selected. Choose a file to upload.\"],\"KDw4GX\":[\"Try again\"],\"KSgo21\":[\"Pick a repository\"],\"KVVYBh\":[\"Add collection to navigation\"],\"KiJn9B\":[\"Note\"],\"L3DEwT\":[\"Remove this avatar? Your favicon and header icon will go back to the default.\"],\"L4t4/q\":[\"March 14\"],\"LdyooL\":[\"link\"],\"M/D8PK\":[\"+ Install on another account\"],\"M/haSd\":[\"Always show the light version of the theme.\"],\"M2kIWU\":[\"Font theme\"],\"M6CbAU\":[\"Toggle edit panel\"],\"Me5t5H\":[\"Connect a GitHub repository to automatically back up your posts as Markdown files. Edits on GitHub sync back to your site.\"],\"MtENL9\":[\"Tune how your site looks, reads, and runs.\"],\"N/8NPV\":[\"Before deleting, download a site export. You won't be able to recover this account after deletion.\"],\"N7UNHY\":[\"Featured feed\"],\"NHnUHF\":[\"Favicon and the profile mark in your header\"],\"NU2Fqi\":[\"Save CSS\"],\"Nldjdr\":[\"No custom URLs yet. Create one to add redirects or custom paths for posts.\"],\"O7rgs6\":[\"Header RSS points to your \",[\"feed\"],\" feed (/feed). Change what /feed returns in General.\"],\"OSJXFg\":[\"Applies to your entire site, including admin pages. Pick a palette, then choose whether it follows the system or stays fixed.\"],\"PEUV5I\":[\"Code injection updated.\"],\"PZ7HJ8\":[\"Blog Avatar\"],\"Pwqkdw\":[\"Loading…\"],\"PxJ9W6\":[\"Generate Token\"],\"Q/6Y+2\":[\"Needs Contents (read/write) and Webhooks (read/write) on the target repository.\"],\"Q30z/l\":[\"Remove this collection from navigation? The collection itself won't be deleted.\"],\"Q99OtV\":[\"Pin a collection to your navigation bar. An asterisk (*) appears next to collections updated in the last 48 hours.\"],\"QZmz0H\":[\"Built-in links\"],\"Qnrzvb\":[\"Active Tokens\"],\"R6Z4LE\":[\"Download failed. Please try again.\"],\"R9Khdg\":[\"Auto\"],\"RxsRD6\":[\"Time Zone\"],\"SJmfuf\":[\"Site Name\"],\"SKZhW9\":[\"Token name\"],\"SVQQPe\":[\"Couldn't connect. Check the error and try again.\"],\"TpF3v+\":[\"Injected before </head>. Use for analytics, custom meta tags, and styles that must load early.\"],\"Tz0i8g\":[\"Settings\"],\"UFK415\":[\"Site-wide HTML for analytics and widgets\"],\"Uj/btJ\":[\"Display avatar in my site header\"],\"UsODUn\":[\"Select an account\"],\"UxKoFf\":[\"Navigation\"],\"V+bhUy\":[\"Install GitHub App\"],\"V4WsyL\":[\"Add Link\"],\"V5pZwT\":[\"Search settings updated.\"],\"VXUPla\":[\"Connect with GitHub App\"],\"VhMDMg\":[\"Change Password\"],\"Vn3jYy\":[\"Navigation items\"],\"VoZYGU\":[\"This will permanently delete all your data — posts, media, collections, settings, and your account. Your blog will be reset to its initial setup state. This cannot be undone.\"],\"Weq9zb\":[\"General\"],\"Wi9i06\":[\"Follow each visitor's system preference.\"],\"Wx1M8N\":[\"Install the GitHub App to grant access without managing personal tokens. Permissions are scoped per repository and revocable from GitHub.\"],\"X+8FMk\":[\"Current password doesn't match. Try again.\"],\"X1G9eY\":[\"Navigation Preview\"],\"X9Hujr\":[\"Manual Push\"],\"XtBJV8\":[\"Checking repository…\"],\"Xtc16w\":[\"Refresh repository list\"],\"Y/F35r\":[\"Create a post with curl:\"],\"YF6zHf\":[\"Site settings updated.\"],\"YdG2RF\":[\"Export Site\"],\"YwhjRx\":[\"Manage Account\"],\"ZDY7Fy\":[\"Syncing…\"],\"ZQKLI1\":[\"Danger Zone\"],\"ZS/CBL\":[\"Delete this navigation link? Visitors won't see it in your site header anymore.\"],\"ZhhOwV\":[\"Quote\"],\"ZiooJI\":[\"API Tokens\"],\"Zm7Qb0\":[\"Backup & Restore Guide\"],\"ZmUkwN\":[\"Add custom link to navigation\"],\"a14mj8\":[\"Unknown device\"],\"a3LDKx\":[\"Security\"],\"aAIQg2\":[\"Appearance\"],\"aFkzVF\":[\"The slug of the target post or collection\"],\"alKG0+\":[\"Font Theme\"],\"anibOb\":[\"About this blog\"],\"any7NR\":[\"Theming guide\"],\"b+/jO6\":[\"301 (Permanent)\"],\"bHOiy1\":[\"Password changes are off in demo mode. Sign in with the shared demo credentials.\"],\"bHYIks\":[\"Sign Out\"],\"bmrL08\":[\"Demo mode hides sessions, password changes, and account deletion. Export still works.\"],\"c3MN2z\":[\"all available endpoints and request formats.\"],\"cSDy01\":[\"Custom CSS updated.\"],\"clzoNp\":[\"Always show the dark version of the theme.\"],\"cnGeoo\":[\"Delete\"],\"d3FRkY\":[\"Could not copy. Try again.\"],\"d5oGUo\":[\"Create a new repository on GitHub\"],\"dEgA5A\":[\"Cancel\"],\"dTXUY+\":[\"Confirm account deletion\"],\"dYKrp3\":[\"Hidden from Latest\"],\"dk7TCH\":[\"Permanently delete all data and reset the blog\"],\"drodVV\":[\"No collections yet. Create one first, then add it to your navigation.\"],\"dsWkIw\":[\"Disconnect from GitHub? The webhook will be removed. Your repository content will not be deleted.\"],\"e/tSI5\":[\"Navigation order updated.\"],\"ePK91l\":[\"Edit\"],\"ebQKK7\":[\"Site\"],\"egK+Yy\":[\"Bearer tokens for scripts and automation\"],\"ehj/zN\":[\"Redirect Type\"],\"eneWvv\":[\"Draft\"],\"erTMh7\":[\"Last synced\"],\"f+m8jj\":[\"Feed URL copied.\"],\"f8fH8W\":[\"Design\"],\"fWYqkz\":[\"Code Injection\"],\"gOWiTY\":[\"Load a serif font optimized for Chinese, Japanese, or Korean content.\"],\"gZ5owP\":[\"Search repositories\"],\"gbqbh6\":[\"Safe to leave this page — syncing continues in the background.\"],\"gkFvVN\":[\"Injected before </body>. Use for chat widgets and scripts that should not block page load.\"],\"gtQsRO\":[\"Create Custom URL\"],\"hBO/y4\":[\"Security token expired. Refresh the page and try again.\"],\"hGmyDl\":[\"Tokens let you access the API from scripts, shortcuts, and other tools without signing in.\"],\"hIHkRy\":[\"Connected via GitHub App\"],\"hdSi1b\":[\"Type \",[\"repo\"],\" to confirm\"],\"he3ygx\":[\"Copy\"],\"i0qMbr\":[\"Home\"],\"iEUzMn\":[\"system\"],\"iSLIjg\":[\"Connect\"],\"iVOMRi\":[\"Home settings updated.\"],\"icB4Cv\":[\"Drag links here to show them under the More menu\"],\"iiDXZc\":[\"Displayed at the bottom of all posts and pages.\"],\"j4VrG6\":[\"Download Export ZIP\"],\"j5nQL2\":[\"e.g. iOS Shortcuts\"],\"jUV7CU\":[\"Upload Avatar\"],\"jVUmOK\":[\"Markdown supported\"],\"jgBjXJ\":[\"Revoke this token? Any scripts using it will stop working.\"],\"jpctdh\":[\"View\"],\"k1ifdL\":[\"Processing...\"],\"kMXclu\":[\"Download a site export\"],\"kNiQp6\":[\"Pinned\"],\"kRhzWq\":[\"GitHub Sync\"],\"kVQs7s\":[\"Fine-grained styling overrides\"],\"ke1gWS\":[\"Custom URLs\"],\"kfcRb0\":[\"Avatar\"],\"kxDZ2i\":[\"This code runs on every page of your site.\"],\"l2Op2p\":[\"Query Parameters\"],\"lLW3vJ\":[\"Target Slug\"],\"lYHJih\":[\"Revoke this session? That device will need to sign in again.\"],\"mLOk1i\":[\"Push all posts to GitHub right now instead of waiting for the next automatic sync.\"],\"mSNmrX\":[\"List posts:\"],\"nK07ni\":[\"Choose a typographic direction for your site. Each theme changes both the font pairing and the reading rhythm.\"],\"nbfdhU\":[\"Integrations\"],\"o/vNDE\":[\"lets you override any theme variable.\"],\"oGC9uP\":[\"owner/repo\"],\"oH2JHg\":[\"We'll prefill the name \",[\"name\"],\". The list refreshes on return.\"],\"oKOOsY\":[\"Color Theme\"],\"oL535e\":[\"Not synced yet\"],\"oNA4If\":[\"All collections are already in your navigation.\"],\"pZq3aX\":[\"Upload failed. Please try again.\"],\"pgTIrt\":[\"Choose the GitHub account and repository to sync with this site.\"],\"psoxDF\":[\"That font theme isn't available. Pick another one.\"],\"pvnfJD\":[\"Dark\"],\"q+hNag\":[\"Collection\"],\"r5EW6f\":[\"This repository is already backing up another Jant site (\",[\"host\"],\"). Pick a different repository.\"],\"rEspiY\":[\"Navigation placement updated.\"],\"rFmBG3\":[\"Color theme\"],\"rlonmB\":[\"Couldn't delete. Try again in a moment.\"],\"satWc6\":[\"Main RSS feed\"],\"sgr2wQ\":[\"collection\"],\"sqxcaY\":[\"Created \",[\"date\"]],\"sxkWRg\":[\"Advanced\"],\"t/YqKh\":[\"Remove\"],\"t3hvHq\":[\"Sync Now\"],\"tfDRzk\":[\"Save\"],\"tvgAq5\":[\"No accounts authorized yet\"],\"u1VTd3\":[\"Palette, surface tone, and overall mood\"],\"u3wRF+\":[\"Published\"],\"u6KOjV\":[\"Want more control?\"],\"udPwLB\":[\"Header\"],\"ui6aMF\":[\"These devices are currently signed in to your account. Revoke any session you don't recognize.\"],\"vBEKwo\":[\"Manage this site's active sessions here. Password and hosted access are managed through \",[\"providerLabel\"],\".\"],\"vRldcl\":[\"Typography choices and reading texture\"],\"vSYKYI\":[\"Main feed\"],\"vTuib7\":[\"This controls what /feed returns.\"],\"vXIe7J\":[\"Language\"],\"vmQmHx\":[\"Add custom CSS to override any styles. Use data attributes like [data-page], [data-post], [data-format] to target specific elements.\"],\"vzX5FB\":[\"Delete Account\"],\"w8Rv8T\":[\"Label is required\"],\"wL3cK8\":[\"Latest\"],\"wPmHHc\":[\"Quiet surfaces let writing lead.\"],\"wW6NCp\":[\"Last error\"],\"wc+17X\":[\"/* Your custom CSS here */\"],\"wuLtXn\":[\"No active sessions right now. Signed-in devices show up here.\"],\"xCWek4\":[\"File storage isn't set up. Check your server config.\"],\"xHt036\":[\"Personal Access Token\"],\"xbN8dp\":[\"Soft color should still carry a clear reading rhythm.\"],\"y28hnO\":[\"Post\"],\"y8Md/V\":[\"Language and time updated.\"],\"yNCqOt\":[\"Latest feed\"],\"yQ3kNF\":[\"Type the following phrase to confirm:\"],\"ydq1k2\":[\"Pick an account first\"],\"yjjCV8\":[\"Fixed feed URLs\"],\"yjkELF\":[\"Confirm New Password\"],\"yzF66j\":[\"Link\"],\"z6wakA\":[\"A short intro shown on your home page.\"],\"zEizrk\":[\"Last used \",[\"date\"]],\"zSURJW\":[\"No repositories match.\"],\"zXH2jX\":[\"Language & Time\"],\"zlcDd2\":[\"Delete this custom URL? Visitors using it won't be redirected anymore.\"],\"zwBp5t\":[\"Private\"],\"zxRN6H\":[\"Header links, home feed, and overflow menu\"]}");
@@ -3352,10 +3352,10 @@ function normalizeThemeColorForMeta(color) {
3352
3352
  * internal paths (e.g. `/_assets/client-HASH.js`) embedded by the Worker build
3353
3353
  * from the Vite client manifest. Used only in production (IS_VITE_DEV=false).
3354
3354
  */ var IS_VITE_DEV = typeof __JANT_DEV__ !== "undefined" && __JANT_DEV__ === true;
3355
- var CORE_VERSION = "0.3.44-c595e1fa6d741ba8";
3356
- var CLIENT_JS_FILE = "/_assets/client-D95FNDg5.js";
3357
- var CLIENT_AUTH_JS_FILE = "/_assets/client-auth-CXILhW1b.js";
3358
- var CLIENT_CSS_FILE = "/_assets/client-BQH7AQ24.css";
3355
+ var CORE_VERSION = "0.3.46-4c46f967df638082";
3356
+ var CLIENT_JS_FILE = "/_assets/client-dSfWfMe9.js";
3357
+ var CLIENT_AUTH_JS_FILE = "/_assets/client-auth-BLCUje4M.js";
3358
+ var CLIENT_CSS_FILE = "/_assets/client-DDs6NzB3.css";
3359
3359
  var CLIENT_CJK_CSS_FILE = "/_assets/client-cjk-B7Z0snDu.css";
3360
3360
  var CLIENT_CJK_TC_CSS_FILE = "/_assets/client-cjk-tc-BesJYrb2.css";
3361
3361
  var CLIENT_CJK_JP_CSS_FILE = "/_assets/client-cjk-jp-DZwrTzQC.css";
@@ -3673,7 +3673,7 @@ var IconSprite = () => {
3673
3673
  const cjkSerifFont = appConfig?.cjkSerifFont ?? "off";
3674
3674
  const cjkStylesheetPath = cjkSerifFont === "zh-Hans" ? IS_VITE_DEV ? assetPath("/src/style-cjk.css") : toPublicAssetPath(CLIENT_CJK_CSS_FILE, assetBasePath) : cjkSerifFont === "zh-Hant" ? IS_VITE_DEV ? assetPath("/src/style-cjk-tc.css") : toPublicAssetPath(CLIENT_CJK_TC_CSS_FILE, assetBasePath) : cjkSerifFont === "ja" ? IS_VITE_DEV ? assetPath("/src/style-cjk-jp.css") : toPublicAssetPath(CLIENT_CJK_JP_CSS_FILE, assetBasePath) : cjkSerifFont === "ko" ? IS_VITE_DEV ? assetPath("/src/style-cjk-kr.css") : toPublicAssetPath(CLIENT_CJK_KR_CSS_FILE, assetBasePath) : null;
3675
3675
  const clientScriptPath = IS_VITE_DEV ? resolvedClientBundle === "full" ? assetPath("/src/client-auth.ts") : assetPath("/src/client.ts") : toPublicAssetPath(resolvedClientBundle === "full" ? CLIENT_AUTH_JS_FILE : CLIENT_JS_FILE, assetBasePath);
3676
- const faviconAssetVersion = resolvedFaviconVersion || "0.3.44-c595e1fa6d741ba8";
3676
+ const faviconAssetVersion = resolvedFaviconVersion || "0.3.46-4c46f967df638082";
3677
3677
  const resolvedFaviconHref = faviconHref ?? (faviconAssetVersion ? toPublicPath(`/favicon.ico?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/favicon.ico", sitePathPrefix));
3678
3678
  const resolvedAppleTouchHref = appleTouchHref ?? (faviconAssetVersion ? toPublicPath(`/apple-touch-icon.png?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/apple-touch-icon.png", sitePathPrefix));
3679
3679
  const socialImageHref = resolvedSocialImagePath && (isFullUrl(resolvedSocialImagePath) || resolvedSocialImagePath.startsWith("//") ? resolvedSocialImagePath : toAbsoluteSiteUrl(resolvedSocialImagePath, appConfig?.siteUrl || "", sitePathPrefix));
@@ -4155,7 +4155,7 @@ var STORAGE_DRIVERS = [
4155
4155
  envKeys: ["DEFAULT_THEME"]
4156
4156
  },
4157
4157
  DEFAULT_FONT_THEME: {
4158
- defaultValue: "tufte",
4158
+ defaultValue: "classic",
4159
4159
  envOnly: true,
4160
4160
  envKeys: ["DEFAULT_FONT_THEME"]
4161
4161
  },
@@ -4621,6 +4621,7 @@ function createTypeIdSchema(prefix) {
4621
4621
  "reset",
4622
4622
  "collections",
4623
4623
  "compose",
4624
+ "new",
4624
4625
  "static",
4625
4626
  "assets",
4626
4627
  "_assets",
@@ -10149,7 +10150,7 @@ var PaginatedPageHeader = ({ title, currentPage = 1, totalPages, description, ic
10149
10150
  * Home Page
10150
10151
  *
10151
10152
  * Timeline feed with per-type card components and thread previews.
10152
- */ var HomePage = ({ items, baseUrl, currentPage, totalPages }) => {
10153
+ */ var HomePage = ({ items, baseUrl, currentPage, totalPages, isAuthenticated, signinUrl }) => {
10153
10154
  const { i18n } = useLingui();
10154
10155
  return /* @__PURE__ */ jsxDEV$1("div", {
10155
10156
  "data-page": "home",
@@ -10168,8 +10169,12 @@ var PaginatedPageHeader = ({ title, currentPage = 1, totalPages, description, ic
10168
10169
  class: "flex flex-col",
10169
10170
  children: /* @__PURE__ */ jsxDEV$1("p", {
10170
10171
  id: "empty-timeline",
10171
- class: "py-12 text-center text-muted-foreground",
10172
- children: i18n._({ id: "WpXcBJ" })
10172
+ class: "py-8 text-muted-foreground",
10173
+ children: [i18n._({ id: "Q/uoSA" }), !isAuthenticated && /* @__PURE__ */ jsxDEV$1(Fragment$1, { children: [" ", /* @__PURE__ */ jsxDEV$1("a", {
10174
+ href: signinUrl,
10175
+ class: "underline underline-offset-2",
10176
+ children: i18n._({ id: "nfU386" })
10177
+ })] })]
10173
10178
  })
10174
10179
  })
10175
10180
  })
@@ -10258,7 +10263,9 @@ homeRoutes.get("/", async (c) => {
10258
10263
  items,
10259
10264
  baseUrl: toPublicPath("/", navData.sitePathPrefix),
10260
10265
  currentPage,
10261
- totalPages
10266
+ totalPages,
10267
+ isAuthenticated,
10268
+ signinUrl: `${toPublicPath("/signin", navData.sitePathPrefix)}?redirect=${encodeURIComponent(toPublicPath("/", navData.sitePathPrefix))}`
10262
10269
  })
10263
10270
  });
10264
10271
  });
@@ -10459,7 +10466,6 @@ function buildPostMeta(post, siteName) {
10459
10466
  siteDomains: () => siteDomains$1,
10460
10467
  siteMembers: () => siteMembers$1,
10461
10468
  sites: () => sites$1,
10462
- syncJobs: () => syncJobs$1,
10463
10469
  uploadSessions: () => uploadSessions$1,
10464
10470
  user: () => user$1,
10465
10471
  verification: () => verification$1
@@ -10862,28 +10868,6 @@ var account$1 = sqliteTable("account", {
10862
10868
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
10863
10869
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
10864
10870
  });
10865
- var SYNC_JOB_STATUSES = [
10866
- "pending",
10867
- "processing",
10868
- "completed",
10869
- "failed"
10870
- ];
10871
- var syncJobs$1 = sqliteTable("sync_job", {
10872
- id: text("id").primaryKey(),
10873
- siteId: text("site_id").notNull().references(() => sites$1.id, { onDelete: "cascade" }),
10874
- kind: text("kind").notNull(),
10875
- payload: text("payload").notNull(),
10876
- status: text("status", { enum: SYNC_JOB_STATUSES }).notNull().default("pending"),
10877
- attempts: integer("attempts").notNull().default(0),
10878
- maxAttempts: integer("max_attempts").notNull().default(3),
10879
- createdAt: integer("created_at").notNull(),
10880
- updatedAt: integer("updated_at").notNull(),
10881
- lockedUntil: integer("locked_until")
10882
- }, (table) => [
10883
- index("idx_sync_job_status_created").on(table.status, table.createdAt),
10884
- index("idx_sync_job_site_id").on(table.siteId),
10885
- check("chk_sync_job_status", sql`${table.status} IN (${sqlTextEnum$1(SYNC_JOB_STATUSES)})`)
10886
- ]);
10887
10871
  var verification$1 = sqliteTable("verification", {
10888
10872
  id: text("id").primaryKey(),
10889
10873
  identifier: text("identifier").notNull(),
@@ -11029,7 +11013,6 @@ function isNodeSqliteDatabase(db) {
11029
11013
  siteDomains: () => siteDomains,
11030
11014
  siteMembers: () => siteMembers,
11031
11015
  sites: () => sites,
11032
- syncJobs: () => syncJobs,
11033
11016
  uploadSessions: () => uploadSessions,
11034
11017
  user: () => user,
11035
11018
  verification: () => verification
@@ -11469,23 +11452,6 @@ var account = pgTable("account", {
11469
11452
  mode: "date"
11470
11453
  }).notNull()
11471
11454
  });
11472
- var syncJobs = pgTable("sync_job", {
11473
- id: text$1("id").primaryKey(),
11474
- siteId: text$1("site_id").notNull().references(() => sites.id, { onDelete: "cascade" }),
11475
- kind: text$1("kind").notNull(),
11476
- payload: text$1("payload").notNull(),
11477
- status: text$1("status", { enum: [
11478
- "pending",
11479
- "processing",
11480
- "completed",
11481
- "failed"
11482
- ] }).notNull().default("pending"),
11483
- attempts: integer$1("attempts").notNull().default(0),
11484
- maxAttempts: integer$1("max_attempts").notNull().default(3),
11485
- createdAt: integer$1("created_at").notNull(),
11486
- updatedAt: integer$1("updated_at").notNull(),
11487
- lockedUntil: integer$1("locked_until")
11488
- }, (table) => [index$1("idx_sync_job_status_created").on(table.status, table.createdAt), index$1("idx_sync_job_site_id").on(table.siteId)]);
11489
11455
  var verification = pgTable("verification", {
11490
11456
  id: text$1("id").primaryKey(),
11491
11457
  identifier: text$1("identifier").notNull(),
@@ -14397,7 +14363,9 @@ latestRoutes.get("/", async (c) => {
14397
14363
  items,
14398
14364
  baseUrl: toPublicPath("/latest", navData.sitePathPrefix),
14399
14365
  currentPage,
14400
- totalPages
14366
+ totalPages,
14367
+ isAuthenticated: navData.isAuthenticated,
14368
+ signinUrl: `${toPublicPath("/signin", navData.sitePathPrefix)}?redirect=${encodeURIComponent(toPublicPath("/latest", navData.sitePathPrefix))}`
14401
14369
  })
14402
14370
  });
14403
14371
  });
@@ -15793,22 +15761,6 @@ var CJK_SERIF_FALLBACK_VAR = "var(--font-cjk-serif-fallback)";
15793
15761
  }
15794
15762
  }
15795
15763
  var BUILTIN_FONT_THEMES = [
15796
- {
15797
- id: "tufte",
15798
- name: {
15799
- id: "Tufte",
15800
- message: "Tufte",
15801
- comment: "@context: Font theme name"
15802
- },
15803
- headingFontFamily: TUFTE_SERIF,
15804
- bodyFontFamily: TUFTE_SERIF,
15805
- cssVariables: {},
15806
- description: {
15807
- id: "Palatino-based old-style serif matching Tufte CSS proportions",
15808
- message: "Palatino-based old-style serif matching Tufte CSS proportions",
15809
- comment: "@context: Font theme description"
15810
- }
15811
- },
15812
15764
  {
15813
15765
  id: "classic",
15814
15766
  name: {
@@ -15825,6 +15777,22 @@ var BUILTIN_FONT_THEMES = [
15825
15777
  comment: "@context: Font theme description"
15826
15778
  }
15827
15779
  },
15780
+ {
15781
+ id: "tufte",
15782
+ name: {
15783
+ id: "Tufte",
15784
+ message: "Tufte",
15785
+ comment: "@context: Font theme name"
15786
+ },
15787
+ headingFontFamily: TUFTE_SERIF,
15788
+ bodyFontFamily: TUFTE_SERIF,
15789
+ cssVariables: {},
15790
+ description: {
15791
+ id: "Palatino-based old-style serif matching Tufte CSS proportions",
15792
+ message: "Palatino-based old-style serif matching Tufte CSS proportions",
15793
+ comment: "@context: Font theme description"
15794
+ }
15795
+ },
15828
15796
  {
15829
15797
  id: "system-sans",
15830
15798
  name: {
@@ -17844,13 +17812,19 @@ function SettingsRootContent({ sitePathPrefix = "", demoMode = false }) {
17844
17812
  }),
17845
17813
  /* @__PURE__ */ jsxDEV$1(SettingsDirectorySection, {
17846
17814
  title: i18n._({ id: "ebQKK7" }),
17847
- children: /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17815
+ children: [/* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17848
17816
  href: toPublicPath("/settings/general", sitePathPrefix),
17849
17817
  icon: ICONS$1.settings,
17850
17818
  tone: "subtle",
17851
17819
  name: i18n._({ id: "Weq9zb" }),
17852
17820
  description: i18n._({ id: "GMMWcy" })
17853
- })
17821
+ }), /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17822
+ href: toPublicPath("/settings/custom-urls", sitePathPrefix),
17823
+ icon: ICONS$1.arrowRightLeft,
17824
+ tone: "subtle",
17825
+ name: i18n._({ id: "ke1gWS" }),
17826
+ description: i18n._({ id: "9T7Cwm" })
17827
+ })]
17854
17828
  }),
17855
17829
  /* @__PURE__ */ jsxDEV$1(SettingsDirectorySection, {
17856
17830
  title: i18n._({ id: "aAIQg2" }),
@@ -17901,29 +17875,19 @@ function SettingsRootContent({ sitePathPrefix = "", demoMode = false }) {
17901
17875
  }),
17902
17876
  /* @__PURE__ */ jsxDEV$1(SettingsDirectorySection, {
17903
17877
  title: i18n._({ id: "sxkWRg" }),
17904
- children: [
17905
- /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17906
- href: toPublicPath("/settings/custom-urls", sitePathPrefix),
17907
- icon: ICONS$1.arrowRightLeft,
17908
- tone: "subtle",
17909
- name: i18n._({ id: "ke1gWS" }),
17910
- description: i18n._({ id: "9T7Cwm" })
17911
- }),
17912
- /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17913
- href: toPublicPath("/settings/code-injection", sitePathPrefix),
17914
- icon: ICONS$1.terminal,
17915
- tone: "subtle",
17916
- name: i18n._({ id: "fWYqkz" }),
17917
- description: i18n._({ id: "UFK415" })
17918
- }),
17919
- /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17920
- href: toPublicPath("/settings/api-tokens", sitePathPrefix),
17921
- icon: ICONS$1.key,
17922
- tone: "subtle",
17923
- name: i18n._({ id: "ZiooJI" }),
17924
- description: i18n._({ id: "egK+Yy" })
17925
- })
17926
- ]
17878
+ children: [/* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17879
+ href: toPublicPath("/settings/code-injection", sitePathPrefix),
17880
+ icon: ICONS$1.terminal,
17881
+ tone: "subtle",
17882
+ name: i18n._({ id: "fWYqkz" }),
17883
+ description: i18n._({ id: "UFK415" })
17884
+ }), /* @__PURE__ */ jsxDEV$1(SettingsDirectoryLink, {
17885
+ href: toPublicPath("/settings/api-tokens", sitePathPrefix),
17886
+ icon: ICONS$1.key,
17887
+ tone: "subtle",
17888
+ name: i18n._({ id: "ZiooJI" }),
17889
+ description: i18n._({ id: "egK+Yy" })
17890
+ })]
17927
17891
  }),
17928
17892
  /* @__PURE__ */ jsxDEV$1(SettingsDirectorySection, {
17929
17893
  title: i18n._({ id: "AeXO77" }),
@@ -20017,31 +19981,6 @@ async function syncHostedControlPlaneSiteAvatar(input) {
20017
19981
  });
20018
19982
  }
20019
19983
  //#endregion
20020
- //#region src/lib/job-queue.ts
20021
- /**
20022
- * Job Queue abstraction.
20023
- *
20024
- * Provides a unified interface for enqueueing background jobs across
20025
- * Cloudflare Workers (CF Queues) and Node/Postgres (DB-backed polling).
20026
- */
20027
- /**
20028
- * A no-op queue that silently drops jobs.
20029
- * Used as a fallback when neither CF Queue nor DB queue is configured.
20030
- */ var noopQueue = { async enqueue() {} };
20031
- //#endregion
20032
- //#region src/lib/job-queue-cf.ts
20033
- /**
20034
- * Cloudflare Queue adapter for the job queue.
20035
- */ /**
20036
- * Create a job queue backed by a Cloudflare Queue binding.
20037
- *
20038
- * @param queue - The CF Queue binding (e.g. `env.GITHUB_SYNC_QUEUE`)
20039
- */ function createCfJobQueue(queue) {
20040
- return { async enqueue(payload) {
20041
- await queue.send(payload);
20042
- } };
20043
- }
20044
- //#endregion
20045
19984
  //#region src/lib/github-sync-site-config.ts
20046
19985
  /**
20047
19986
  * Build the SiteConfig the GitHub Sync service needs from a request
@@ -20089,33 +20028,6 @@ async function syncHostedControlPlaneSiteAvatar(input) {
20089
20028
  rssFeedLimit: appConfig.rssFeedLimit
20090
20029
  };
20091
20030
  }
20092
- //#endregion
20093
- //#region src/lib/github-sync-trigger.ts
20094
- /**
20095
- * GitHub Sync Trigger
20096
- *
20097
- * Two dispatch paths:
20098
- *
20099
- * - `triggerGitHubSync` (queue-based): the original design, kept for
20100
- * future use when a Cloudflare Queue binding is actually wired up.
20101
- * Falls back to a no-op queue today, which silently drops jobs.
20102
- * - `triggerGitHubSyncInline` (inline): runs pushFullSync in the
20103
- * current worker invocation via `c.executionCtx.waitUntil`. Works
20104
- * uniformly on Workers and Node, no queue binding required. This
20105
- * is what every caller uses today.
20106
- *
20107
- * Both paths debounce through a PENDING flag. When a new trigger
20108
- * arrives while a sync is running, the inline runner records it via
20109
- * DIRTY; the running sync re-runs once more after completion so the
20110
- * new edits land.
20111
- */
20112
- /**
20113
- * Resolve the appropriate job queue from the environment.
20114
- * Returns the CF Queue adapter if available, otherwise noop.
20115
- */ function resolveJobQueue(env) {
20116
- if (env.GITHUB_SYNC_QUEUE) return createCfJobQueue(env.GITHUB_SYNC_QUEUE);
20117
- return noopQueue;
20118
- }
20119
20031
  /**
20120
20032
  * Returns the effective "sync in progress" state.
20121
20033
  *
@@ -20187,7 +20099,7 @@ async function syncHostedControlPlaneSiteAvatar(input) {
20187
20099
  return;
20188
20100
  }
20189
20101
  await markSyncPending(settings);
20190
- const { createGitHubSyncService } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
20102
+ const { createGitHubSyncService } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
20191
20103
  const { getGitHubAppConfig } = await import("./env-CgaH9Mut.js").then((n) => n.t);
20192
20104
  const run = runBackgroundSync(settings, createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
20193
20105
  storage: c.var.storage,
@@ -20974,7 +20886,7 @@ settingsRoutes.post("/github-sync/connect", async (c) => {
20974
20886
  await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "pat");
20975
20887
  await c.var.services.settings.set("GITHUB_SYNC_APP_INSTALLATION_ID", "");
20976
20888
  await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
20977
- const { createGitHubSyncService } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
20889
+ const { createGitHubSyncService } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
20978
20890
  const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
20979
20891
  storage: c.var.storage,
20980
20892
  githubApp: getGitHubAppConfig(c.env)
@@ -20993,7 +20905,7 @@ settingsRoutes.post("/github-sync/connect", async (c) => {
20993
20905
  return dsRedirect(publicPath(c, "/settings/github-sync"));
20994
20906
  });
20995
20907
  settingsRoutes.post("/github-sync/push", async (c) => {
20996
- const { createGitHubSyncService } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
20908
+ const { createGitHubSyncService } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
20997
20909
  const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
20998
20910
  storage: c.var.storage,
20999
20911
  githubApp: getGitHubAppConfig(c.env)
@@ -21013,7 +20925,7 @@ settingsRoutes.post("/github-sync/push", async (c) => {
21013
20925
  });
21014
20926
  });
21015
20927
  settingsRoutes.post("/github-sync/disconnect", async (c) => {
21016
- const { createGitHubSyncService } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
20928
+ const { createGitHubSyncService } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
21017
20929
  await createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), { githubApp: getGitHubAppConfig(c.env) }).teardownWebhook();
21018
20930
  return dsRedirect(publicPath(c, "/settings/github-sync"));
21019
20931
  });
@@ -21204,7 +21116,7 @@ function buildRepoPickerLabels(c) {
21204
21116
  const { parseRepoSlug, createGitHubClient } = await import("./github-api-BkRWnqMx.js").then((n) => n.n);
21205
21117
  const parsed = parseRepoSlug(repo);
21206
21118
  if (!parsed) return wantsJson ? c.json({ error: "Invalid repository format." }, 400) : c.text("Invalid repository format.", 400);
21207
- const { classifyRepoForSync } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
21119
+ const { classifyRepoForSync } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
21208
21120
  const ghClient = createGitHubClient(() => getInstallationTokenFromApp(app, installationId));
21209
21121
  let classification;
21210
21122
  try {
@@ -21234,7 +21146,7 @@ function buildRepoPickerLabels(c) {
21234
21146
  await c.var.services.settings.set("GITHUB_SYNC_REPO", repo);
21235
21147
  await c.var.services.settings.set("GITHUB_SYNC_TOKEN", "");
21236
21148
  await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
21237
- const { createGitHubSyncService } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
21149
+ const { createGitHubSyncService } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
21238
21150
  const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
21239
21151
  storage: c.var.storage,
21240
21152
  githubApp: app
@@ -21350,7 +21262,7 @@ function requireGitHubApp(c) {
21350
21262
  const { parseRepoSlug, createGitHubClient } = await import("./github-api-BkRWnqMx.js").then((n) => n.n);
21351
21263
  const parsed = parseRepoSlug(repo);
21352
21264
  if (!parsed) return c.json({ error: "Invalid repository format." }, 400);
21353
- const { classifyRepoForSync } = await import("./github-sync-7y_nTXx1.js").then((n) => n.r);
21265
+ const { classifyRepoForSync } = await import("./github-sync-CQ1x271f.js").then((n) => n.r);
21354
21266
  const client = createGitHubClient(() => getInstallationTokenFromApp(app, installationId));
21355
21267
  try {
21356
21268
  const classification = await classifyRepoForSync(client, parsed.owner, parsed.repo, c.var.currentSite.id);
@@ -24809,6 +24721,38 @@ internalSitesRoutes.delete("/:siteId/domains/:domainId", requireInternalAdminApi
24809
24721
  })) });
24810
24722
  });
24811
24723
  //#endregion
24724
+ //#region src/middleware/config.ts
24725
+ /**
24726
+ * Config Middleware
24727
+ *
24728
+ * Loads settings from DB, resolves app config and theme.
24729
+ * Apply only to route groups that need config/theme data —
24730
+ * skip for /healthz, /media/*, /favicon.ico, /api/auth/*, etc.
24731
+ */
24732
+ /**
24733
+ * Middleware that loads settings, resolves app config, and builds theme CSS.
24734
+ *
24735
+ * Sets `allSettings`, `appConfig`, and `themeStyle` on the Hono context.
24736
+ */ function withConfig() {
24737
+ return async (c, next) => {
24738
+ const allSettings = await c.var.services.settings.getAll();
24739
+ c.set("allSettings", allSettings);
24740
+ const publicRequestOrigin = new URL(c.var.publicRequestUrl).origin;
24741
+ const siteUrlOverride = getSiteResolutionMode(c.env) === "host-based" ? `${publicRequestOrigin}${c.var.currentSiteDomain?.pathPrefix ?? ""}` : getConfiguredSingleSiteUrl(c.env) || `${publicRequestOrigin}${getConfiguredSingleSitePathPrefix(c.env)}`;
24742
+ const appConfig = resolveConfig(c.env, allSettings, { siteUrl: siteUrlOverride });
24743
+ c.set("appConfig", appConfig);
24744
+ const activeTheme = resolveBuiltinTheme(appConfig.themeId);
24745
+ const fontTheme = BUILTIN_FONT_THEMES.find((f) => f.id === appConfig.fontThemeId);
24746
+ const fontOverrides = {
24747
+ ...getCjkSerifCssVariables(appConfig.cjkSerifFont),
24748
+ ...fontTheme ? getFontThemeCssVariables(fontTheme) : {}
24749
+ };
24750
+ const themeStyle = buildThemeStyle(activeTheme, appConfig.themeMode, fontOverrides);
24751
+ c.set("themeStyle", themeStyle);
24752
+ await next();
24753
+ };
24754
+ }
24755
+ //#endregion
24812
24756
  //#region src/lib/webhook-signature.ts
24813
24757
  /**
24814
24758
  * GitHub webhook HMAC-SHA256 signature verification.
@@ -24864,7 +24808,7 @@ function timingSafeEqual(a, b) {
24864
24808
  * Webhook receiver (HMAC-verified, no session auth) and admin endpoints
24865
24809
  * (session/token auth) for managing GitHub Sync configuration.
24866
24810
  */ var githubSyncWebhookRoutes = new Hono();
24867
- githubSyncWebhookRoutes.post("/webhook", async (c) => {
24811
+ githubSyncWebhookRoutes.post("/webhook", withConfig(), async (c) => {
24868
24812
  const secret = getGitHubAppConfig(c.env)?.webhookSecret ?? await c.var.services.settings.get("GITHUB_SYNC_WEBHOOK_SECRET");
24869
24813
  if (!secret) return c.json({ error: "GitHub Sync not configured" }, 404);
24870
24814
  const signature = c.req.header("X-Hub-Signature-256") ?? "";
@@ -24879,16 +24823,23 @@ githubSyncWebhookRoutes.post("/webhook", async (c) => {
24879
24823
  ok: true,
24880
24824
  skipped: "jant-sync commits"
24881
24825
  });
24882
- await resolveJobQueue(c.env).enqueue({
24883
- kind: "github-sync-pull",
24884
- siteId: c.var.currentSite.id,
24885
- data: {
24886
- ref: payload.ref,
24887
- before: payload.before,
24888
- after: payload.after,
24889
- commits: payload.commits
24890
- }
24826
+ const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
24827
+ storage: c.var.storage,
24828
+ githubApp: getGitHubAppConfig(c.env)
24891
24829
  });
24830
+ const settings = c.var.services.settings;
24831
+ const run = (async () => {
24832
+ try {
24833
+ await syncService.handleWebhookPush(payload);
24834
+ await settings.set("GITHUB_SYNC_LAST_ERROR", "");
24835
+ } catch (err) {
24836
+ const message = err instanceof Error ? err.message : String(err);
24837
+ await settings.set("GITHUB_SYNC_LAST_ERROR", message);
24838
+ }
24839
+ })();
24840
+ try {
24841
+ c.executionCtx?.waitUntil(run);
24842
+ } catch {}
24892
24843
  return c.json({
24893
24844
  ok: true,
24894
24845
  queued: true
@@ -25848,6 +25799,20 @@ manifestRoutes.get("/manifest.webmanifest", (c) => {
25848
25799
  //#endregion
25849
25800
  //#region src/lib/startup-config.ts
25850
25801
  var HOSTED_SHARED_SECRET_MIN_LENGTH = 32;
25802
+ var AUTH_SECRET_GENERATION_HINT = "Generate one with `openssl rand -base64 32`.";
25803
+ var AUTH_SECRET_PLACEHOLDER_MARKER = "replace-me";
25804
+ function getAuthSecretIssueKind(env) {
25805
+ const secret = getAuthSecret(env);
25806
+ if (!secret) return "missing";
25807
+ if (secret.toLowerCase().includes(AUTH_SECRET_PLACEHOLDER_MARKER)) return "placeholder";
25808
+ if (secret.length < 32) return "too-short";
25809
+ return null;
25810
+ }
25811
+ function getAuthSecretReadinessError(kind) {
25812
+ if (kind === "placeholder") return `AUTH_SECRET still uses the placeholder value from .env.example. ${AUTH_SECRET_GENERATION_HINT}`;
25813
+ if (kind === "too-short") return `AUTH_SECRET must be at least 32 characters before Jant can accept traffic. ${AUTH_SECRET_GENERATION_HINT}`;
25814
+ return "AUTH_SECRET must be set before Jant can accept traffic.";
25815
+ }
25851
25816
  function renderConfigurationErrorPage(input) {
25852
25817
  return `<!DOCTYPE html>
25853
25818
  <html lang="en">
@@ -25866,11 +25831,20 @@ ${input.bodyHtml}
25866
25831
  </body>
25867
25832
  </html>`;
25868
25833
  }
25869
- function getAuthSecretErrorHtml() {
25834
+ function getAuthSecretErrorHtml(kind) {
25835
+ const runtimeInstructions = `<p>Set <code>AUTH_SECRET=...</code> in the environment used to start Jant. Generate one with <code>openssl rand -base64 32</code>.</p>
25836
+ <p><strong>Cloudflare Workers:</strong> add <code>AUTH_SECRET</code> as a Worker secret in the dashboard under Variables and Secrets, or run <code>wrangler secret put AUTH_SECRET</code>.</p>`;
25870
25837
  return renderConfigurationErrorPage({
25871
- title: "AUTH_SECRET is not set",
25872
- bodyHtml: `<p>Jant needs a 32+ character auth secret to sign sessions.</p><p>Set <code>AUTH_SECRET=...</code> in the environment used to start Jant.</p>
25873
- <p><strong>Cloudflare Workers:</strong> add <code>AUTH_SECRET</code> as a Worker secret in the dashboard under Variables and Secrets, or run <code>wrangler secret put AUTH_SECRET</code>.</p>`,
25838
+ title: {
25839
+ missing: "AUTH_SECRET is not set",
25840
+ placeholder: "AUTH_SECRET is still the placeholder from .env.example",
25841
+ "too-short": `AUTH_SECRET is too short (must be at least 32 characters)`
25842
+ }[kind],
25843
+ bodyHtml: `${{
25844
+ missing: `<p>Jant needs a 32+ character auth secret to sign sessions.</p>`,
25845
+ placeholder: `<p>The current <code>AUTH_SECRET</code> still contains the <code>replace-me</code> placeholder from <code>.env.example</code>. This value is publicly known and unsafe to use; replace it with a real secret before serving traffic.</p>`,
25846
+ "too-short": `<p>Jant needs an auth secret of at least 32 characters to sign sessions. The current value is too short.</p>`
25847
+ }[kind]}${runtimeInstructions}`,
25874
25848
  docsHref: "https://github.com/jant-me/jant/blob/main/docs/configuration.md#required"
25875
25849
  });
25876
25850
  }
@@ -25957,7 +25931,8 @@ function getRuntimeConfigurationErrorPage(message) {
25957
25931
  * getStartupConfigurationErrorPage({ AUTH_SECRET: "secret" }) // null
25958
25932
  * ```
25959
25933
  */ function getStartupConfigurationErrorPage(env) {
25960
- if (!getAuthSecret(env)) return getAuthSecretErrorHtml();
25934
+ const authSecretIssue = getAuthSecretIssueKind(env);
25935
+ if (authSecretIssue) return getAuthSecretErrorHtml(authSecretIssue);
25961
25936
  const hostBasedIssues = collectHostBasedStartupConfigurationIssues(env);
25962
25937
  if (hostBasedIssues.length > 0) return getHostBasedConfigurationErrorHtml(hostBasedIssues);
25963
25938
  return null;
@@ -26008,38 +25983,6 @@ function getRuntimeConfigurationErrorPage(message) {
26008
25983
  throw err;
26009
25984
  };
26010
25985
  //#endregion
26011
- //#region src/middleware/config.ts
26012
- /**
26013
- * Config Middleware
26014
- *
26015
- * Loads settings from DB, resolves app config and theme.
26016
- * Apply only to route groups that need config/theme data —
26017
- * skip for /healthz, /media/*, /favicon.ico, /api/auth/*, etc.
26018
- */
26019
- /**
26020
- * Middleware that loads settings, resolves app config, and builds theme CSS.
26021
- *
26022
- * Sets `allSettings`, `appConfig`, and `themeStyle` on the Hono context.
26023
- */ function withConfig() {
26024
- return async (c, next) => {
26025
- const allSettings = await c.var.services.settings.getAll();
26026
- c.set("allSettings", allSettings);
26027
- const publicRequestOrigin = new URL(c.var.publicRequestUrl).origin;
26028
- const siteUrlOverride = getSiteResolutionMode(c.env) === "host-based" ? `${publicRequestOrigin}${c.var.currentSiteDomain?.pathPrefix ?? ""}` : getConfiguredSingleSiteUrl(c.env) || `${publicRequestOrigin}${getConfiguredSingleSitePathPrefix(c.env)}`;
26029
- const appConfig = resolveConfig(c.env, allSettings, { siteUrl: siteUrlOverride });
26030
- c.set("appConfig", appConfig);
26031
- const activeTheme = resolveBuiltinTheme(appConfig.themeId);
26032
- const fontTheme = BUILTIN_FONT_THEMES.find((f) => f.id === appConfig.fontThemeId);
26033
- const fontOverrides = {
26034
- ...getCjkSerifCssVariables(appConfig.cjkSerifFont),
26035
- ...fontTheme ? getFontThemeCssVariables(fontTheme) : {}
26036
- };
26037
- const themeStyle = buildThemeStyle(activeTheme, appConfig.themeMode, fontOverrides);
26038
- c.set("themeStyle", themeStyle);
26039
- await next();
26040
- };
26041
- }
26042
- //#endregion
26043
25986
  //#region ../../node_modules/.pnpm/hono@4.11.9/node_modules/hono/dist/middleware/secure-headers/secure-headers.js
26044
25987
  var HEADERS_MAP = {
26045
25988
  crossOriginEmbedderPolicy: ["Cross-Origin-Embedder-Policy", "require-corp"],
@@ -31573,7 +31516,8 @@ async function createRequestRuntime(env, publicRequestUrl) {
31573
31516
  //#region src/runtime/readiness.ts
31574
31517
  function getStartupConfigurationReadiness(env) {
31575
31518
  const errors = [];
31576
- if (!getAuthSecret(env)) errors.push("AUTH_SECRET must be set before Jant can accept traffic.");
31519
+ const authSecretIssue = getAuthSecretIssueKind(env);
31520
+ if (authSecretIssue) errors.push(getAuthSecretReadinessError(authSecretIssue));
31577
31521
  for (const issue of getHostBasedStartupConfigurationIssues(env)) errors.push(`${issue.variable}: ${issue.message}`);
31578
31522
  return errors.length > 0 ? {
31579
31523
  ok: false,
@@ -31948,4 +31892,4 @@ async function servePublicStorage(c) {
31948
31892
  return app;
31949
31893
  }
31950
31894
  //#endregion
31951
- export { NAV_ITEM_TYPES$2 as A, toPostView as C, MAX_MEDIA_ATTACHMENTS as D, FORMATS$2 as E, BUILTIN_COLOR_THEMES as F, getPublicAssetBasePath as I, isAssetPath as L, STATUSES$2 as M, TEXT_ATTACHMENT_CONTENT_FORMATS as N, MAX_PINNED_POSTS as O, buildThemeStyle as P, toNavItemViews as S, toSearchResultView as T, createMediaContext as _, createSiteService as a, toMediaView as b, resolveConfig as c, getFontThemeCssVariables as d, defaultFeedRenderer as f, schema_exports$1 as g, createNodeDatabase as h, createNodeRequestRuntime as i, SORT_ORDERS as j, MEDIA_KINDS as k, BUILTIN_FONT_THEMES as l, sqliteSchemaBundle as m, createRequestRuntime as n, resolveDatabaseDialect as o, pgSchemaBundle as p, createNodeCliRuntime as r, getHostBasedStartupConfigurationIssues as s, createApp as t, getCjkSerifCssVariables as u, toArchiveGroups as v, toPostViews as w, toNavItemView as x, toArchiveGroupsWithMedia as y };
31895
+ export { SORT_ORDERS as A, toPostViews as C, MAX_PINNED_POSTS as D, MAX_MEDIA_ATTACHMENTS as E, getPublicAssetBasePath as F, isAssetPath as I, TEXT_ATTACHMENT_CONTENT_FORMATS as M, buildThemeStyle as N, MEDIA_KINDS as O, BUILTIN_COLOR_THEMES as P, toPostView as S, FORMATS$2 as T, toArchiveGroups as _, resolveDatabaseDialect as a, toNavItemView as b, BUILTIN_FONT_THEMES as c, defaultFeedRenderer as d, pgSchemaBundle as f, createMediaContext as g, schema_exports$1 as h, createSiteService as i, STATUSES$2 as j, NAV_ITEM_TYPES$2 as k, getCjkSerifCssVariables as l, createNodeDatabase as m, createNodeCliRuntime as n, getHostBasedStartupConfigurationIssues as o, sqliteSchemaBundle as p, createNodeRequestRuntime as r, resolveConfig as s, createApp as t, getFontThemeCssVariables as u, toArchiveGroupsWithMedia as v, toSearchResultView as w, toNavItemViews as x, toMediaView as y };