@jant/core 0.3.43 → 0.3.44

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.
@@ -4,6 +4,7 @@ import { C as coalesceDisplayText, S as shouldUseSecureCookies, _ as getInternal
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";
6
6
  import { I18n } from "@lingui/core";
7
+ import * as lucideIcons from "lucide-static";
7
8
  import { z } from "zod";
8
9
  import { fromString, typeidUnboxed } from "typeid-js";
9
10
  import { decode } from "blurhash";
@@ -13,7 +14,6 @@ import { drizzle } from "drizzle-orm/better-sqlite3";
13
14
  import { drizzle as drizzle$1 } from "drizzle-orm/d1";
14
15
  import { check, foreignKey, index, integer, primaryKey, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
15
16
  import { boolean, check as check$1, customType, foreignKey as foreignKey$1, index as index$1, integer as integer$1, pgTable, primaryKey as primaryKey$1, text as text$1, timestamp, unique, uniqueIndex as uniqueIndex$1 } from "drizzle-orm/pg-core";
16
- import * as lucideIcons from "lucide-static";
17
17
  import { APIError, betterAuth } from "better-auth";
18
18
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
19
19
  import { verifyPassword } from "better-auth/crypto";
@@ -3352,15 +3352,289 @@ 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.43-8e1ad0b8fb20998c";
3355
+ var CORE_VERSION = "0.3.44-c595e1fa6d741ba8";
3356
3356
  var CLIENT_JS_FILE = "/_assets/client-D95FNDg5.js";
3357
3357
  var CLIENT_AUTH_JS_FILE = "/_assets/client-auth-CXILhW1b.js";
3358
- var CLIENT_CSS_FILE = "/_assets/client-C_kImWZj.css";
3358
+ var CLIENT_CSS_FILE = "/_assets/client-BQH7AQ24.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";
3362
3362
  var CLIENT_CJK_KR_CSS_FILE = "/_assets/client-cjk-kr-_3ZNI2ZP.css";
3363
3363
  //#endregion
3364
+ //#region src/ui/shared/icon-collector.ts
3365
+ /**
3366
+ * Request-scoped icon collector for SSR SVG sprite pattern.
3367
+ *
3368
+ * The <Icon> component registers each used icon name here during render.
3369
+ * At the end of <body>, <IconSprite> reads the collected set and emits a
3370
+ * single <svg><symbol>...</symbol></svg> block so every <use href="#icon-x">
3371
+ * reference in the page resolves to a definition.
3372
+ *
3373
+ * Mirrors the I18nProvider pattern in i18n/context.tsx: Hono JSX renders
3374
+ * synchronously per request, so a module-level singleton is safe.
3375
+ */ var currentCollector = null;
3376
+ /**
3377
+ * Start a new collection scope for the current render pass.
3378
+ * Call at the top of the root layout before children render.
3379
+ */ function resetIconCollector() {
3380
+ currentCollector = /* @__PURE__ */ new Set();
3381
+ }
3382
+ /**
3383
+ * Register an icon as used during this render.
3384
+ * Safe to call even when no collector is active (no-op).
3385
+ */ function collectIcon(name) {
3386
+ currentCollector?.add(name);
3387
+ }
3388
+ /**
3389
+ * Get the icon names collected so far during this render.
3390
+ * Returns an empty set if no collection scope is active.
3391
+ */ function getCollectedIcons() {
3392
+ return currentCollector ?? /* @__PURE__ */ new Set();
3393
+ }
3394
+ //#endregion
3395
+ //#region src/lib/featured-icons.ts
3396
+ /**
3397
+ * Shared icon definitions for featured post affordances.
3398
+ *
3399
+ * These paths are reused across Hono JSX, Lit, and exported static markup so
3400
+ * "Featured" keeps one visual language everywhere.
3401
+ */ var FEATURED_SPARKLE_PATH = "M12 3 10.1 10.1 3 12l7.1 1.9L12 21l1.9-7.1L21 12l-7.1-1.9Z";
3402
+ var FEATURED_SPARKLE_OFF_SLASH_PATH = "M4 4 20 20";
3403
+ /**
3404
+ * Build inline SVG markup for the shared featured sparkle icon.
3405
+ *
3406
+ * @param options - Render options for the sparkle icon.
3407
+ * @returns SVG markup string for inline insertion.
3408
+ * @example
3409
+ * getFeaturedIconSvg({ off: true, className: "icon-fine" });
3410
+ */ function getFeaturedIconSvg(options = {}) {
3411
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"${options.className ? ` class="${options.className}"` : ""} aria-hidden="true"><path d="${FEATURED_SPARKLE_PATH}" />${options.off ? `<path d="${FEATURED_SPARKLE_OFF_SLASH_PATH}" />` : ""}</svg>`;
3412
+ }
3413
+ //#endregion
3414
+ //#region src/lib/decorative-quote-mark.ts
3415
+ var DECORATIVE_QUOTE_MARK_VIEWBOX = "0 0 96 96";
3416
+ var DECORATIVE_QUOTE_MARK_PATHS = ["M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z", "M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z"];
3417
+ DECORATIVE_QUOTE_MARK_PATHS.map((path) => `<path fill="currentColor" d="${path}" />`).join("");
3418
+ //#endregion
3419
+ //#region src/ui/shared/custom-icons.ts
3420
+ /**
3421
+ * Custom (non-lucide) SVG symbol definitions used by the icon sprite.
3422
+ *
3423
+ * Icons here fall into three groups:
3424
+ * 1. Jant-specific paths (decorative quote mark, featured sparkle).
3425
+ * 2. Lucide-equivalent paths the UI uses with non-default stroke widths
3426
+ * or sizes that don't match the stock lucide symbol (we keep them as
3427
+ * custom symbols to preserve exact visual fidelity during refactor).
3428
+ * 3. Fixed-color SVGs (video play overlay) that don't use currentColor.
3429
+ *
3430
+ * Each entry provides everything needed to render a <symbol> element:
3431
+ * <symbol id="icon-${name}" viewBox={viewBox}>{inner}</symbol>
3432
+ * Consumers of <Icon name="..."> pass `size` / `className` on the outer
3433
+ * <svg><use/></svg>; `<symbol>` children inherit the outer attributes.
3434
+ */ var STROKE_THIN = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.35\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
3435
+ var STROKE_POST_BADGE = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
3436
+ var CUSTOM_SYMBOLS = {
3437
+ "featured-sparkle": {
3438
+ viewBox: "0 0 24 24",
3439
+ inner: `<path ${STROKE_THIN} d="${FEATURED_SPARKLE_PATH}" />`
3440
+ },
3441
+ "featured-sparkle-off": {
3442
+ viewBox: "0 0 24 24",
3443
+ inner: `<path ${STROKE_THIN} d="${FEATURED_SPARKLE_PATH}" /><path ${STROKE_THIN} d="${FEATURED_SPARKLE_OFF_SLASH_PATH}" />`
3444
+ },
3445
+ "decorative-quote": {
3446
+ viewBox: DECORATIVE_QUOTE_MARK_VIEWBOX,
3447
+ inner: DECORATIVE_QUOTE_MARK_PATHS.map((path) => `<path fill="currentColor" d="${path}" />`).join("")
3448
+ },
3449
+ "post-collection-lock": {
3450
+ viewBox: "0 0 16 16",
3451
+ inner: `<rect ${STROKE_THIN} x="3" y="5.05" width="10" height="8.15" rx="2.2" /><path ${STROKE_THIN} d="M5.1 5.05V4.2a1.1 1.1 0 0 1 1.1-1.1h3.6a1.1 1.1 0 0 1 1.1 1.1v.85" />`
3452
+ },
3453
+ "post-menu-dots": {
3454
+ viewBox: "0 0 24 24",
3455
+ inner: `<circle cx="5" cy="12" r="1.75" fill="currentColor" /><circle cx="12" cy="12" r="1.75" fill="currentColor" /><circle cx="19" cy="12" r="1.75" fill="currentColor" />`
3456
+ },
3457
+ "post-external-link": {
3458
+ viewBox: "0 0 24 24",
3459
+ inner: `<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 17 17 7" /><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M9 7h8v8" />`
3460
+ },
3461
+ "post-reply": {
3462
+ viewBox: "0 0 24 24",
3463
+ inner: `<polyline fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" points="9 17 4 12 9 7" /><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M20 18v-2a4 4 0 0 0-4-4H4" />`
3464
+ },
3465
+ "link-domain": {
3466
+ viewBox: "0 0 24 24",
3467
+ inner: `<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />`
3468
+ },
3469
+ "link-preview-play": {
3470
+ viewBox: "0 0 68 48",
3471
+ inner: "<path class=\"link-preview-play-bg\" fill=\"rgba(0,0,0,.65)\" d=\"M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55C3.97 2.33 2.27 4.81 1.48 7.74.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z\" /><path fill=\"#fff\" d=\"M45 24L27 14v20\" />"
3472
+ },
3473
+ "link-preview-badge-play": {
3474
+ viewBox: "0 0 16 16",
3475
+ inner: "<path fill=\"currentColor\" d=\"M5.5 3.5v9l7-4.5z\" />"
3476
+ },
3477
+ "toast-success": {
3478
+ viewBox: "0 0 24 24",
3479
+ inner: `<circle fill="none" stroke="currentColor" stroke-width="2" cx="12" cy="12" r="10" /><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="m9 12 2 2 4-4" />`
3480
+ },
3481
+ "toast-error": {
3482
+ viewBox: "0 0 24 24",
3483
+ inner: `<circle fill="none" stroke="currentColor" stroke-width="2" cx="12" cy="12" r="10" /><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="m15 9-6 6M9 9l6 6" />`
3484
+ },
3485
+ "toast-close": {
3486
+ viewBox: "0 0 24 24",
3487
+ inner: `<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M18 6 6 18M6 6l12 12" />`
3488
+ },
3489
+ "post-status-pin": {
3490
+ viewBox: "0 0 24 24",
3491
+ inner: `<line ${STROKE_POST_BADGE} x1="12" x2="12" y1="17" y2="22" /><path ${STROKE_POST_BADGE} d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z" />`
3492
+ },
3493
+ "post-status-private": {
3494
+ viewBox: "0 0 24 24",
3495
+ inner: `<path ${STROKE_POST_BADGE} d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" /><path ${STROKE_POST_BADGE} d="M14.084 14.158a3 3 0 0 1-4.242-4.242" /><path ${STROKE_POST_BADGE} d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" /><path ${STROKE_POST_BADGE} d="m2 2 20 20" />`
3496
+ }
3497
+ };
3498
+ function getCustomSymbol(name) {
3499
+ return CUSTOM_SYMBOLS[name] ?? null;
3500
+ }
3501
+ /**
3502
+ * Return the viewBox for an icon's outer <svg> wrapper.
3503
+ *
3504
+ * This must match the <symbol>'s viewBox so the browser computes the correct
3505
+ * intrinsic aspect ratio. Without this, outer <svg> with `height: auto` in
3506
+ * CSS falls back to the 300×150 replaced-element default instead of the
3507
+ * icon's real aspect ratio.
3508
+ *
3509
+ * Falls back to lucide's "0 0 24 24" for lucide-sourced icons.
3510
+ */ function getIconViewBox(name) {
3511
+ return CUSTOM_SYMBOLS[name]?.viewBox ?? "0 0 24 24";
3512
+ }
3513
+ //#endregion
3514
+ //#region src/ui/shared/Icon.tsx
3515
+ /**
3516
+ * <Icon> — sprite-based SVG icon for SSR pages.
3517
+ *
3518
+ * Renders a lightweight <svg><use href="#icon-${name}"/></svg> stub and
3519
+ * registers the icon name with the request-scoped collector so the final
3520
+ * sprite (rendered by <IconSprite>) contains exactly the icons used on
3521
+ * this page.
3522
+ *
3523
+ * Name can refer to any lucide-static icon (kebab-case) or one of the
3524
+ * custom symbols defined in `custom-icons.ts`. Unknown names render an
3525
+ * empty <svg> — the same failure mode as the previous getIconSvg() path.
3526
+ *
3527
+ * Size: outer <svg> width/height in pixels. Defaults to 24 (lucide default).
3528
+ * Pass `class` to add CSS classes, e.g. for sizing via stylesheet instead
3529
+ * of inline width/height.
3530
+ */ var Icon$1 = ({ name, size, class: cls, "aria-label": ariaLabel, "aria-hidden": ariaHidden }) => {
3531
+ collectIcon(name);
3532
+ const hidden = ariaHidden ?? (ariaLabel ? void 0 : true);
3533
+ return /* @__PURE__ */ jsxDEV$1("svg", {
3534
+ viewBox: getIconViewBox(name),
3535
+ ...size !== void 0 ? {
3536
+ width: size,
3537
+ height: size
3538
+ } : {},
3539
+ ...cls ? { class: cls } : {},
3540
+ ...ariaLabel ? {
3541
+ "aria-label": ariaLabel,
3542
+ role: "img"
3543
+ } : {},
3544
+ ...hidden ? { "aria-hidden": "true" } : {},
3545
+ children: /* @__PURE__ */ jsxDEV$1("use", { href: `#icon-${name}` })
3546
+ });
3547
+ };
3548
+ //#endregion
3549
+ //#region src/lib/icons.ts
3550
+ /**
3551
+ * Shared icon utilities.
3552
+ *
3553
+ * Provides a small wrapper around lucide-static so server-rendered UI can fetch
3554
+ * SVG markup by kebab-case icon name.
3555
+ */
3556
+ /**
3557
+ * Convert a kebab-case icon name to PascalCase for lucide-static lookup.
3558
+ *
3559
+ * @param name - Kebab-case icon name such as "book-open"
3560
+ * @returns PascalCase name such as "BookOpen"
3561
+ */ function toPascalCase(name) {
3562
+ return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
3563
+ }
3564
+ /**
3565
+ * Get SVG markup for a Lucide icon by kebab-case name.
3566
+ *
3567
+ * @param name - Kebab-case icon name
3568
+ * @returns SVG string or null when the icon is unknown
3569
+ *
3570
+ * @example
3571
+ * ```ts
3572
+ * getIconSvg("book-open");
3573
+ * ```
3574
+ */ function getIconSvg(name) {
3575
+ const svg = lucideIcons[toPascalCase(name)];
3576
+ return typeof svg === "string" ? svg : null;
3577
+ }
3578
+ /**
3579
+ * Get the inner SVG contents for a Lucide icon (the path children only,
3580
+ * without the outer <svg> wrapper). Used by the icon sprite to build
3581
+ * <symbol> definitions.
3582
+ *
3583
+ * @param name - Kebab-case icon name
3584
+ * @returns Inner SVG markup (e.g. "<path ... />"), or null when unknown
3585
+ *
3586
+ * @example
3587
+ * ```ts
3588
+ * getIconInnerSvg("book-open");
3589
+ * // -> "<path d=\"...\"/><path d=\"...\"/>"
3590
+ * ```
3591
+ */ function getIconInnerSvg(name) {
3592
+ const svg = getIconSvg(name);
3593
+ if (!svg) return null;
3594
+ const openTagEnd = svg.indexOf(">");
3595
+ const closeTagStart = svg.lastIndexOf("</svg>");
3596
+ if (openTagEnd < 0 || closeTagStart < 0) return null;
3597
+ return svg.slice(openTagEnd + 1, closeTagStart);
3598
+ }
3599
+ /**
3600
+ * Default stroke/fill attributes inherited by <symbol> children when a
3601
+ * lucide icon is referenced via <use>. These mirror the attributes lucide
3602
+ * normally sets on the outer <svg> so the currentColor-based theming keeps
3603
+ * working through <use>.
3604
+ */ var LUCIDE_SYMBOL_ATTRS = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
3605
+ var LUCIDE_VIEWBOX = "0 0 24 24";
3606
+ //#endregion
3607
+ //#region src/ui/shared/IconSprite.tsx
3608
+ /**
3609
+ * <IconSprite> — emits the SVG symbol definitions used by this render.
3610
+ *
3611
+ * Must be rendered AFTER all <Icon> usages in the document (e.g. at the
3612
+ * end of <body>) so the collector has the full set of icon names. Hono
3613
+ * JSX stringifies synchronously in document order, so children declared
3614
+ * earlier in the tree are evaluated before this component.
3615
+ *
3616
+ * <use href="#icon-x"> anywhere in the document resolves correctly even
3617
+ * when the <symbol> definition comes after the reference, since browsers
3618
+ * wire up the references after the full document is parsed.
3619
+ */ function buildSymbol(name) {
3620
+ const custom = getCustomSymbol(name);
3621
+ if (custom) return `<symbol id="icon-${name}" viewBox="${custom.viewBox}">${custom.inner}</symbol>`;
3622
+ const inner = getIconInnerSvg(name);
3623
+ if (inner === null) return null;
3624
+ return `<symbol id="icon-${name}" viewBox="${LUCIDE_VIEWBOX}" ${LUCIDE_SYMBOL_ATTRS}>${inner}</symbol>`;
3625
+ }
3626
+ var IconSprite = () => {
3627
+ const symbols = Array.from(getCollectedIcons()).sort().map(buildSymbol).filter((s) => s !== null).join("");
3628
+ if (!symbols) return null;
3629
+ return /* @__PURE__ */ jsxDEV$1("svg", {
3630
+ xmlns: "http://www.w3.org/2000/svg",
3631
+ style: "display:none",
3632
+ "aria-hidden": "true",
3633
+ "data-icon-sprite": true,
3634
+ children: raw(symbols)
3635
+ });
3636
+ };
3637
+ //#endregion
3364
3638
  //#region src/ui/layouts/BaseLayout.tsx
3365
3639
  /**
3366
3640
  * Base HTML Layout
@@ -3371,6 +3645,7 @@ var CLIENT_CJK_KR_CSS_FILE = "/_assets/client-cjk-kr-_3ZNI2ZP.css";
3371
3645
  * In dev mode (Vite), serves assets via Vite's dev server.
3372
3646
  * In production, serves pre-built assets with content-hashed filenames.
3373
3647
  */ var BaseLayout = ({ title, description, lang, c, toast, faviconHref, appleTouchHref, faviconUrl, faviconVersion, socialImageUrl, canonicalHref, noindex, isAuthenticated = false, clientBundle, children }) => {
3648
+ resetIconCollector();
3374
3649
  const resolvedLang = lang ?? (c ? c.get("lang") : "en");
3375
3650
  const appConfig = c ? c.get("appConfig") : void 0;
3376
3651
  const resolvedSocialImagePath = socialImageUrl ?? faviconUrl ?? appConfig?.siteAvatarUrl ?? getJantIconHref("socialImage", appConfig?.sitePathPrefix || "");
@@ -3398,7 +3673,7 @@ var CLIENT_CJK_KR_CSS_FILE = "/_assets/client-cjk-kr-_3ZNI2ZP.css";
3398
3673
  const cjkSerifFont = appConfig?.cjkSerifFont ?? "off";
3399
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;
3400
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);
3401
- const faviconAssetVersion = resolvedFaviconVersion || "0.3.43-8e1ad0b8fb20998c";
3676
+ const faviconAssetVersion = resolvedFaviconVersion || "0.3.44-c595e1fa6d741ba8";
3402
3677
  const resolvedFaviconHref = faviconHref ?? (faviconAssetVersion ? toPublicPath(`/favicon.ico?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/favicon.ico", sitePathPrefix));
3403
3678
  const resolvedAppleTouchHref = appleTouchHref ?? (faviconAssetVersion ? toPublicPath(`/apple-touch-icon.png?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/apple-touch-icon.png", sitePathPrefix));
3404
3679
  const socialImageHref = resolvedSocialImagePath && (isFullUrl(resolvedSocialImagePath) || resolvedSocialImagePath.startsWith("//") ? resolvedSocialImagePath : toAbsoluteSiteUrl(resolvedSocialImagePath, appConfig?.siteUrl || "", sitePathPrefix));
@@ -3559,46 +3834,18 @@ var CLIENT_CJK_KR_CSS_FILE = "/_assets/client-cjk-kr-_3ZNI2ZP.css";
3559
3834
  class: `toast ${toast.type === "error" ? "toast-error" : "toast-success"}`,
3560
3835
  "data-init": "el.closest('[popover]').showPopover(); history.replaceState({}, '', location.pathname); setTimeout(() => { el.classList.add('toast-out'); el.addEventListener('animationend', () => el.remove()) }, 3000)",
3561
3836
  children: [
3562
- toast.type === "error" ? /* @__PURE__ */ jsxDEV$1("svg", {
3563
- xmlns: "http://www.w3.org/2000/svg",
3564
- fill: "none",
3565
- viewBox: "0 0 24 24",
3566
- "stroke-width": "2",
3567
- stroke: "currentColor",
3568
- children: [/* @__PURE__ */ jsxDEV$1("circle", {
3569
- cx: "12",
3570
- cy: "12",
3571
- r: "10"
3572
- }), /* @__PURE__ */ jsxDEV$1("path", { d: "m15 9-6 6M9 9l6 6" })]
3573
- }) : /* @__PURE__ */ jsxDEV$1("svg", {
3574
- xmlns: "http://www.w3.org/2000/svg",
3575
- fill: "none",
3576
- viewBox: "0 0 24 24",
3577
- "stroke-width": "2",
3578
- stroke: "currentColor",
3579
- children: [/* @__PURE__ */ jsxDEV$1("circle", {
3580
- cx: "12",
3581
- cy: "12",
3582
- r: "10"
3583
- }), /* @__PURE__ */ jsxDEV$1("path", { d: "m9 12 2 2 4-4" })]
3584
- }),
3837
+ toast.type === "error" ? /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "toast-error" }) : /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "toast-success" }),
3585
3838
  /* @__PURE__ */ jsxDEV$1("span", { children: toast.message }),
3586
3839
  /* @__PURE__ */ jsxDEV$1("button", {
3587
3840
  class: "toast-close",
3588
3841
  "data-on:click": "el.closest('.toast').classList.add('toast-out'); el.closest('.toast').addEventListener('animationend', () => el.closest('.toast').remove())",
3589
- children: /* @__PURE__ */ jsxDEV$1("svg", {
3590
- xmlns: "http://www.w3.org/2000/svg",
3591
- fill: "none",
3592
- viewBox: "0 0 24 24",
3593
- "stroke-width": "2",
3594
- stroke: "currentColor",
3595
- children: /* @__PURE__ */ jsxDEV$1("path", { d: "M18 6 6 18M6 6l12 12" })
3596
- })
3842
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "toast-close" })
3597
3843
  })
3598
3844
  ]
3599
3845
  })
3600
3846
  }),
3601
- customBodyEndHtml && raw(customBodyEndHtml)
3847
+ customBodyEndHtml && raw(customBodyEndHtml),
3848
+ /* @__PURE__ */ jsxDEV$1(IconSprite, {})
3602
3849
  ]
3603
3850
  })]
3604
3851
  })] });
@@ -9054,25 +9301,6 @@ var MediaGallery = ({ attachments, postPermalink }) => {
9054
9301
  });
9055
9302
  };
9056
9303
  //#endregion
9057
- //#region src/lib/featured-icons.ts
9058
- /**
9059
- * Shared icon definitions for featured post affordances.
9060
- *
9061
- * These paths are reused across Hono JSX, Lit, and exported static markup so
9062
- * "Featured" keeps one visual language everywhere.
9063
- */ var FEATURED_SPARKLE_PATH = "M12 3 10.1 10.1 3 12l7.1 1.9L12 21l1.9-7.1L21 12l-7.1-1.9Z";
9064
- var FEATURED_SPARKLE_OFF_SLASH_PATH = "M4 4 20 20";
9065
- /**
9066
- * Build inline SVG markup for the shared featured sparkle icon.
9067
- *
9068
- * @param options - Render options for the sparkle icon.
9069
- * @returns SVG markup string for inline insertion.
9070
- * @example
9071
- * getFeaturedIconSvg({ off: true, className: "icon-fine" });
9072
- */ function getFeaturedIconSvg(options = {}) {
9073
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"${options.className ? ` class="${options.className}"` : ""} aria-hidden="true"><path d="${FEATURED_SPARKLE_PATH}" />${options.off ? `<path d="${FEATURED_SPARKLE_OFF_SLASH_PATH}" />` : ""}</svg>`;
9074
- }
9075
- //#endregion
9076
9304
  //#region src/ui/shared/PostFooter.tsx
9077
9305
  /**
9078
9306
  * Post Footer
@@ -9099,22 +9327,7 @@ var FEATURED_SPARKLE_OFF_SLASH_PATH = "M4 4 20 20";
9099
9327
  children: [showIcon && /* @__PURE__ */ jsxDEV$1("span", {
9100
9328
  class: "post-collection-primary-icon",
9101
9329
  "aria-hidden": "true",
9102
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9103
- xmlns: "http://www.w3.org/2000/svg",
9104
- viewBox: "0 0 16 16",
9105
- fill: "none",
9106
- stroke: "currentColor",
9107
- "stroke-width": "1.35",
9108
- "stroke-linecap": "round",
9109
- "stroke-linejoin": "round",
9110
- children: [/* @__PURE__ */ jsxDEV$1("rect", {
9111
- x: "3",
9112
- y: "5.05",
9113
- width: "10",
9114
- height: "8.15",
9115
- rx: "2.2"
9116
- }), /* @__PURE__ */ jsxDEV$1("path", { d: "M5.1 5.05V4.2a1.1 1.1 0 0 1 1.1-1.1h3.6a1.1 1.1 0 0 1 1.1 1.1v.85" })]
9117
- })
9330
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "post-collection-lock" })
9118
9331
  }), /* @__PURE__ */ jsxDEV$1("span", {
9119
9332
  class: "post-collection-tag-text",
9120
9333
  children: first.title
@@ -9185,29 +9398,9 @@ var PostMenuTriggerButton = ({ className = "post-menu-trigger" }) => {
9185
9398
  "aria-label": i18n._({ id: "JcD7qf" }),
9186
9399
  "aria-expanded": "false",
9187
9400
  "data-post-menu-trigger": true,
9188
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9189
- xmlns: "http://www.w3.org/2000/svg",
9190
- width: "15",
9191
- height: "15",
9192
- viewBox: "0 0 24 24",
9193
- fill: "currentColor",
9194
- children: [
9195
- /* @__PURE__ */ jsxDEV$1("circle", {
9196
- cx: "5",
9197
- cy: "12",
9198
- r: "1.75"
9199
- }),
9200
- /* @__PURE__ */ jsxDEV$1("circle", {
9201
- cx: "12",
9202
- cy: "12",
9203
- r: "1.75"
9204
- }),
9205
- /* @__PURE__ */ jsxDEV$1("circle", {
9206
- cx: "19",
9207
- cy: "12",
9208
- r: "1.75"
9209
- })
9210
- ]
9401
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, {
9402
+ name: "post-menu-dots",
9403
+ size: 15
9211
9404
  })
9212
9405
  });
9213
9406
  };
@@ -9235,17 +9428,7 @@ var PostFooter = ({ post, detail, display }) => {
9235
9428
  "aria-label": featuredLabel,
9236
9429
  "data-tooltip": featuredLabel,
9237
9430
  "data-align": "center",
9238
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9239
- xmlns: "http://www.w3.org/2000/svg",
9240
- viewBox: "0 0 24 24",
9241
- fill: "none",
9242
- stroke: "currentColor",
9243
- "stroke-width": "1.35",
9244
- "stroke-linecap": "round",
9245
- "stroke-linejoin": "round",
9246
- "aria-hidden": "true",
9247
- children: /* @__PURE__ */ jsxDEV$1("path", { d: FEATURED_SPARKLE_PATH })
9248
- })
9431
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "featured-sparkle" })
9249
9432
  }),
9250
9433
  showTimestamp && /* @__PURE__ */ jsxDEV$1(PostPublishedLink, {
9251
9434
  post,
@@ -9257,16 +9440,7 @@ var PostFooter = ({ post, detail, display }) => {
9257
9440
  target: "_blank",
9258
9441
  rel: "noopener noreferrer",
9259
9442
  "aria-label": i18n._({ id: "9dr9Nh" }),
9260
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9261
- xmlns: "http://www.w3.org/2000/svg",
9262
- viewBox: "0 0 24 24",
9263
- fill: "none",
9264
- stroke: "currentColor",
9265
- "stroke-width": "2",
9266
- "stroke-linecap": "round",
9267
- "stroke-linejoin": "round",
9268
- children: [/* @__PURE__ */ jsxDEV$1("path", { d: "M7 17 17 7" }), /* @__PURE__ */ jsxDEV$1("path", { d: "M9 7h8v8" })]
9269
- })
9443
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "post-external-link" })
9270
9444
  }),
9271
9445
  /* @__PURE__ */ jsxDEV$1(CompactCollectionTags, {
9272
9446
  collections: post.collections,
@@ -9281,17 +9455,9 @@ var PostFooter = ({ post, detail, display }) => {
9281
9455
  class: "reply-trigger",
9282
9456
  "aria-label": i18n._({ id: "ImOQa9" }),
9283
9457
  "data-reply-trigger": true,
9284
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9285
- xmlns: "http://www.w3.org/2000/svg",
9286
- width: "14",
9287
- height: "14",
9288
- viewBox: "0 0 24 24",
9289
- fill: "none",
9290
- stroke: "currentColor",
9291
- "stroke-width": "2",
9292
- "stroke-linecap": "round",
9293
- "stroke-linejoin": "round",
9294
- children: [/* @__PURE__ */ jsxDEV$1("polyline", { points: "9 17 4 12 9 7" }), /* @__PURE__ */ jsxDEV$1("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })]
9458
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, {
9459
+ name: "post-reply",
9460
+ size: 14
9295
9461
  })
9296
9462
  }), /* @__PURE__ */ jsxDEV$1(PostMenuTriggerButton, {})]
9297
9463
  })]
@@ -9313,57 +9479,15 @@ var PostFooter = ({ post, detail, display }) => {
9313
9479
  children: [
9314
9480
  /* @__PURE__ */ jsxDEV$1("span", {
9315
9481
  class: "post-status-badge post-status-pinned",
9316
- children: [/* @__PURE__ */ jsxDEV$1("svg", {
9317
- xmlns: "http://www.w3.org/2000/svg",
9318
- viewBox: "0 0 24 24",
9319
- fill: "none",
9320
- stroke: "currentColor",
9321
- "stroke-width": "1.75",
9322
- "stroke-linecap": "round",
9323
- "stroke-linejoin": "round",
9324
- children: [/* @__PURE__ */ jsxDEV$1("line", {
9325
- x1: "12",
9326
- x2: "12",
9327
- y1: "17",
9328
- y2: "22"
9329
- }), /* @__PURE__ */ jsxDEV$1("path", { d: "M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z" })]
9330
- }), "Pinned"]
9482
+ children: [/* @__PURE__ */ jsxDEV$1(Icon$1, { name: "post-status-pin" }), "Pinned"]
9331
9483
  }),
9332
9484
  /* @__PURE__ */ jsxDEV$1("span", {
9333
9485
  class: "post-status-badge post-status-pinned-in-collection",
9334
- children: [/* @__PURE__ */ jsxDEV$1("svg", {
9335
- xmlns: "http://www.w3.org/2000/svg",
9336
- viewBox: "0 0 24 24",
9337
- fill: "none",
9338
- stroke: "currentColor",
9339
- "stroke-width": "1.75",
9340
- "stroke-linecap": "round",
9341
- "stroke-linejoin": "round",
9342
- children: [/* @__PURE__ */ jsxDEV$1("line", {
9343
- x1: "12",
9344
- x2: "12",
9345
- y1: "17",
9346
- y2: "22"
9347
- }), /* @__PURE__ */ jsxDEV$1("path", { d: "M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z" })]
9348
- }), "Pinned"]
9486
+ children: [/* @__PURE__ */ jsxDEV$1(Icon$1, { name: "post-status-pin" }), "Pinned"]
9349
9487
  }),
9350
9488
  /* @__PURE__ */ jsxDEV$1("span", {
9351
9489
  class: "post-status-badge post-status-private",
9352
- children: [/* @__PURE__ */ jsxDEV$1("svg", {
9353
- xmlns: "http://www.w3.org/2000/svg",
9354
- viewBox: "0 0 24 24",
9355
- fill: "none",
9356
- stroke: "currentColor",
9357
- "stroke-width": "1.75",
9358
- "stroke-linecap": "round",
9359
- "stroke-linejoin": "round",
9360
- children: [
9361
- /* @__PURE__ */ jsxDEV$1("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
9362
- /* @__PURE__ */ jsxDEV$1("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
9363
- /* @__PURE__ */ jsxDEV$1("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
9364
- /* @__PURE__ */ jsxDEV$1("path", { d: "m2 2 20 20" })
9365
- ]
9366
- }), "Private"]
9490
+ children: [/* @__PURE__ */ jsxDEV$1(Icon$1, { name: "post-status-private" }), "Private"]
9367
9491
  })
9368
9492
  ]
9369
9493
  });
@@ -9497,29 +9621,17 @@ var LinkPreview = ({ imageUrl, linkUrl, kind, provider }) => {
9497
9621
  isVideo && /* @__PURE__ */ jsxDEV$1("div", {
9498
9622
  class: "link-preview-play",
9499
9623
  "aria-hidden": "true",
9500
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9501
- class: "link-preview-play-icon",
9502
- viewBox: "0 0 68 48",
9503
- xmlns: "http://www.w3.org/2000/svg",
9504
- children: [/* @__PURE__ */ jsxDEV$1("path", {
9505
- class: "link-preview-play-bg",
9506
- d: "M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55C3.97 2.33 2.27 4.81 1.48 7.74.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z",
9507
- fill: "rgba(0,0,0,.65)"
9508
- }), /* @__PURE__ */ jsxDEV$1("path", {
9509
- d: "M45 24L27 14v20",
9510
- fill: "#fff"
9511
- })]
9624
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, {
9625
+ name: "link-preview-play",
9626
+ class: "link-preview-play-icon"
9512
9627
  })
9513
9628
  }),
9514
9629
  providerLabel && /* @__PURE__ */ jsxDEV$1("span", {
9515
9630
  class: "link-preview-badge",
9516
9631
  "aria-hidden": "true",
9517
- children: [isVideo && /* @__PURE__ */ jsxDEV$1("svg", {
9518
- class: "link-preview-badge-icon",
9519
- viewBox: "0 0 16 16",
9520
- xmlns: "http://www.w3.org/2000/svg",
9521
- fill: "currentColor",
9522
- children: /* @__PURE__ */ jsxDEV$1("path", { d: "M5.5 3.5v9l7-4.5z" })
9632
+ children: [isVideo && /* @__PURE__ */ jsxDEV$1(Icon$1, {
9633
+ name: "link-preview-badge-play",
9634
+ class: "link-preview-badge-icon"
9523
9635
  }), providerLabel]
9524
9636
  })
9525
9637
  ]
@@ -9546,25 +9658,15 @@ var LinkPreview = ({ imageUrl, linkUrl, kind, provider }) => {
9546
9658
  class: "feed-link-domain",
9547
9659
  target: "_blank",
9548
9660
  rel: "noopener noreferrer",
9549
- children: [/* @__PURE__ */ jsxDEV$1("svg", {
9550
- class: "feed-link-domain-icon",
9551
- xmlns: "http://www.w3.org/2000/svg",
9552
- fill: "none",
9553
- viewBox: "0 0 24 24",
9554
- "stroke-width": "2",
9555
- stroke: "currentColor",
9556
- children: /* @__PURE__ */ jsxDEV$1("path", { d: "M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" })
9661
+ children: [/* @__PURE__ */ jsxDEV$1(Icon$1, {
9662
+ name: "link-domain",
9663
+ class: "feed-link-domain-icon"
9557
9664
  }), /* @__PURE__ */ jsxDEV$1("span", { children: domain })]
9558
9665
  }) : /* @__PURE__ */ jsxDEV$1("div", {
9559
9666
  class: "feed-link-domain",
9560
- children: [/* @__PURE__ */ jsxDEV$1("svg", {
9561
- class: "feed-link-domain-icon",
9562
- xmlns: "http://www.w3.org/2000/svg",
9563
- fill: "none",
9564
- viewBox: "0 0 24 24",
9565
- "stroke-width": "2",
9566
- stroke: "currentColor",
9567
- children: /* @__PURE__ */ jsxDEV$1("path", { d: "M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" })
9667
+ children: [/* @__PURE__ */ jsxDEV$1(Icon$1, {
9668
+ name: "link-domain",
9669
+ class: "feed-link-domain-icon"
9568
9670
  }), /* @__PURE__ */ jsxDEV$1("span", { children: domain })]
9569
9671
  }));
9570
9672
  const previewEl = !isCompact && post.previewImageUrl && /* @__PURE__ */ jsxDEV$1(LinkPreview, {
@@ -9645,11 +9747,6 @@ var LinkPreview = ({ imageUrl, linkUrl, kind, provider }) => {
9645
9747
  });
9646
9748
  };
9647
9749
  //#endregion
9648
- //#region src/lib/decorative-quote-mark.ts
9649
- var DECORATIVE_QUOTE_MARK_VIEWBOX = "0 0 96 96";
9650
- var DECORATIVE_QUOTE_MARK_PATHS = ["M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z", "M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z"];
9651
- DECORATIVE_QUOTE_MARK_PATHS.map((path) => `<path fill="currentColor" d="${path}" />`).join("");
9652
- //#endregion
9653
9750
  //#region src/ui/shared/DecorativeQuoteMark.tsx
9654
9751
  /**
9655
9752
  * Decorative double-quote mark rendered as SVG so the shape stays consistent
@@ -9658,15 +9755,7 @@ DECORATIVE_QUOTE_MARK_PATHS.map((path) => `<path fill="currentColor" d="${path}"
9658
9755
  class: `decorative-quote-mark${cls ? ` ${cls}` : ""}`,
9659
9756
  "data-direction": direction,
9660
9757
  "aria-hidden": "true",
9661
- children: /* @__PURE__ */ jsxDEV$1("svg", {
9662
- viewBox: DECORATIVE_QUOTE_MARK_VIEWBOX,
9663
- role: "presentation",
9664
- focusable: "false",
9665
- children: DECORATIVE_QUOTE_MARK_PATHS.map((path) => /* @__PURE__ */ jsxDEV$1("path", {
9666
- fill: "currentColor",
9667
- d: path
9668
- }))
9669
- })
9758
+ children: /* @__PURE__ */ jsxDEV$1(Icon$1, { name: "decorative-quote" })
9670
9759
  });
9671
9760
  //#endregion
9672
9761
  //#region src/ui/feed/QuoteCard.tsx
@@ -10427,9 +10516,14 @@ var sites$1 = sqliteTable("site", {
10427
10516
  id: text("id").primaryKey(),
10428
10517
  key: text("key").notNull(),
10429
10518
  status: text("status", { enum: SITE_STATUSES$1 }).notNull().default("active"),
10519
+ provisioningIdempotencyKey: text("provisioning_idempotency_key"),
10430
10520
  createdAt: integer("created_at").notNull(),
10431
10521
  updatedAt: integer("updated_at").notNull()
10432
- }, (table) => [uniqueIndex("uq_site_key").on(table.key), check("chk_site_status", sql`${table.status} IN (${sqlTextEnum$1(SITE_STATUSES$1)})`)]);
10522
+ }, (table) => [
10523
+ uniqueIndex("uq_site_key").on(table.key),
10524
+ uniqueIndex("uq_site_provisioning_idempotency_key").on(table.provisioningIdempotencyKey).where(sql`${table.provisioningIdempotencyKey} IS NOT NULL`),
10525
+ check("chk_site_status", sql`${table.status} IN (${sqlTextEnum$1(SITE_STATUSES$1)})`)
10526
+ ]);
10433
10527
  var siteDomains$1 = sqliteTable("site_domain", {
10434
10528
  id: text("id").primaryKey(),
10435
10529
  siteId: text("site_id").notNull().references(() => sites$1.id, { onDelete: "cascade" }),
@@ -10995,9 +11089,14 @@ var sites = pgTable("site", {
10995
11089
  id: text$1("id").primaryKey(),
10996
11090
  key: text$1("key").notNull(),
10997
11091
  status: text$1("status", { enum: SITE_STATUSES }).notNull().default("active"),
11092
+ provisioningIdempotencyKey: text$1("provisioning_idempotency_key"),
10998
11093
  createdAt: integer$1("created_at").notNull(),
10999
11094
  updatedAt: integer$1("updated_at").notNull()
11000
- }, (table) => [uniqueIndex$1("uq_site_key").on(table.key), check$1("chk_site_status", sql`${table.status} IN (${sqlTextEnum(SITE_STATUSES)})`)]);
11095
+ }, (table) => [
11096
+ uniqueIndex$1("uq_site_key").on(table.key),
11097
+ uniqueIndex$1("uq_site_provisioning_idempotency_key").on(table.provisioningIdempotencyKey).where(sql`${table.provisioningIdempotencyKey} IS NOT NULL`),
11098
+ check$1("chk_site_status", sql`${table.status} IN (${sqlTextEnum(SITE_STATUSES)})`)
11099
+ ]);
11001
11100
  var siteDomains = pgTable("site_domain", {
11002
11101
  id: text$1("id").primaryKey(),
11003
11102
  siteId: text$1("site_id").notNull().references(() => sites.id, { onDelete: "cascade" }),
@@ -11848,36 +11947,6 @@ function createMediaService(db, siteId, databaseSchema = sqliteSchemaBundle, dat
11848
11947
  };
11849
11948
  }
11850
11949
  //#endregion
11851
- //#region src/lib/icons.ts
11852
- /**
11853
- * Shared icon utilities.
11854
- *
11855
- * Provides a small wrapper around lucide-static so server-rendered UI can fetch
11856
- * SVG markup by kebab-case icon name.
11857
- */
11858
- /**
11859
- * Convert a kebab-case icon name to PascalCase for lucide-static lookup.
11860
- *
11861
- * @param name - Kebab-case icon name such as "book-open"
11862
- * @returns PascalCase name such as "BookOpen"
11863
- */ function toPascalCase(name) {
11864
- return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
11865
- }
11866
- /**
11867
- * Get SVG markup for a Lucide icon by kebab-case name.
11868
- *
11869
- * @param name - Kebab-case icon name
11870
- * @returns SVG string or null when the icon is unknown
11871
- *
11872
- * @example
11873
- * ```ts
11874
- * getIconSvg("book-open");
11875
- * ```
11876
- */ function getIconSvg(name) {
11877
- const svg = lucideIcons[toPascalCase(name)];
11878
- return typeof svg === "string" ? svg : null;
11879
- }
11880
- //#endregion
11881
11950
  //#region src/ui/pages/ArchivePage.tsx
11882
11951
  /**
11883
11952
  * Archive Page
@@ -24639,7 +24708,8 @@ var CreateManagedSiteSchema = z.object({
24639
24708
  primaryHost: z.string().trim().toLowerCase().min(1).max(255).regex(/^(?=.{1,255}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/, "Primary host must be a valid hostname."),
24640
24709
  siteName: z.string().trim().min(1).max(120),
24641
24710
  siteLanguage: z.string().trim().max(35).optional(),
24642
- timeZone: z.string().trim().max(100).optional()
24711
+ timeZone: z.string().trim().max(100).optional(),
24712
+ idempotencyKey: z.string().trim().min(1).max(128).optional()
24643
24713
  });
24644
24714
  var ManagedSiteDomainSchema = z.object({
24645
24715
  host: z.string().trim().toLowerCase().min(1).max(255).regex(/^(?=.{1,255}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/, "Domain host must be a valid hostname."),
@@ -30366,10 +30436,28 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
30366
30436
  const pathPrefix = domain.pathPrefix ?? "";
30367
30437
  return `${protocol}//${domain.host}${pathPrefix}`;
30368
30438
  }
30439
+ async function loadByIdempotencyKey(targetDb, idempotencyKey) {
30440
+ const siteRow = (await targetDb.select().from(sites).where(eq(sites.provisioningIdempotencyKey, idempotencyKey)).limit(1))[0];
30441
+ if (!siteRow) return null;
30442
+ const domainRow = (await targetDb.select().from(siteDomains).where(and(eq(siteDomains.siteId, siteRow.id), eq(siteDomains.kind, "primary"))).limit(1))[0];
30443
+ if (!domainRow) return null;
30444
+ return {
30445
+ site: toSite(siteRow),
30446
+ domain: toSiteDomain(domainRow)
30447
+ };
30448
+ }
30369
30449
  async function createWithDatabase(targetDb, input) {
30370
30450
  const siteKey = input.key.trim();
30371
30451
  const primaryHost = input.primaryHost.trim().toLowerCase();
30372
30452
  const siteName = input.siteName.trim();
30453
+ const idempotencyKey = input.idempotencyKey?.trim() || null;
30454
+ if (idempotencyKey) {
30455
+ const existing = await loadByIdempotencyKey(targetDb, idempotencyKey);
30456
+ if (existing) {
30457
+ if (existing.site.key !== siteKey || existing.domain.host !== primaryHost) throw new ConflictError("Idempotency key was reused with a different site key or primary host.");
30458
+ return existing;
30459
+ }
30460
+ }
30373
30461
  if ((await targetDb.select({ id: sites.id }).from(sites).where(eq(sites.key, siteKey)).limit(1))[0]) throw new ConflictError("Site key is already in use.");
30374
30462
  if ((await targetDb.select({ id: siteDomains.id }).from(siteDomains).where(eq(siteDomains.host, primaryHost)).limit(1))[0]) throw new ConflictError("Primary host is already in use.");
30375
30463
  const timestamp = now();
@@ -30379,6 +30467,7 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
30379
30467
  id: siteId,
30380
30468
  key: siteKey,
30381
30469
  status: "active",
30470
+ provisioningIdempotencyKey: idempotencyKey,
30382
30471
  createdAt: timestamp,
30383
30472
  updatedAt: timestamp
30384
30473
  }).returning())[0];