@mailoo/blog 0.0.0-stage → 1.33.0

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 (59) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +142 -2
  3. package/dist/client/article-body.cjs +115 -0
  4. package/dist/client/article-body.cjs.map +1 -0
  5. package/dist/client/article-body.d.cts +15 -0
  6. package/dist/client/article-body.d.ts +15 -0
  7. package/dist/client/article-body.js +80 -0
  8. package/dist/client/article-body.js.map +1 -0
  9. package/dist/client/index.cjs +776 -0
  10. package/dist/client/index.cjs.map +1 -0
  11. package/dist/client/index.d.cts +6 -0
  12. package/dist/client/index.d.ts +6 -0
  13. package/dist/client/index.js +731 -0
  14. package/dist/client/index.js.map +1 -0
  15. package/dist/client/linked-links.cjs +125 -0
  16. package/dist/client/linked-links.cjs.map +1 -0
  17. package/dist/client/linked-links.d.cts +31 -0
  18. package/dist/client/linked-links.d.ts +31 -0
  19. package/dist/client/linked-links.js +100 -0
  20. package/dist/client/linked-links.js.map +1 -0
  21. package/dist/client/marks.cjs +300 -0
  22. package/dist/client/marks.cjs.map +1 -0
  23. package/dist/client/marks.d.cts +30 -0
  24. package/dist/client/marks.d.ts +30 -0
  25. package/dist/client/marks.js +276 -0
  26. package/dist/client/marks.js.map +1 -0
  27. package/dist/client/printable.cjs +106 -0
  28. package/dist/client/printable.cjs.map +1 -0
  29. package/dist/client/printable.d.cts +12 -0
  30. package/dist/client/printable.d.ts +12 -0
  31. package/dist/client/printable.js +82 -0
  32. package/dist/client/printable.js.map +1 -0
  33. package/dist/client/share.cjs +234 -0
  34. package/dist/client/share.cjs.map +1 -0
  35. package/dist/client/share.d.cts +41 -0
  36. package/dist/client/share.d.ts +41 -0
  37. package/dist/client/share.js +205 -0
  38. package/dist/client/share.js.map +1 -0
  39. package/dist/index.cjs +89 -0
  40. package/dist/index.cjs.map +1 -0
  41. package/dist/index.d.cts +5 -0
  42. package/dist/index.d.ts +5 -0
  43. package/dist/index.js +60 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/routes/index.cjs +266 -0
  46. package/dist/routes/index.cjs.map +1 -0
  47. package/dist/routes/index.d.cts +87 -0
  48. package/dist/routes/index.d.ts +87 -0
  49. package/dist/routes/index.js +240 -0
  50. package/dist/routes/index.js.map +1 -0
  51. package/dist/server/index.cjs +368 -0
  52. package/dist/server/index.cjs.map +1 -0
  53. package/dist/server/index.d.cts +132 -0
  54. package/dist/server/index.d.ts +132 -0
  55. package/dist/server/index.js +339 -0
  56. package/dist/server/index.js.map +1 -0
  57. package/dist/utils-DXbN9MTi.d.cts +120 -0
  58. package/dist/utils-DXbN9MTi.d.ts +120 -0
  59. package/package.json +110 -3
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/blog-article-marks.tsx"],"sourcesContent":["'use client'\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport type {\n BlogArticleMarkCounts,\n BlogArticleMarkState,\n BlogPostReactionSentiment,\n} from '../types.js'\n\nconst STORAGE_KEY = 'mailoo_blog_visitor_key'\nconst VISITOR_STATE_PREFIX = 'mailoo_blog_mark:'\n\nconst BTN =\n 'inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-60'\nconst BTN_IDLE =\n 'border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800'\nconst BTN_ACTIVE =\n 'border-blue-600 bg-blue-50 text-blue-800 focus:ring-blue-500 dark:border-blue-400 dark:bg-blue-950 dark:text-blue-200'\n\nfunction createVisitorKey(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n const v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nfunction readVisitorKey(): string {\n try {\n const existing = localStorage.getItem(STORAGE_KEY)\n if (existing && existing.length >= 32) return existing\n const next = createVisitorKey()\n localStorage.setItem(STORAGE_KEY, next)\n return next\n } catch {\n return createVisitorKey()\n }\n}\n\ntype LocalMark = {\n sentiment: BlogPostReactionSentiment | null\n hasFeedback: boolean\n}\n\nfunction readLocalMark(slug: string): LocalMark {\n try {\n const raw = localStorage.getItem(`${VISITOR_STATE_PREFIX}${slug}`)\n if (!raw) return { sentiment: null, hasFeedback: false }\n const parsed = JSON.parse(raw) as LocalMark\n return {\n sentiment:\n parsed.sentiment === 'LIKE' || parsed.sentiment === 'DISLIKE'\n ? parsed.sentiment\n : null,\n hasFeedback: Boolean(parsed.hasFeedback),\n }\n } catch {\n return { sentiment: null, hasFeedback: false }\n }\n}\n\nfunction writeLocalMark(slug: string, mark: LocalMark): void {\n try {\n localStorage.setItem(\n `${VISITOR_STATE_PREFIX}${slug}`,\n JSON.stringify(mark)\n )\n } catch {\n // ignore quota / private mode\n }\n}\n\nexport type BlogArticleMarksLabels = {\n like: string\n dislike: string\n feedback: string\n feedbackPlaceholder: string\n submit: string\n cancel: string\n dislikeRequiresComment: string\n error: string\n}\n\nexport type BlogArticleMarksProps = {\n slug: string\n locale?: string\n likeCount?: number\n dislikeCount?: number\n feedbackCount?: number\n /** Same-origin BFF base, e.g. `/api/v1/blog` */\n apiBasePath?: string\n labels: BlogArticleMarksLabels\n className?: string\n}\n\nasync function postMark(\n apiBasePath: string,\n slug: string,\n kind: 'reaction' | 'feedback',\n body: Record<string, unknown>\n): Promise<BlogArticleMarkState> {\n const res = await fetch(\n `${apiBasePath}/slug/${encodeURIComponent(slug)}/${kind}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n }\n )\n const json = (await res.json()) as {\n data?: BlogArticleMarkState\n error?: string\n message?: string\n }\n if (!res.ok || !json.data) {\n throw new Error(json.message || json.error || `HTTP ${res.status}`)\n }\n return json.data\n}\n\n/**\n * Public like / dislike / feedback controls with counts.\n * POSTs to same-origin BFF (never sends API key).\n */\nexport function BlogArticleMarks({\n slug,\n locale,\n likeCount = 0,\n dislikeCount = 0,\n feedbackCount = 0,\n apiBasePath = '/api/v1/blog',\n labels,\n className,\n}: BlogArticleMarksProps) {\n const [visitorKey, setVisitorKey] = useState<string | null>(null)\n const [counts, setCounts] = useState<BlogArticleMarkCounts>({\n likeCount,\n dislikeCount,\n feedbackCount,\n })\n const [sentiment, setSentiment] = useState<BlogPostReactionSentiment | null>(\n null\n )\n const [hasFeedback, setHasFeedback] = useState(false)\n const [pending, setPending] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [modal, setModal] = useState<'dislike' | 'feedback' | null>(null)\n const [comment, setComment] = useState('')\n\n useEffect(() => {\n const key = readVisitorKey()\n setVisitorKey(key)\n const local = readLocalMark(slug)\n setSentiment(local.sentiment)\n setHasFeedback(local.hasFeedback)\n }, [slug])\n\n useEffect(() => {\n setCounts({ likeCount, dislikeCount, feedbackCount })\n }, [likeCount, dislikeCount, feedbackCount])\n\n const applyState = useCallback(\n (state: BlogArticleMarkState) => {\n setCounts({\n likeCount: state.likeCount,\n dislikeCount: state.dislikeCount,\n feedbackCount: state.feedbackCount,\n })\n setSentiment(state.sentiment)\n setHasFeedback(state.hasFeedback)\n writeLocalMark(slug, {\n sentiment: state.sentiment,\n hasFeedback: state.hasFeedback,\n })\n },\n [slug]\n )\n\n const run = useCallback(\n async (kind: 'reaction' | 'feedback', body: Record<string, unknown>) => {\n if (!visitorKey || pending) return\n setPending(true)\n setError(null)\n try {\n const state = await postMark(apiBasePath, slug, kind, {\n ...body,\n visitorKey,\n ...(locale ? { locale } : {}),\n })\n applyState(state)\n setModal(null)\n setComment('')\n } catch {\n setError(labels.error)\n } finally {\n setPending(false)\n }\n },\n [apiBasePath, applyState, labels.error, locale, pending, slug, visitorKey]\n )\n\n const onLike = useCallback(() => {\n if (sentiment === 'LIKE') {\n void run('reaction', { action: 'clear' })\n return\n }\n void run('reaction', { action: 'set', sentiment: 'LIKE' })\n }, [run, sentiment])\n\n const onDislikeClick = useCallback(() => {\n if (sentiment === 'DISLIKE') {\n void run('reaction', { action: 'clear' })\n return\n }\n if (hasFeedback) {\n void run('reaction', { action: 'set', sentiment: 'DISLIKE' })\n return\n }\n setModal('dislike')\n }, [hasFeedback, run, sentiment])\n\n const onSubmitModal = useCallback(() => {\n const trimmed = comment.trim()\n if (!trimmed) {\n setError(labels.dislikeRequiresComment)\n return\n }\n if (modal === 'dislike') {\n void run('reaction', {\n action: 'set',\n sentiment: 'DISLIKE',\n comment: trimmed,\n })\n return\n }\n void run('feedback', { action: 'set', comment: trimmed })\n }, [comment, labels.dislikeRequiresComment, modal, run])\n\n const rootClass = useMemo(\n () =>\n [\n 'mt-8 flex flex-col gap-3 border-t border-gray-200 pt-6 dark:border-gray-700',\n className,\n ]\n .filter(Boolean)\n .join(' '),\n [className]\n )\n\n return (\n <div className={rootClass}>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n className={`${BTN} ${sentiment === 'LIKE' ? BTN_ACTIVE : BTN_IDLE}`}\n disabled={pending || !visitorKey}\n onClick={onLike}\n aria-pressed={sentiment === 'LIKE'}\n >\n <span>{labels.like}</span>\n <span className=\"tabular-nums\">{counts.likeCount}</span>\n </button>\n <button\n type=\"button\"\n className={`${BTN} ${sentiment === 'DISLIKE' ? BTN_ACTIVE : BTN_IDLE}`}\n disabled={pending || !visitorKey}\n onClick={onDislikeClick}\n aria-pressed={sentiment === 'DISLIKE'}\n >\n <span>{labels.dislike}</span>\n <span className=\"tabular-nums\">{counts.dislikeCount}</span>\n </button>\n <button\n type=\"button\"\n className={`${BTN} ${hasFeedback ? BTN_ACTIVE : BTN_IDLE}`}\n disabled={pending || !visitorKey}\n onClick={() => {\n setError(null)\n setModal('feedback')\n }}\n aria-pressed={hasFeedback}\n >\n <span>{labels.feedback}</span>\n <span className=\"tabular-nums\">{counts.feedbackCount}</span>\n </button>\n </div>\n\n {error ? (\n <p className=\"text-sm text-red-600 dark:text-red-400\" role=\"alert\">\n {error}\n </p>\n ) : null}\n\n {modal ? (\n <div className=\"rounded-lg border border-gray-200 p-4 dark:border-gray-700\">\n <label className=\"mb-2 block text-sm font-medium text-gray-800 dark:text-gray-200\">\n {modal === 'dislike'\n ? labels.dislikeRequiresComment\n : labels.feedback}\n </label>\n <textarea\n className=\"mb-3 w-full rounded-md border border-gray-300 bg-white p-2 text-sm text-gray-900 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100\"\n rows={3}\n maxLength={1000}\n value={comment}\n placeholder={labels.feedbackPlaceholder}\n onChange={(e) => setComment(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n className={`${BTN} ${BTN_ACTIVE}`}\n disabled={pending}\n onClick={onSubmitModal}\n >\n {labels.submit}\n </button>\n <button\n type=\"button\"\n className={`${BTN} ${BTN_IDLE}`}\n disabled={pending}\n onClick={() => {\n setModal(null)\n setComment('')\n setError(null)\n }}\n >\n {labels.cancel}\n </button>\n </div>\n </div>\n ) : null}\n </div>\n )\n}\n"],"mappings":";;;AAEA,SAAS,aAAa,WAAW,SAAS,gBAAgB;AA6PlD,SAOE,KAPF;AAtPR,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAE7B,IAAM,MACJ;AACF,IAAM,WACJ;AACF,IAAM,aACJ;AAEF,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAEA,SAAS,iBAAyB;AAChC,MAAI;AACF,UAAM,WAAW,aAAa,QAAQ,WAAW;AACjD,QAAI,YAAY,SAAS,UAAU,GAAI,QAAO;AAC9C,UAAM,OAAO,iBAAiB;AAC9B,iBAAa,QAAQ,aAAa,IAAI;AACtC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,iBAAiB;AAAA,EAC1B;AACF;AAOA,SAAS,cAAc,MAAyB;AAC9C,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,GAAG,oBAAoB,GAAG,IAAI,EAAE;AACjE,QAAI,CAAC,IAAK,QAAO,EAAE,WAAW,MAAM,aAAa,MAAM;AACvD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO;AAAA,MACL,WACE,OAAO,cAAc,UAAU,OAAO,cAAc,YAChD,OAAO,YACP;AAAA,MACN,aAAa,QAAQ,OAAO,WAAW;AAAA,IACzC;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,WAAW,MAAM,aAAa,MAAM;AAAA,EAC/C;AACF;AAEA,SAAS,eAAe,MAAc,MAAuB;AAC3D,MAAI;AACF,iBAAa;AAAA,MACX,GAAG,oBAAoB,GAAG,IAAI;AAAA,MAC9B,KAAK,UAAU,IAAI;AAAA,IACrB;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAyBA,eAAe,SACb,aACA,MACA,MACA,MAC+B;AAC/B,QAAM,MAAM,MAAM;AAAA,IAChB,GAAG,WAAW,SAAS,mBAAmB,IAAI,CAAC,IAAI,IAAI;AAAA,IACvD;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B;AAAA,EACF;AACA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,MAAI,CAAC,IAAI,MAAM,CAAC,KAAK,MAAM;AACzB,UAAM,IAAI,MAAM,KAAK,WAAW,KAAK,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,EACpE;AACA,SAAO,KAAK;AACd;AAMO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAChE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAgC;AAAA,IAC1D;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,CAAC,WAAW,YAAY,IAAI;AAAA,IAChC;AAAA,EACF;AACA,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwC,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AAEzC,YAAU,MAAM;AACd,UAAM,MAAM,eAAe;AAC3B,kBAAc,GAAG;AACjB,UAAM,QAAQ,cAAc,IAAI;AAChC,iBAAa,MAAM,SAAS;AAC5B,mBAAe,MAAM,WAAW;AAAA,EAClC,GAAG,CAAC,IAAI,CAAC;AAET,YAAU,MAAM;AACd,cAAU,EAAE,WAAW,cAAc,cAAc,CAAC;AAAA,EACtD,GAAG,CAAC,WAAW,cAAc,aAAa,CAAC;AAE3C,QAAM,aAAa;AAAA,IACjB,CAAC,UAAgC;AAC/B,gBAAU;AAAA,QACR,WAAW,MAAM;AAAA,QACjB,cAAc,MAAM;AAAA,QACpB,eAAe,MAAM;AAAA,MACvB,CAAC;AACD,mBAAa,MAAM,SAAS;AAC5B,qBAAe,MAAM,WAAW;AAChC,qBAAe,MAAM;AAAA,QACnB,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,IACA,CAAC,IAAI;AAAA,EACP;AAEA,QAAM,MAAM;AAAA,IACV,OAAO,MAA+B,SAAkC;AACtE,UAAI,CAAC,cAAc,QAAS;AAC5B,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,cAAM,QAAQ,MAAM,SAAS,aAAa,MAAM,MAAM;AAAA,UACpD,GAAG;AAAA,UACH;AAAA,UACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,QAC7B,CAAC;AACD,mBAAW,KAAK;AAChB,iBAAS,IAAI;AACb,mBAAW,EAAE;AAAA,MACf,QAAQ;AACN,iBAAS,OAAO,KAAK;AAAA,MACvB,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,YAAY,OAAO,OAAO,QAAQ,SAAS,MAAM,UAAU;AAAA,EAC3E;AAEA,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,cAAc,QAAQ;AACxB,WAAK,IAAI,YAAY,EAAE,QAAQ,QAAQ,CAAC;AACxC;AAAA,IACF;AACA,SAAK,IAAI,YAAY,EAAE,QAAQ,OAAO,WAAW,OAAO,CAAC;AAAA,EAC3D,GAAG,CAAC,KAAK,SAAS,CAAC;AAEnB,QAAM,iBAAiB,YAAY,MAAM;AACvC,QAAI,cAAc,WAAW;AAC3B,WAAK,IAAI,YAAY,EAAE,QAAQ,QAAQ,CAAC;AACxC;AAAA,IACF;AACA,QAAI,aAAa;AACf,WAAK,IAAI,YAAY,EAAE,QAAQ,OAAO,WAAW,UAAU,CAAC;AAC5D;AAAA,IACF;AACA,aAAS,SAAS;AAAA,EACpB,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC;AAEhC,QAAM,gBAAgB,YAAY,MAAM;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,CAAC,SAAS;AACZ,eAAS,OAAO,sBAAsB;AACtC;AAAA,IACF;AACA,QAAI,UAAU,WAAW;AACvB,WAAK,IAAI,YAAY;AAAA,QACnB,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AACD;AAAA,IACF;AACA,SAAK,IAAI,YAAY,EAAE,QAAQ,OAAO,SAAS,QAAQ,CAAC;AAAA,EAC1D,GAAG,CAAC,SAAS,OAAO,wBAAwB,OAAO,GAAG,CAAC;AAEvD,QAAM,YAAY;AAAA,IAChB,MACE;AAAA,MACE;AAAA,MACA;AAAA,IACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,IACb,CAAC,SAAS;AAAA,EACZ;AAEA,SACE,qBAAC,SAAI,WAAW,WACd;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAW,GAAG,GAAG,IAAI,cAAc,SAAS,aAAa,QAAQ;AAAA,UACjE,UAAU,WAAW,CAAC;AAAA,UACtB,SAAS;AAAA,UACT,gBAAc,cAAc;AAAA,UAE5B;AAAA,gCAAC,UAAM,iBAAO,MAAK;AAAA,YACnB,oBAAC,UAAK,WAAU,gBAAgB,iBAAO,WAAU;AAAA;AAAA;AAAA,MACnD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAW,GAAG,GAAG,IAAI,cAAc,YAAY,aAAa,QAAQ;AAAA,UACpE,UAAU,WAAW,CAAC;AAAA,UACtB,SAAS;AAAA,UACT,gBAAc,cAAc;AAAA,UAE5B;AAAA,gCAAC,UAAM,iBAAO,SAAQ;AAAA,YACtB,oBAAC,UAAK,WAAU,gBAAgB,iBAAO,cAAa;AAAA;AAAA;AAAA,MACtD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAW,GAAG,GAAG,IAAI,cAAc,aAAa,QAAQ;AAAA,UACxD,UAAU,WAAW,CAAC;AAAA,UACtB,SAAS,MAAM;AACb,qBAAS,IAAI;AACb,qBAAS,UAAU;AAAA,UACrB;AAAA,UACA,gBAAc;AAAA,UAEd;AAAA,gCAAC,UAAM,iBAAO,UAAS;AAAA,YACvB,oBAAC,UAAK,WAAU,gBAAgB,iBAAO,eAAc;AAAA;AAAA;AAAA,MACvD;AAAA,OACF;AAAA,IAEC,QACC,oBAAC,OAAE,WAAU,0CAAyC,MAAK,SACxD,iBACH,IACE;AAAA,IAEH,QACC,qBAAC,SAAI,WAAU,8DACb;AAAA,0BAAC,WAAM,WAAU,mEACd,oBAAU,YACP,OAAO,yBACP,OAAO,UACb;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,MAAM;AAAA,UACN,WAAW;AAAA,UACX,OAAO;AAAA,UACP,aAAa,OAAO;AAAA,UACpB,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA;AAAA,MAC5C;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAW,GAAG,GAAG,IAAI,UAAU;AAAA,YAC/B,UAAU;AAAA,YACV,SAAS;AAAA,YAER,iBAAO;AAAA;AAAA,QACV;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAW,GAAG,GAAG,IAAI,QAAQ;AAAA,YAC7B,UAAU;AAAA,YACV,SAAS,MAAM;AACb,uBAAS,IAAI;AACb,yBAAW,EAAE;AACb,uBAAS,IAAI;AAAA,YACf;AAAA,YAEC,iBAAO;AAAA;AAAA,QACV;AAAA,SACF;AAAA,OACF,IACE;AAAA,KACN;AAEJ;","names":[]}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/components/blog-printable-buttons.tsx
22
+ var blog_printable_buttons_exports = {};
23
+ __export(blog_printable_buttons_exports, {
24
+ BlogPrintableButtons: () => BlogPrintableButtons
25
+ });
26
+ module.exports = __toCommonJS(blog_printable_buttons_exports);
27
+ var import_react = require("react");
28
+ var PRINT_BUTTON_CLASSNAME = [
29
+ "inline-flex items-center justify-center rounded-lg font-medium transition-colors",
30
+ "focus:outline-none focus:ring-2 focus:ring-offset-2",
31
+ "border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
32
+ "dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
33
+ "px-3 py-1.5 text-sm"
34
+ ].join(" ");
35
+ function escapeHtml(text) {
36
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
37
+ }
38
+ function printMailooSection(section) {
39
+ const body = section.querySelector(".mailoo-printable-body");
40
+ if (!body) return;
41
+ const printTitle = section.getAttribute("data-print-title")?.trim() || document.title;
42
+ const win = window.open("", "_blank", "noopener,noreferrer");
43
+ if (!win) return;
44
+ const html = `<!DOCTYPE html>
45
+ <html lang="en">
46
+ <head>
47
+ <meta charset="utf-8">
48
+ <title>${escapeHtml(printTitle)}</title>
49
+ <style>
50
+ body {
51
+ font-family: system-ui, -apple-system, sans-serif;
52
+ line-height: 1.6;
53
+ max-width: 48rem;
54
+ margin: 2rem auto;
55
+ padding: 0 1rem;
56
+ color: #111;
57
+ }
58
+ img { max-width: 100%; height: auto; }
59
+ a { color: #2563eb; }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ ${body.innerHTML}
64
+ </body>
65
+ </html>`;
66
+ win.document.open();
67
+ win.document.write(html);
68
+ win.document.close();
69
+ win.focus();
70
+ win.onload = () => {
71
+ win.print();
72
+ };
73
+ setTimeout(() => {
74
+ win.print();
75
+ }, 300);
76
+ }
77
+ function BlogPrintableButtons({
78
+ printLabel = "Print summary",
79
+ buttonClassName = PRINT_BUTTON_CLASSNAME
80
+ } = {}) {
81
+ (0, import_react.useEffect)(() => {
82
+ const sections = document.querySelectorAll(
83
+ "[data-mailoo-print]:not([data-mailoo-print-enhanced])"
84
+ );
85
+ for (const section of sections) {
86
+ section.setAttribute("data-mailoo-print-enhanced", "true");
87
+ const actions = document.createElement("div");
88
+ actions.className = "mailoo-printable-actions";
89
+ const button = document.createElement("button");
90
+ button.type = "button";
91
+ button.className = buttonClassName;
92
+ button.textContent = printLabel;
93
+ button.addEventListener("click", () => {
94
+ printMailooSection(section);
95
+ });
96
+ actions.appendChild(button);
97
+ section.insertBefore(actions, section.firstChild);
98
+ }
99
+ }, [printLabel, buttonClassName]);
100
+ return null;
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ BlogPrintableButtons
105
+ });
106
+ //# sourceMappingURL=printable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/blog-printable-buttons.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\nconst PRINT_BUTTON_CLASSNAME = [\n 'inline-flex items-center justify-center rounded-lg font-medium transition-colors',\n 'focus:outline-none focus:ring-2 focus:ring-offset-2',\n 'border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500',\n 'dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800',\n 'px-3 py-1.5 text-sm',\n].join(' ')\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction printMailooSection(section: HTMLElement): void {\n const body = section.querySelector('.mailoo-printable-body')\n if (!body) return\n\n const printTitle =\n section.getAttribute('data-print-title')?.trim() || document.title\n const win = window.open('', '_blank', 'noopener,noreferrer')\n if (!win) return\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>${escapeHtml(printTitle)}</title>\n<style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n max-width: 48rem;\n margin: 2rem auto;\n padding: 0 1rem;\n color: #111;\n }\n img { max-width: 100%; height: auto; }\n a { color: #2563eb; }\n</style>\n</head>\n<body>\n${body.innerHTML}\n</body>\n</html>`\n\n win.document.open()\n win.document.write(html)\n win.document.close()\n win.focus()\n win.onload = () => {\n win.print()\n }\n setTimeout(() => {\n win.print()\n }, 300)\n}\n\nexport interface BlogPrintableButtonsProps {\n /** Button label (host provides i18n) */\n printLabel?: string\n buttonClassName?: string\n}\n\n/**\n * Enhances `[data-mailoo-print]` sections from API-generated article HTML with\n * a print button.\n */\nexport function BlogPrintableButtons({\n printLabel = 'Print summary',\n buttonClassName = PRINT_BUTTON_CLASSNAME,\n}: BlogPrintableButtonsProps = {}) {\n useEffect(() => {\n const sections = document.querySelectorAll<HTMLElement>(\n '[data-mailoo-print]:not([data-mailoo-print-enhanced])'\n )\n\n for (const section of sections) {\n section.setAttribute('data-mailoo-print-enhanced', 'true')\n\n const actions = document.createElement('div')\n actions.className = 'mailoo-printable-actions'\n\n const button = document.createElement('button')\n button.type = 'button'\n button.className = buttonClassName\n button.textContent = printLabel\n button.addEventListener('click', () => {\n printMailooSection(section)\n })\n\n actions.appendChild(button)\n section.insertBefore(actions, section.firstChild)\n }\n }, [printLabel, buttonClassName])\n\n return null\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAA0B;AAE1B,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEV,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAS,mBAAmB,SAA4B;AACtD,QAAM,OAAO,QAAQ,cAAc,wBAAwB;AAC3D,MAAI,CAAC,KAAM;AAEX,QAAM,aACJ,QAAQ,aAAa,kBAAkB,GAAG,KAAK,KAAK,SAAS;AAC/D,QAAM,MAAM,OAAO,KAAK,IAAI,UAAU,qBAAqB;AAC3D,MAAI,CAAC,IAAK;AAEV,QAAM,OAAO;AAAA;AAAA;AAAA;AAAA,SAIN,WAAW,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe7B,KAAK,SAAS;AAAA;AAAA;AAId,MAAI,SAAS,KAAK;AAClB,MAAI,SAAS,MAAM,IAAI;AACvB,MAAI,SAAS,MAAM;AACnB,MAAI,MAAM;AACV,MAAI,SAAS,MAAM;AACjB,QAAI,MAAM;AAAA,EACZ;AACA,aAAW,MAAM;AACf,QAAI,MAAM;AAAA,EACZ,GAAG,GAAG;AACR;AAYO,SAAS,qBAAqB;AAAA,EACnC,aAAa;AAAA,EACb,kBAAkB;AACpB,IAA+B,CAAC,GAAG;AACjC,8BAAU,MAAM;AACd,UAAM,WAAW,SAAS;AAAA,MACxB;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,cAAQ,aAAa,8BAA8B,MAAM;AAEzD,YAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,cAAQ,YAAY;AAEpB,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,OAAO;AACd,aAAO,YAAY;AACnB,aAAO,cAAc;AACrB,aAAO,iBAAiB,SAAS,MAAM;AACrC,2BAAmB,OAAO;AAAA,MAC5B,CAAC;AAED,cAAQ,YAAY,MAAM;AAC1B,cAAQ,aAAa,SAAS,QAAQ,UAAU;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,YAAY,eAAe,CAAC;AAEhC,SAAO;AACT;","names":[]}
@@ -0,0 +1,12 @@
1
+ interface BlogPrintableButtonsProps {
2
+ /** Button label (host provides i18n) */
3
+ printLabel?: string;
4
+ buttonClassName?: string;
5
+ }
6
+ /**
7
+ * Enhances `[data-mailoo-print]` sections from API-generated article HTML with
8
+ * a print button.
9
+ */
10
+ declare function BlogPrintableButtons({ printLabel, buttonClassName, }?: BlogPrintableButtonsProps): null;
11
+
12
+ export { BlogPrintableButtons, type BlogPrintableButtonsProps };
@@ -0,0 +1,12 @@
1
+ interface BlogPrintableButtonsProps {
2
+ /** Button label (host provides i18n) */
3
+ printLabel?: string;
4
+ buttonClassName?: string;
5
+ }
6
+ /**
7
+ * Enhances `[data-mailoo-print]` sections from API-generated article HTML with
8
+ * a print button.
9
+ */
10
+ declare function BlogPrintableButtons({ printLabel, buttonClassName, }?: BlogPrintableButtonsProps): null;
11
+
12
+ export { BlogPrintableButtons, type BlogPrintableButtonsProps };
@@ -0,0 +1,82 @@
1
+ "use client";
2
+
3
+ // src/components/blog-printable-buttons.tsx
4
+ import { useEffect } from "react";
5
+ var PRINT_BUTTON_CLASSNAME = [
6
+ "inline-flex items-center justify-center rounded-lg font-medium transition-colors",
7
+ "focus:outline-none focus:ring-2 focus:ring-offset-2",
8
+ "border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
9
+ "dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
10
+ "px-3 py-1.5 text-sm"
11
+ ].join(" ");
12
+ function escapeHtml(text) {
13
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
14
+ }
15
+ function printMailooSection(section) {
16
+ const body = section.querySelector(".mailoo-printable-body");
17
+ if (!body) return;
18
+ const printTitle = section.getAttribute("data-print-title")?.trim() || document.title;
19
+ const win = window.open("", "_blank", "noopener,noreferrer");
20
+ if (!win) return;
21
+ const html = `<!DOCTYPE html>
22
+ <html lang="en">
23
+ <head>
24
+ <meta charset="utf-8">
25
+ <title>${escapeHtml(printTitle)}</title>
26
+ <style>
27
+ body {
28
+ font-family: system-ui, -apple-system, sans-serif;
29
+ line-height: 1.6;
30
+ max-width: 48rem;
31
+ margin: 2rem auto;
32
+ padding: 0 1rem;
33
+ color: #111;
34
+ }
35
+ img { max-width: 100%; height: auto; }
36
+ a { color: #2563eb; }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ ${body.innerHTML}
41
+ </body>
42
+ </html>`;
43
+ win.document.open();
44
+ win.document.write(html);
45
+ win.document.close();
46
+ win.focus();
47
+ win.onload = () => {
48
+ win.print();
49
+ };
50
+ setTimeout(() => {
51
+ win.print();
52
+ }, 300);
53
+ }
54
+ function BlogPrintableButtons({
55
+ printLabel = "Print summary",
56
+ buttonClassName = PRINT_BUTTON_CLASSNAME
57
+ } = {}) {
58
+ useEffect(() => {
59
+ const sections = document.querySelectorAll(
60
+ "[data-mailoo-print]:not([data-mailoo-print-enhanced])"
61
+ );
62
+ for (const section of sections) {
63
+ section.setAttribute("data-mailoo-print-enhanced", "true");
64
+ const actions = document.createElement("div");
65
+ actions.className = "mailoo-printable-actions";
66
+ const button = document.createElement("button");
67
+ button.type = "button";
68
+ button.className = buttonClassName;
69
+ button.textContent = printLabel;
70
+ button.addEventListener("click", () => {
71
+ printMailooSection(section);
72
+ });
73
+ actions.appendChild(button);
74
+ section.insertBefore(actions, section.firstChild);
75
+ }
76
+ }, [printLabel, buttonClassName]);
77
+ return null;
78
+ }
79
+ export {
80
+ BlogPrintableButtons
81
+ };
82
+ //# sourceMappingURL=printable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/blog-printable-buttons.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\nconst PRINT_BUTTON_CLASSNAME = [\n 'inline-flex items-center justify-center rounded-lg font-medium transition-colors',\n 'focus:outline-none focus:ring-2 focus:ring-offset-2',\n 'border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500',\n 'dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800',\n 'px-3 py-1.5 text-sm',\n].join(' ')\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction printMailooSection(section: HTMLElement): void {\n const body = section.querySelector('.mailoo-printable-body')\n if (!body) return\n\n const printTitle =\n section.getAttribute('data-print-title')?.trim() || document.title\n const win = window.open('', '_blank', 'noopener,noreferrer')\n if (!win) return\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>${escapeHtml(printTitle)}</title>\n<style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n max-width: 48rem;\n margin: 2rem auto;\n padding: 0 1rem;\n color: #111;\n }\n img { max-width: 100%; height: auto; }\n a { color: #2563eb; }\n</style>\n</head>\n<body>\n${body.innerHTML}\n</body>\n</html>`\n\n win.document.open()\n win.document.write(html)\n win.document.close()\n win.focus()\n win.onload = () => {\n win.print()\n }\n setTimeout(() => {\n win.print()\n }, 300)\n}\n\nexport interface BlogPrintableButtonsProps {\n /** Button label (host provides i18n) */\n printLabel?: string\n buttonClassName?: string\n}\n\n/**\n * Enhances `[data-mailoo-print]` sections from API-generated article HTML with\n * a print button.\n */\nexport function BlogPrintableButtons({\n printLabel = 'Print summary',\n buttonClassName = PRINT_BUTTON_CLASSNAME,\n}: BlogPrintableButtonsProps = {}) {\n useEffect(() => {\n const sections = document.querySelectorAll<HTMLElement>(\n '[data-mailoo-print]:not([data-mailoo-print-enhanced])'\n )\n\n for (const section of sections) {\n section.setAttribute('data-mailoo-print-enhanced', 'true')\n\n const actions = document.createElement('div')\n actions.className = 'mailoo-printable-actions'\n\n const button = document.createElement('button')\n button.type = 'button'\n button.className = buttonClassName\n button.textContent = printLabel\n button.addEventListener('click', () => {\n printMailooSection(section)\n })\n\n actions.appendChild(button)\n section.insertBefore(actions, section.firstChild)\n }\n }, [printLabel, buttonClassName])\n\n return null\n}\n"],"mappings":";;;AAEA,SAAS,iBAAiB;AAE1B,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEV,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAS,mBAAmB,SAA4B;AACtD,QAAM,OAAO,QAAQ,cAAc,wBAAwB;AAC3D,MAAI,CAAC,KAAM;AAEX,QAAM,aACJ,QAAQ,aAAa,kBAAkB,GAAG,KAAK,KAAK,SAAS;AAC/D,QAAM,MAAM,OAAO,KAAK,IAAI,UAAU,qBAAqB;AAC3D,MAAI,CAAC,IAAK;AAEV,QAAM,OAAO;AAAA;AAAA;AAAA;AAAA,SAIN,WAAW,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe7B,KAAK,SAAS;AAAA;AAAA;AAId,MAAI,SAAS,KAAK;AAClB,MAAI,SAAS,MAAM,IAAI;AACvB,MAAI,SAAS,MAAM;AACnB,MAAI,MAAM;AACV,MAAI,SAAS,MAAM;AACjB,QAAI,MAAM;AAAA,EACZ;AACA,aAAW,MAAM;AACf,QAAI,MAAM;AAAA,EACZ,GAAG,GAAG;AACR;AAYO,SAAS,qBAAqB;AAAA,EACnC,aAAa;AAAA,EACb,kBAAkB;AACpB,IAA+B,CAAC,GAAG;AACjC,YAAU,MAAM;AACd,UAAM,WAAW,SAAS;AAAA,MACxB;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,cAAQ,aAAa,8BAA8B,MAAM;AAEzD,YAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,cAAQ,YAAY;AAEpB,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,OAAO;AACd,aAAO,YAAY;AACnB,aAAO,cAAc;AACrB,aAAO,iBAAiB,SAAS,MAAM;AACrC,2BAAmB,OAAO;AAAA,MAC5B,CAAC;AAED,cAAQ,YAAY,MAAM;AAC1B,cAAQ,aAAa,SAAS,QAAQ,UAAU;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,YAAY,eAAe,CAAC;AAEhC,SAAO;AACT;","names":[]}
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/components/blog-article-share.tsx
22
+ var blog_article_share_exports = {};
23
+ __export(blog_article_share_exports, {
24
+ BlogArticleShare: () => BlogArticleShare,
25
+ buildFacebookSharerUrl: () => buildFacebookSharerUrl,
26
+ buildLinkedInShareUrl: () => buildLinkedInShareUrl,
27
+ buildXIntentTweetUrl: () => buildXIntentTweetUrl,
28
+ getSharePageUrlFromLocation: () => getSharePageUrlFromLocation,
29
+ requireAbsoluteHttpUrl: () => requireAbsoluteHttpUrl
30
+ });
31
+ module.exports = __toCommonJS(blog_article_share_exports);
32
+ var import_react = require("react");
33
+
34
+ // src/lib/share-urls.ts
35
+ function requireAbsoluteHttpUrl(pageUrl) {
36
+ const trimmed = pageUrl.trim();
37
+ if (!trimmed) {
38
+ throw new Error("Share URL must be a non-empty absolute http(s) URL");
39
+ }
40
+ let parsed;
41
+ try {
42
+ parsed = new URL(trimmed);
43
+ } catch {
44
+ throw new Error(`Share URL is not a valid absolute URL: ${pageUrl}`);
45
+ }
46
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
47
+ throw new Error(`Share URL must use http or https: ${pageUrl}`);
48
+ }
49
+ return trimmed;
50
+ }
51
+ function buildFacebookSharerUrl(pageUrl, quote) {
52
+ const params = new URLSearchParams({
53
+ u: requireAbsoluteHttpUrl(pageUrl)
54
+ });
55
+ const trimmedQuote = quote?.trim();
56
+ if (trimmedQuote) {
57
+ params.set("quote", trimmedQuote);
58
+ }
59
+ return `https://www.facebook.com/sharer/sharer.php?${params.toString()}`;
60
+ }
61
+ function buildXIntentTweetUrl(pageUrl, text) {
62
+ const params = new URLSearchParams({
63
+ text,
64
+ url: requireAbsoluteHttpUrl(pageUrl)
65
+ });
66
+ return `https://twitter.com/intent/tweet?${params.toString()}`;
67
+ }
68
+ function buildLinkedInShareUrl(pageUrl) {
69
+ const params = new URLSearchParams({
70
+ url: requireAbsoluteHttpUrl(pageUrl)
71
+ });
72
+ return `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`;
73
+ }
74
+ function getSharePageUrlFromLocation(location) {
75
+ return `${location.origin}${location.pathname}`;
76
+ }
77
+
78
+ // src/components/blog-article-share.tsx
79
+ var import_jsx_runtime = require("react/jsx-runtime");
80
+ var DEFAULT_BUTTON_CLASSNAME = [
81
+ "inline-flex items-center justify-center rounded-lg font-medium transition-colors",
82
+ "focus:outline-none focus:ring-2 focus:ring-offset-2",
83
+ "border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
84
+ "dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
85
+ "px-3 py-1.5 text-sm"
86
+ ].join(" ");
87
+ var DEFAULT_HEADING_CLASSNAME = "mb-4 text-lg font-semibold text-gray-900 dark:text-white";
88
+ var DEFAULT_SECTION_CLASSNAME = "mt-12 border-t border-gray-200 pt-8 dark:border-gray-700";
89
+ function resolveShareUrl(url) {
90
+ if (url !== void 0) {
91
+ return requireAbsoluteHttpUrl(url);
92
+ }
93
+ if (typeof window === "undefined") {
94
+ throw new Error("Share URL requires a browser location when url is omitted");
95
+ }
96
+ return getSharePageUrlFromLocation(window.location);
97
+ }
98
+ function openShareWindow(href) {
99
+ window.open(href, "_blank", "noopener,noreferrer");
100
+ }
101
+ function BlogArticleShare({
102
+ title: _title,
103
+ text,
104
+ labels,
105
+ url,
106
+ className = DEFAULT_SECTION_CLASSNAME,
107
+ headingClassName = DEFAULT_HEADING_CLASSNAME,
108
+ buttonClassName = DEFAULT_BUTTON_CLASSNAME
109
+ }) {
110
+ const [linkCopied, setLinkCopied] = (0, import_react.useState)(false);
111
+ const shareUrl = (0, import_react.useCallback)(() => resolveShareUrl(url), [url]);
112
+ const onCopy = (0, import_react.useCallback)(async () => {
113
+ try {
114
+ await navigator.clipboard.writeText(shareUrl());
115
+ setLinkCopied(true);
116
+ setTimeout(() => setLinkCopied(false), 2e3);
117
+ } catch {
118
+ }
119
+ }, [shareUrl]);
120
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: headingClassName, children: labels.heading }),
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
124
+ "button",
125
+ {
126
+ type: "button",
127
+ className: buttonClassName,
128
+ onClick: () => {
129
+ openShareWindow(buildXIntentTweetUrl(shareUrl(), text));
130
+ },
131
+ children: [
132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
133
+ "svg",
134
+ {
135
+ className: "mr-2 h-4 w-4",
136
+ fill: "currentColor",
137
+ viewBox: "0 0 24 24",
138
+ "aria-hidden": "true",
139
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" })
140
+ }
141
+ ),
142
+ labels.x
143
+ ]
144
+ }
145
+ ),
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
147
+ "button",
148
+ {
149
+ type: "button",
150
+ className: buttonClassName,
151
+ onClick: () => {
152
+ openShareWindow(buildFacebookSharerUrl(shareUrl()));
153
+ },
154
+ children: [
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ "svg",
157
+ {
158
+ className: "mr-2 h-4 w-4",
159
+ fill: "currentColor",
160
+ viewBox: "0 0 24 24",
161
+ "aria-hidden": "true",
162
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M24 12.073C24 5.405 18.627 0 12 0S0 5.405 0 12.073C0 18.1 4.388 23.094 10.125 24v-8.435H7.078v-3.492h3.047V9.414c0-3.025 1.792-4.697 4.533-4.697 1.312 0 2.686.236 2.686.236v2.974h-1.513c-1.49 0-1.956.931-1.956 1.887v2.259h3.328l-.532 3.492h-2.796V24C19.612 23.094 24 18.1 24 12.073z" })
163
+ }
164
+ ),
165
+ labels.facebook
166
+ ]
167
+ }
168
+ ),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
170
+ "button",
171
+ {
172
+ type: "button",
173
+ className: buttonClassName,
174
+ onClick: () => {
175
+ openShareWindow(buildLinkedInShareUrl(shareUrl()));
176
+ },
177
+ children: [
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
179
+ "svg",
180
+ {
181
+ className: "mr-2 h-4 w-4",
182
+ fill: "currentColor",
183
+ viewBox: "0 0 24 24",
184
+ "aria-hidden": "true",
185
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" })
186
+ }
187
+ ),
188
+ labels.linkedin
189
+ ]
190
+ }
191
+ ),
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
193
+ "button",
194
+ {
195
+ type: "button",
196
+ className: buttonClassName,
197
+ onClick: () => void onCopy(),
198
+ children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
200
+ "svg",
201
+ {
202
+ className: "mr-2 h-4 w-4",
203
+ fill: "none",
204
+ stroke: "currentColor",
205
+ viewBox: "0 0 24 24",
206
+ "aria-hidden": "true",
207
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
+ "path",
209
+ {
210
+ strokeLinecap: "round",
211
+ strokeLinejoin: "round",
212
+ strokeWidth: 2,
213
+ d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
214
+ }
215
+ )
216
+ }
217
+ ),
218
+ linkCopied ? labels.linkCopied : labels.copyLink
219
+ ]
220
+ }
221
+ )
222
+ ] })
223
+ ] });
224
+ }
225
+ // Annotate the CommonJS export names for ESM import in node:
226
+ 0 && (module.exports = {
227
+ BlogArticleShare,
228
+ buildFacebookSharerUrl,
229
+ buildLinkedInShareUrl,
230
+ buildXIntentTweetUrl,
231
+ getSharePageUrlFromLocation,
232
+ requireAbsoluteHttpUrl
233
+ });
234
+ //# sourceMappingURL=share.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/blog-article-share.tsx","../../src/lib/share-urls.ts"],"sourcesContent":["'use client'\n\nimport { useCallback, useState } from 'react'\nimport {\n buildFacebookSharerUrl,\n buildLinkedInShareUrl,\n buildXIntentTweetUrl,\n getSharePageUrlFromLocation,\n requireAbsoluteHttpUrl,\n} from '../lib/share-urls.js'\n\nexport {\n buildFacebookSharerUrl,\n buildLinkedInShareUrl,\n buildXIntentTweetUrl,\n getSharePageUrlFromLocation,\n requireAbsoluteHttpUrl,\n} from '../lib/share-urls.js'\n\nconst DEFAULT_BUTTON_CLASSNAME = [\n 'inline-flex items-center justify-center rounded-lg font-medium transition-colors',\n 'focus:outline-none focus:ring-2 focus:ring-offset-2',\n 'border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500',\n 'dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800',\n 'px-3 py-1.5 text-sm',\n].join(' ')\n\nconst DEFAULT_HEADING_CLASSNAME =\n 'mb-4 text-lg font-semibold text-gray-900 dark:text-white'\n\nconst DEFAULT_SECTION_CLASSNAME =\n 'mt-12 border-t border-gray-200 pt-8 dark:border-gray-700'\n\nexport type BlogArticleShareLabels = {\n heading: string\n x: string\n facebook: string\n linkedin: string\n copyLink: string\n linkCopied: string\n}\n\nexport type BlogArticleShareProps = {\n /** Article title (host API parity with mindful-journal BlogPostShareButtons). */\n title: string\n /** Already-interpolated share text for X (e.g. \"Check out this article: …\"). */\n text: string\n labels: BlogArticleShareLabels\n /**\n * Absolute http(s) share URL. When omitted, each click uses\n * `window.location.origin + pathname` (no query/hash).\n */\n url?: string\n className?: string\n headingClassName?: string\n buttonClassName?: string\n}\n\nfunction resolveShareUrl(url: string | undefined): string {\n if (url !== undefined) {\n return requireAbsoluteHttpUrl(url)\n }\n if (typeof window === 'undefined') {\n throw new Error('Share URL requires a browser location when url is omitted')\n }\n return getSharePageUrlFromLocation(window.location)\n}\n\nfunction openShareWindow(href: string): void {\n window.open(href, '_blank', 'noopener,noreferrer')\n}\n\n/**\n * X / Facebook / LinkedIn / copy-link controls for a blog article.\n * Host provides i18n labels and share text; optional absolute `url`.\n */\nexport function BlogArticleShare({\n title: _title,\n text,\n labels,\n url,\n className = DEFAULT_SECTION_CLASSNAME,\n headingClassName = DEFAULT_HEADING_CLASSNAME,\n buttonClassName = DEFAULT_BUTTON_CLASSNAME,\n}: BlogArticleShareProps) {\n const [linkCopied, setLinkCopied] = useState(false)\n\n const shareUrl = useCallback(() => resolveShareUrl(url), [url])\n\n const onCopy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(shareUrl())\n setLinkCopied(true)\n setTimeout(() => setLinkCopied(false), 2000)\n } catch {\n // Fail-fast UI: do not claim success or invent a second copy path.\n }\n }, [shareUrl])\n\n return (\n <div className={className}>\n <h3 className={headingClassName}>{labels.heading}</h3>\n <div className=\"flex flex-wrap gap-4\">\n <button\n type=\"button\"\n className={buttonClassName}\n onClick={() => {\n openShareWindow(buildXIntentTweetUrl(shareUrl(), text))\n }}\n >\n <svg\n className=\"mr-2 h-4 w-4\"\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path d=\"M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84\" />\n </svg>\n {labels.x}\n </button>\n <button\n type=\"button\"\n className={buttonClassName}\n onClick={() => {\n openShareWindow(buildFacebookSharerUrl(shareUrl()))\n }}\n >\n <svg\n className=\"mr-2 h-4 w-4\"\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path d=\"M24 12.073C24 5.405 18.627 0 12 0S0 5.405 0 12.073C0 18.1 4.388 23.094 10.125 24v-8.435H7.078v-3.492h3.047V9.414c0-3.025 1.792-4.697 4.533-4.697 1.312 0 2.686.236 2.686.236v2.974h-1.513c-1.49 0-1.956.931-1.956 1.887v2.259h3.328l-.532 3.492h-2.796V24C19.612 23.094 24 18.1 24 12.073z\" />\n </svg>\n {labels.facebook}\n </button>\n <button\n type=\"button\"\n className={buttonClassName}\n onClick={() => {\n openShareWindow(buildLinkedInShareUrl(shareUrl()))\n }}\n >\n <svg\n className=\"mr-2 h-4 w-4\"\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\" />\n </svg>\n {labels.linkedin}\n </button>\n <button\n type=\"button\"\n className={buttonClassName}\n onClick={() => void onCopy()}\n >\n <svg\n className=\"mr-2 h-4 w-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n {linkCopied ? labels.linkCopied : labels.copyLink}\n </button>\n </div>\n </div>\n )\n}\n","/** Lightweight social share URL builders (no host UI deps). */\n\nexport function requireAbsoluteHttpUrl(pageUrl: string): string {\n const trimmed = pageUrl.trim()\n if (!trimmed) {\n throw new Error('Share URL must be a non-empty absolute http(s) URL')\n }\n let parsed: URL\n try {\n parsed = new URL(trimmed)\n } catch {\n throw new Error(`Share URL is not a valid absolute URL: ${pageUrl}`)\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(`Share URL must use http or https: ${pageUrl}`)\n }\n return trimmed\n}\n\nexport function buildFacebookSharerUrl(\n pageUrl: string,\n quote?: string\n): string {\n const params = new URLSearchParams({\n u: requireAbsoluteHttpUrl(pageUrl),\n })\n const trimmedQuote = quote?.trim()\n if (trimmedQuote) {\n params.set('quote', trimmedQuote)\n }\n return `https://www.facebook.com/sharer/sharer.php?${params.toString()}`\n}\n\n/** Blog article share (matches mindful-journal BlogPostShareButtons). */\nexport function buildXIntentTweetUrl(pageUrl: string, text: string): string {\n const params = new URLSearchParams({\n text,\n url: requireAbsoluteHttpUrl(pageUrl),\n })\n return `https://twitter.com/intent/tweet?${params.toString()}`\n}\n\nexport function buildLinkedInShareUrl(pageUrl: string): string {\n const params = new URLSearchParams({\n url: requireAbsoluteHttpUrl(pageUrl),\n })\n return `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`\n}\n\n/** Canonical page URL without query/hash — safer for social scrapers. */\nexport function getSharePageUrlFromLocation(\n location: Pick<Location, 'origin' | 'pathname'>\n): string {\n return `${location.origin}${location.pathname}`\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,41 @@
1
+ import * as react from 'react';
2
+
3
+ /** Lightweight social share URL builders (no host UI deps). */
4
+ declare function requireAbsoluteHttpUrl(pageUrl: string): string;
5
+ declare function buildFacebookSharerUrl(pageUrl: string, quote?: string): string;
6
+ /** Blog article share (matches mindful-journal BlogPostShareButtons). */
7
+ declare function buildXIntentTweetUrl(pageUrl: string, text: string): string;
8
+ declare function buildLinkedInShareUrl(pageUrl: string): string;
9
+ /** Canonical page URL without query/hash — safer for social scrapers. */
10
+ declare function getSharePageUrlFromLocation(location: Pick<Location, 'origin' | 'pathname'>): string;
11
+
12
+ type BlogArticleShareLabels = {
13
+ heading: string;
14
+ x: string;
15
+ facebook: string;
16
+ linkedin: string;
17
+ copyLink: string;
18
+ linkCopied: string;
19
+ };
20
+ type BlogArticleShareProps = {
21
+ /** Article title (host API parity with mindful-journal BlogPostShareButtons). */
22
+ title: string;
23
+ /** Already-interpolated share text for X (e.g. "Check out this article: …"). */
24
+ text: string;
25
+ labels: BlogArticleShareLabels;
26
+ /**
27
+ * Absolute http(s) share URL. When omitted, each click uses
28
+ * `window.location.origin + pathname` (no query/hash).
29
+ */
30
+ url?: string;
31
+ className?: string;
32
+ headingClassName?: string;
33
+ buttonClassName?: string;
34
+ };
35
+ /**
36
+ * X / Facebook / LinkedIn / copy-link controls for a blog article.
37
+ * Host provides i18n labels and share text; optional absolute `url`.
38
+ */
39
+ declare function BlogArticleShare({ title: _title, text, labels, url, className, headingClassName, buttonClassName, }: BlogArticleShareProps): react.JSX.Element;
40
+
41
+ export { BlogArticleShare, type BlogArticleShareLabels, type BlogArticleShareProps, buildFacebookSharerUrl, buildLinkedInShareUrl, buildXIntentTweetUrl, getSharePageUrlFromLocation, requireAbsoluteHttpUrl };
@@ -0,0 +1,41 @@
1
+ import * as react from 'react';
2
+
3
+ /** Lightweight social share URL builders (no host UI deps). */
4
+ declare function requireAbsoluteHttpUrl(pageUrl: string): string;
5
+ declare function buildFacebookSharerUrl(pageUrl: string, quote?: string): string;
6
+ /** Blog article share (matches mindful-journal BlogPostShareButtons). */
7
+ declare function buildXIntentTweetUrl(pageUrl: string, text: string): string;
8
+ declare function buildLinkedInShareUrl(pageUrl: string): string;
9
+ /** Canonical page URL without query/hash — safer for social scrapers. */
10
+ declare function getSharePageUrlFromLocation(location: Pick<Location, 'origin' | 'pathname'>): string;
11
+
12
+ type BlogArticleShareLabels = {
13
+ heading: string;
14
+ x: string;
15
+ facebook: string;
16
+ linkedin: string;
17
+ copyLink: string;
18
+ linkCopied: string;
19
+ };
20
+ type BlogArticleShareProps = {
21
+ /** Article title (host API parity with mindful-journal BlogPostShareButtons). */
22
+ title: string;
23
+ /** Already-interpolated share text for X (e.g. "Check out this article: …"). */
24
+ text: string;
25
+ labels: BlogArticleShareLabels;
26
+ /**
27
+ * Absolute http(s) share URL. When omitted, each click uses
28
+ * `window.location.origin + pathname` (no query/hash).
29
+ */
30
+ url?: string;
31
+ className?: string;
32
+ headingClassName?: string;
33
+ buttonClassName?: string;
34
+ };
35
+ /**
36
+ * X / Facebook / LinkedIn / copy-link controls for a blog article.
37
+ * Host provides i18n labels and share text; optional absolute `url`.
38
+ */
39
+ declare function BlogArticleShare({ title: _title, text, labels, url, className, headingClassName, buttonClassName, }: BlogArticleShareProps): react.JSX.Element;
40
+
41
+ export { BlogArticleShare, type BlogArticleShareLabels, type BlogArticleShareProps, buildFacebookSharerUrl, buildLinkedInShareUrl, buildXIntentTweetUrl, getSharePageUrlFromLocation, requireAbsoluteHttpUrl };