@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.
- package/LICENSE +16 -0
- package/README.md +142 -2
- package/dist/client/article-body.cjs +115 -0
- package/dist/client/article-body.cjs.map +1 -0
- package/dist/client/article-body.d.cts +15 -0
- package/dist/client/article-body.d.ts +15 -0
- package/dist/client/article-body.js +80 -0
- package/dist/client/article-body.js.map +1 -0
- package/dist/client/index.cjs +776 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +6 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +731 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/linked-links.cjs +125 -0
- package/dist/client/linked-links.cjs.map +1 -0
- package/dist/client/linked-links.d.cts +31 -0
- package/dist/client/linked-links.d.ts +31 -0
- package/dist/client/linked-links.js +100 -0
- package/dist/client/linked-links.js.map +1 -0
- package/dist/client/marks.cjs +300 -0
- package/dist/client/marks.cjs.map +1 -0
- package/dist/client/marks.d.cts +30 -0
- package/dist/client/marks.d.ts +30 -0
- package/dist/client/marks.js +276 -0
- package/dist/client/marks.js.map +1 -0
- package/dist/client/printable.cjs +106 -0
- package/dist/client/printable.cjs.map +1 -0
- package/dist/client/printable.d.cts +12 -0
- package/dist/client/printable.d.ts +12 -0
- package/dist/client/printable.js +82 -0
- package/dist/client/printable.js.map +1 -0
- package/dist/client/share.cjs +234 -0
- package/dist/client/share.cjs.map +1 -0
- package/dist/client/share.d.cts +41 -0
- package/dist/client/share.d.ts +41 -0
- package/dist/client/share.js +205 -0
- package/dist/client/share.js.map +1 -0
- package/dist/index.cjs +89 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/index.cjs +266 -0
- package/dist/routes/index.cjs.map +1 -0
- package/dist/routes/index.d.cts +87 -0
- package/dist/routes/index.d.ts +87 -0
- package/dist/routes/index.js +240 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/server/index.cjs +368 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +132 -0
- package/dist/server/index.d.ts +132 -0
- package/dist/server/index.js +339 -0
- package/dist/server/index.js.map +1 -0
- package/dist/utils-DXbN9MTi.d.cts +120 -0
- package/dist/utils-DXbN9MTi.d.ts +120 -0
- package/package.json +110 -3
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/client/index.ts
|
|
32
|
+
var client_exports = {};
|
|
33
|
+
__export(client_exports, {
|
|
34
|
+
BlogArticleBody: () => BlogArticleBody,
|
|
35
|
+
BlogArticleMarks: () => BlogArticleMarks,
|
|
36
|
+
BlogArticleShare: () => BlogArticleShare,
|
|
37
|
+
BlogLinkedLinksDisplay: () => BlogLinkedLinksDisplay,
|
|
38
|
+
BlogPrintableButtons: () => BlogPrintableButtons,
|
|
39
|
+
buildFacebookSharerUrl: () => buildFacebookSharerUrl,
|
|
40
|
+
buildLinkedInShareUrl: () => buildLinkedInShareUrl,
|
|
41
|
+
buildXIntentTweetUrl: () => buildXIntentTweetUrl,
|
|
42
|
+
getSharePageUrlFromLocation: () => getSharePageUrlFromLocation,
|
|
43
|
+
requireAbsoluteHttpUrl: () => requireAbsoluteHttpUrl
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(client_exports);
|
|
46
|
+
|
|
47
|
+
// src/components/blog-article-body.tsx
|
|
48
|
+
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
49
|
+
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
50
|
+
|
|
51
|
+
// src/markdown-link-target.ts
|
|
52
|
+
function markdownHrefOpensInNewTab(href) {
|
|
53
|
+
const h = href.trim();
|
|
54
|
+
if (h === "") return false;
|
|
55
|
+
if (h.startsWith("#") || h.startsWith("?")) return false;
|
|
56
|
+
if (h.startsWith("//")) return true;
|
|
57
|
+
if (h.startsWith("/")) return false;
|
|
58
|
+
if (h.startsWith("./") || h.startsWith("../")) return false;
|
|
59
|
+
const lower = h.toLowerCase();
|
|
60
|
+
return lower.startsWith("http://") || lower.startsWith("https://");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/server/utils.ts
|
|
64
|
+
var BLOG_ARTICLE_PROSE_CLASSNAME = [
|
|
65
|
+
"prose",
|
|
66
|
+
"prose-gray",
|
|
67
|
+
"prose-lg",
|
|
68
|
+
"max-w-none",
|
|
69
|
+
"dark:prose-invert",
|
|
70
|
+
"prose-a:text-blue-600",
|
|
71
|
+
"hover:prose-a:text-blue-700",
|
|
72
|
+
"dark:prose-a:text-blue-400",
|
|
73
|
+
"dark:hover:prose-a:text-blue-300",
|
|
74
|
+
"prose-blockquote:border-blue-500",
|
|
75
|
+
"prose-img:rounded-lg",
|
|
76
|
+
"prose-img:shadow-sm"
|
|
77
|
+
].join(" ");
|
|
78
|
+
|
|
79
|
+
// src/components/blog-article-body.tsx
|
|
80
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
81
|
+
function BlogArticleBody({
|
|
82
|
+
htmlContent,
|
|
83
|
+
markdownFallback,
|
|
84
|
+
className = BLOG_ARTICLE_PROSE_CLASSNAME
|
|
85
|
+
}) {
|
|
86
|
+
const html = htmlContent?.trim();
|
|
87
|
+
if (html) {
|
|
88
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
89
|
+
"div",
|
|
90
|
+
{
|
|
91
|
+
className,
|
|
92
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const md = markdownFallback?.trim();
|
|
97
|
+
if (md) {
|
|
98
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
99
|
+
import_react_markdown.default,
|
|
100
|
+
{
|
|
101
|
+
remarkPlugins: [import_remark_gfm.default],
|
|
102
|
+
components: {
|
|
103
|
+
a({ children, href, ...props }) {
|
|
104
|
+
const h = href ?? "";
|
|
105
|
+
const openNew = markdownHrefOpensInNewTab(h);
|
|
106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
107
|
+
"a",
|
|
108
|
+
{
|
|
109
|
+
href: h,
|
|
110
|
+
...openNew ? { target: "_blank", rel: "noopener noreferrer" } : {},
|
|
111
|
+
...props,
|
|
112
|
+
children
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
children: md
|
|
118
|
+
}
|
|
119
|
+
) });
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/components/blog-linked-links-display.tsx
|
|
125
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
126
|
+
var TYPE_ORDER = [
|
|
127
|
+
"internal_article",
|
|
128
|
+
"update_announcement",
|
|
129
|
+
"external_resource"
|
|
130
|
+
];
|
|
131
|
+
function groupByType(links) {
|
|
132
|
+
const sorted = [...links].sort((a, b) => a.sortOrder - b.sortOrder);
|
|
133
|
+
const map = /* @__PURE__ */ new Map();
|
|
134
|
+
for (const t of TYPE_ORDER) map.set(t, []);
|
|
135
|
+
for (const link of sorted) {
|
|
136
|
+
const bucket = map.get(link.type);
|
|
137
|
+
if (bucket) bucket.push(link);
|
|
138
|
+
}
|
|
139
|
+
return map;
|
|
140
|
+
}
|
|
141
|
+
function hrefForLink(url, blogLocale) {
|
|
142
|
+
if (blogLocale && url.startsWith("/blog/")) {
|
|
143
|
+
return `/${blogLocale}${url}`;
|
|
144
|
+
}
|
|
145
|
+
return url;
|
|
146
|
+
}
|
|
147
|
+
function BlogLinkedLinksDisplay({
|
|
148
|
+
links,
|
|
149
|
+
title,
|
|
150
|
+
typeLabels,
|
|
151
|
+
variant = "article",
|
|
152
|
+
blogLocale,
|
|
153
|
+
LinkComponent
|
|
154
|
+
}) {
|
|
155
|
+
if (!links.length) return null;
|
|
156
|
+
const grouped = groupByType(links);
|
|
157
|
+
const sectionClass = variant === "article" ? "mt-10 border-t border-gray-200 pt-8 dark:border-gray-700" : "mt-4 rounded-lg border border-dashed border-gray-300 p-4 dark:border-gray-600";
|
|
158
|
+
const Link = LinkComponent ?? (({ href, className, children }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href, className, children }));
|
|
159
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
160
|
+
"section",
|
|
161
|
+
{
|
|
162
|
+
className: sectionClass,
|
|
163
|
+
"aria-labelledby": "mailoo-linked-links-heading",
|
|
164
|
+
children: [
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
166
|
+
"h2",
|
|
167
|
+
{
|
|
168
|
+
id: "mailoo-linked-links-heading",
|
|
169
|
+
className: variant === "article" ? "mb-6 text-xl font-semibold text-gray-900 dark:text-white" : "mb-3 text-sm font-semibold text-gray-800 dark:text-gray-200",
|
|
170
|
+
children: title
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-8", children: TYPE_ORDER.map((type) => {
|
|
174
|
+
const items = grouped.get(type) ?? [];
|
|
175
|
+
if (!items.length) return null;
|
|
176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
177
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
178
|
+
"h3",
|
|
179
|
+
{
|
|
180
|
+
className: variant === "article" ? "mb-3 text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400" : "mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400",
|
|
181
|
+
children: typeLabels[type]
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "space-y-4", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
185
|
+
"li",
|
|
186
|
+
{
|
|
187
|
+
children: [
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
189
|
+
Link,
|
|
190
|
+
{
|
|
191
|
+
href: hrefForLink(item.url, blogLocale),
|
|
192
|
+
className: variant === "article" ? "text-lg font-medium text-blue-600 hover:underline dark:text-blue-400" : "text-sm font-medium text-blue-600 hover:underline dark:text-blue-400",
|
|
193
|
+
children: item.label
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
item.date && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
197
|
+
"p",
|
|
198
|
+
{
|
|
199
|
+
className: variant === "article" ? "mt-1 text-sm text-gray-500 dark:text-gray-400" : "mt-0.5 text-xs text-gray-500 dark:text-gray-400",
|
|
200
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("time", { dateTime: item.date, children: new Date(item.date).toLocaleDateString() })
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
item.intro && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
204
|
+
"p",
|
|
205
|
+
{
|
|
206
|
+
className: variant === "article" ? "mt-2 text-sm text-gray-600 dark:text-gray-300" : "mt-1 text-xs text-gray-600 dark:text-gray-300",
|
|
207
|
+
children: item.intro
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
item.id ?? `${item.type}-${item.url}-${item.sortOrder}`
|
|
213
|
+
)) })
|
|
214
|
+
] }, type);
|
|
215
|
+
}) })
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/components/blog-printable-buttons.tsx
|
|
222
|
+
var import_react = require("react");
|
|
223
|
+
var PRINT_BUTTON_CLASSNAME = [
|
|
224
|
+
"inline-flex items-center justify-center rounded-lg font-medium transition-colors",
|
|
225
|
+
"focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
226
|
+
"border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
|
|
227
|
+
"dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
|
|
228
|
+
"px-3 py-1.5 text-sm"
|
|
229
|
+
].join(" ");
|
|
230
|
+
function escapeHtml(text) {
|
|
231
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
232
|
+
}
|
|
233
|
+
function printMailooSection(section) {
|
|
234
|
+
const body = section.querySelector(".mailoo-printable-body");
|
|
235
|
+
if (!body) return;
|
|
236
|
+
const printTitle = section.getAttribute("data-print-title")?.trim() || document.title;
|
|
237
|
+
const win = window.open("", "_blank", "noopener,noreferrer");
|
|
238
|
+
if (!win) return;
|
|
239
|
+
const html = `<!DOCTYPE html>
|
|
240
|
+
<html lang="en">
|
|
241
|
+
<head>
|
|
242
|
+
<meta charset="utf-8">
|
|
243
|
+
<title>${escapeHtml(printTitle)}</title>
|
|
244
|
+
<style>
|
|
245
|
+
body {
|
|
246
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
247
|
+
line-height: 1.6;
|
|
248
|
+
max-width: 48rem;
|
|
249
|
+
margin: 2rem auto;
|
|
250
|
+
padding: 0 1rem;
|
|
251
|
+
color: #111;
|
|
252
|
+
}
|
|
253
|
+
img { max-width: 100%; height: auto; }
|
|
254
|
+
a { color: #2563eb; }
|
|
255
|
+
</style>
|
|
256
|
+
</head>
|
|
257
|
+
<body>
|
|
258
|
+
${body.innerHTML}
|
|
259
|
+
</body>
|
|
260
|
+
</html>`;
|
|
261
|
+
win.document.open();
|
|
262
|
+
win.document.write(html);
|
|
263
|
+
win.document.close();
|
|
264
|
+
win.focus();
|
|
265
|
+
win.onload = () => {
|
|
266
|
+
win.print();
|
|
267
|
+
};
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
win.print();
|
|
270
|
+
}, 300);
|
|
271
|
+
}
|
|
272
|
+
function BlogPrintableButtons({
|
|
273
|
+
printLabel = "Print summary",
|
|
274
|
+
buttonClassName = PRINT_BUTTON_CLASSNAME
|
|
275
|
+
} = {}) {
|
|
276
|
+
(0, import_react.useEffect)(() => {
|
|
277
|
+
const sections = document.querySelectorAll(
|
|
278
|
+
"[data-mailoo-print]:not([data-mailoo-print-enhanced])"
|
|
279
|
+
);
|
|
280
|
+
for (const section of sections) {
|
|
281
|
+
section.setAttribute("data-mailoo-print-enhanced", "true");
|
|
282
|
+
const actions = document.createElement("div");
|
|
283
|
+
actions.className = "mailoo-printable-actions";
|
|
284
|
+
const button = document.createElement("button");
|
|
285
|
+
button.type = "button";
|
|
286
|
+
button.className = buttonClassName;
|
|
287
|
+
button.textContent = printLabel;
|
|
288
|
+
button.addEventListener("click", () => {
|
|
289
|
+
printMailooSection(section);
|
|
290
|
+
});
|
|
291
|
+
actions.appendChild(button);
|
|
292
|
+
section.insertBefore(actions, section.firstChild);
|
|
293
|
+
}
|
|
294
|
+
}, [printLabel, buttonClassName]);
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/components/blog-article-marks.tsx
|
|
299
|
+
var import_react2 = require("react");
|
|
300
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
301
|
+
var STORAGE_KEY = "mailoo_blog_visitor_key";
|
|
302
|
+
var VISITOR_STATE_PREFIX = "mailoo_blog_mark:";
|
|
303
|
+
var BTN = "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";
|
|
304
|
+
var BTN_IDLE = "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";
|
|
305
|
+
var BTN_ACTIVE = "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";
|
|
306
|
+
function createVisitorKey() {
|
|
307
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
308
|
+
return crypto.randomUUID();
|
|
309
|
+
}
|
|
310
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
311
|
+
const r = Math.random() * 16 | 0;
|
|
312
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
313
|
+
return v.toString(16);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function readVisitorKey() {
|
|
317
|
+
try {
|
|
318
|
+
const existing = localStorage.getItem(STORAGE_KEY);
|
|
319
|
+
if (existing && existing.length >= 32) return existing;
|
|
320
|
+
const next = createVisitorKey();
|
|
321
|
+
localStorage.setItem(STORAGE_KEY, next);
|
|
322
|
+
return next;
|
|
323
|
+
} catch {
|
|
324
|
+
return createVisitorKey();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function readLocalMark(slug) {
|
|
328
|
+
try {
|
|
329
|
+
const raw = localStorage.getItem(`${VISITOR_STATE_PREFIX}${slug}`);
|
|
330
|
+
if (!raw) return { sentiment: null, hasFeedback: false };
|
|
331
|
+
const parsed = JSON.parse(raw);
|
|
332
|
+
return {
|
|
333
|
+
sentiment: parsed.sentiment === "LIKE" || parsed.sentiment === "DISLIKE" ? parsed.sentiment : null,
|
|
334
|
+
hasFeedback: Boolean(parsed.hasFeedback)
|
|
335
|
+
};
|
|
336
|
+
} catch {
|
|
337
|
+
return { sentiment: null, hasFeedback: false };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function writeLocalMark(slug, mark) {
|
|
341
|
+
try {
|
|
342
|
+
localStorage.setItem(
|
|
343
|
+
`${VISITOR_STATE_PREFIX}${slug}`,
|
|
344
|
+
JSON.stringify(mark)
|
|
345
|
+
);
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function postMark(apiBasePath, slug, kind, body) {
|
|
350
|
+
const res = await fetch(
|
|
351
|
+
`${apiBasePath}/slug/${encodeURIComponent(slug)}/${kind}`,
|
|
352
|
+
{
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers: { "Content-Type": "application/json" },
|
|
355
|
+
body: JSON.stringify(body)
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
const json = await res.json();
|
|
359
|
+
if (!res.ok || !json.data) {
|
|
360
|
+
throw new Error(json.message || json.error || `HTTP ${res.status}`);
|
|
361
|
+
}
|
|
362
|
+
return json.data;
|
|
363
|
+
}
|
|
364
|
+
function BlogArticleMarks({
|
|
365
|
+
slug,
|
|
366
|
+
locale,
|
|
367
|
+
likeCount = 0,
|
|
368
|
+
dislikeCount = 0,
|
|
369
|
+
feedbackCount = 0,
|
|
370
|
+
apiBasePath = "/api/v1/blog",
|
|
371
|
+
labels,
|
|
372
|
+
className
|
|
373
|
+
}) {
|
|
374
|
+
const [visitorKey, setVisitorKey] = (0, import_react2.useState)(null);
|
|
375
|
+
const [counts, setCounts] = (0, import_react2.useState)({
|
|
376
|
+
likeCount,
|
|
377
|
+
dislikeCount,
|
|
378
|
+
feedbackCount
|
|
379
|
+
});
|
|
380
|
+
const [sentiment, setSentiment] = (0, import_react2.useState)(
|
|
381
|
+
null
|
|
382
|
+
);
|
|
383
|
+
const [hasFeedback, setHasFeedback] = (0, import_react2.useState)(false);
|
|
384
|
+
const [pending, setPending] = (0, import_react2.useState)(false);
|
|
385
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
386
|
+
const [modal, setModal] = (0, import_react2.useState)(null);
|
|
387
|
+
const [comment, setComment] = (0, import_react2.useState)("");
|
|
388
|
+
(0, import_react2.useEffect)(() => {
|
|
389
|
+
const key = readVisitorKey();
|
|
390
|
+
setVisitorKey(key);
|
|
391
|
+
const local = readLocalMark(slug);
|
|
392
|
+
setSentiment(local.sentiment);
|
|
393
|
+
setHasFeedback(local.hasFeedback);
|
|
394
|
+
}, [slug]);
|
|
395
|
+
(0, import_react2.useEffect)(() => {
|
|
396
|
+
setCounts({ likeCount, dislikeCount, feedbackCount });
|
|
397
|
+
}, [likeCount, dislikeCount, feedbackCount]);
|
|
398
|
+
const applyState = (0, import_react2.useCallback)(
|
|
399
|
+
(state) => {
|
|
400
|
+
setCounts({
|
|
401
|
+
likeCount: state.likeCount,
|
|
402
|
+
dislikeCount: state.dislikeCount,
|
|
403
|
+
feedbackCount: state.feedbackCount
|
|
404
|
+
});
|
|
405
|
+
setSentiment(state.sentiment);
|
|
406
|
+
setHasFeedback(state.hasFeedback);
|
|
407
|
+
writeLocalMark(slug, {
|
|
408
|
+
sentiment: state.sentiment,
|
|
409
|
+
hasFeedback: state.hasFeedback
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
[slug]
|
|
413
|
+
);
|
|
414
|
+
const run = (0, import_react2.useCallback)(
|
|
415
|
+
async (kind, body) => {
|
|
416
|
+
if (!visitorKey || pending) return;
|
|
417
|
+
setPending(true);
|
|
418
|
+
setError(null);
|
|
419
|
+
try {
|
|
420
|
+
const state = await postMark(apiBasePath, slug, kind, {
|
|
421
|
+
...body,
|
|
422
|
+
visitorKey,
|
|
423
|
+
...locale ? { locale } : {}
|
|
424
|
+
});
|
|
425
|
+
applyState(state);
|
|
426
|
+
setModal(null);
|
|
427
|
+
setComment("");
|
|
428
|
+
} catch {
|
|
429
|
+
setError(labels.error);
|
|
430
|
+
} finally {
|
|
431
|
+
setPending(false);
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
[apiBasePath, applyState, labels.error, locale, pending, slug, visitorKey]
|
|
435
|
+
);
|
|
436
|
+
const onLike = (0, import_react2.useCallback)(() => {
|
|
437
|
+
if (sentiment === "LIKE") {
|
|
438
|
+
void run("reaction", { action: "clear" });
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
void run("reaction", { action: "set", sentiment: "LIKE" });
|
|
442
|
+
}, [run, sentiment]);
|
|
443
|
+
const onDislikeClick = (0, import_react2.useCallback)(() => {
|
|
444
|
+
if (sentiment === "DISLIKE") {
|
|
445
|
+
void run("reaction", { action: "clear" });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (hasFeedback) {
|
|
449
|
+
void run("reaction", { action: "set", sentiment: "DISLIKE" });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
setModal("dislike");
|
|
453
|
+
}, [hasFeedback, run, sentiment]);
|
|
454
|
+
const onSubmitModal = (0, import_react2.useCallback)(() => {
|
|
455
|
+
const trimmed = comment.trim();
|
|
456
|
+
if (!trimmed) {
|
|
457
|
+
setError(labels.dislikeRequiresComment);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (modal === "dislike") {
|
|
461
|
+
void run("reaction", {
|
|
462
|
+
action: "set",
|
|
463
|
+
sentiment: "DISLIKE",
|
|
464
|
+
comment: trimmed
|
|
465
|
+
});
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
void run("feedback", { action: "set", comment: trimmed });
|
|
469
|
+
}, [comment, labels.dislikeRequiresComment, modal, run]);
|
|
470
|
+
const rootClass = (0, import_react2.useMemo)(
|
|
471
|
+
() => [
|
|
472
|
+
"mt-8 flex flex-col gap-3 border-t border-gray-200 pt-6 dark:border-gray-700",
|
|
473
|
+
className
|
|
474
|
+
].filter(Boolean).join(" "),
|
|
475
|
+
[className]
|
|
476
|
+
);
|
|
477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: rootClass, children: [
|
|
478
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
480
|
+
"button",
|
|
481
|
+
{
|
|
482
|
+
type: "button",
|
|
483
|
+
className: `${BTN} ${sentiment === "LIKE" ? BTN_ACTIVE : BTN_IDLE}`,
|
|
484
|
+
disabled: pending || !visitorKey,
|
|
485
|
+
onClick: onLike,
|
|
486
|
+
"aria-pressed": sentiment === "LIKE",
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: labels.like }),
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "tabular-nums", children: counts.likeCount })
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
),
|
|
493
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
494
|
+
"button",
|
|
495
|
+
{
|
|
496
|
+
type: "button",
|
|
497
|
+
className: `${BTN} ${sentiment === "DISLIKE" ? BTN_ACTIVE : BTN_IDLE}`,
|
|
498
|
+
disabled: pending || !visitorKey,
|
|
499
|
+
onClick: onDislikeClick,
|
|
500
|
+
"aria-pressed": sentiment === "DISLIKE",
|
|
501
|
+
children: [
|
|
502
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: labels.dislike }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "tabular-nums", children: counts.dislikeCount })
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
),
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
508
|
+
"button",
|
|
509
|
+
{
|
|
510
|
+
type: "button",
|
|
511
|
+
className: `${BTN} ${hasFeedback ? BTN_ACTIVE : BTN_IDLE}`,
|
|
512
|
+
disabled: pending || !visitorKey,
|
|
513
|
+
onClick: () => {
|
|
514
|
+
setError(null);
|
|
515
|
+
setModal("feedback");
|
|
516
|
+
},
|
|
517
|
+
"aria-pressed": hasFeedback,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: labels.feedback }),
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "tabular-nums", children: counts.feedbackCount })
|
|
521
|
+
]
|
|
522
|
+
}
|
|
523
|
+
)
|
|
524
|
+
] }),
|
|
525
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: error }) : null,
|
|
526
|
+
modal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "rounded-lg border border-gray-200 p-4 dark:border-gray-700", children: [
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "mb-2 block text-sm font-medium text-gray-800 dark:text-gray-200", children: modal === "dislike" ? labels.dislikeRequiresComment : labels.feedback }),
|
|
528
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
529
|
+
"textarea",
|
|
530
|
+
{
|
|
531
|
+
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",
|
|
532
|
+
rows: 3,
|
|
533
|
+
maxLength: 1e3,
|
|
534
|
+
value: comment,
|
|
535
|
+
placeholder: labels.feedbackPlaceholder,
|
|
536
|
+
onChange: (e) => setComment(e.target.value)
|
|
537
|
+
}
|
|
538
|
+
),
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-2", children: [
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
541
|
+
"button",
|
|
542
|
+
{
|
|
543
|
+
type: "button",
|
|
544
|
+
className: `${BTN} ${BTN_ACTIVE}`,
|
|
545
|
+
disabled: pending,
|
|
546
|
+
onClick: onSubmitModal,
|
|
547
|
+
children: labels.submit
|
|
548
|
+
}
|
|
549
|
+
),
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
551
|
+
"button",
|
|
552
|
+
{
|
|
553
|
+
type: "button",
|
|
554
|
+
className: `${BTN} ${BTN_IDLE}`,
|
|
555
|
+
disabled: pending,
|
|
556
|
+
onClick: () => {
|
|
557
|
+
setModal(null);
|
|
558
|
+
setComment("");
|
|
559
|
+
setError(null);
|
|
560
|
+
},
|
|
561
|
+
children: labels.cancel
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
] })
|
|
565
|
+
] }) : null
|
|
566
|
+
] });
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/components/blog-article-share.tsx
|
|
570
|
+
var import_react3 = require("react");
|
|
571
|
+
|
|
572
|
+
// src/lib/share-urls.ts
|
|
573
|
+
function requireAbsoluteHttpUrl(pageUrl) {
|
|
574
|
+
const trimmed = pageUrl.trim();
|
|
575
|
+
if (!trimmed) {
|
|
576
|
+
throw new Error("Share URL must be a non-empty absolute http(s) URL");
|
|
577
|
+
}
|
|
578
|
+
let parsed;
|
|
579
|
+
try {
|
|
580
|
+
parsed = new URL(trimmed);
|
|
581
|
+
} catch {
|
|
582
|
+
throw new Error(`Share URL is not a valid absolute URL: ${pageUrl}`);
|
|
583
|
+
}
|
|
584
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
585
|
+
throw new Error(`Share URL must use http or https: ${pageUrl}`);
|
|
586
|
+
}
|
|
587
|
+
return trimmed;
|
|
588
|
+
}
|
|
589
|
+
function buildFacebookSharerUrl(pageUrl, quote) {
|
|
590
|
+
const params = new URLSearchParams({
|
|
591
|
+
u: requireAbsoluteHttpUrl(pageUrl)
|
|
592
|
+
});
|
|
593
|
+
const trimmedQuote = quote?.trim();
|
|
594
|
+
if (trimmedQuote) {
|
|
595
|
+
params.set("quote", trimmedQuote);
|
|
596
|
+
}
|
|
597
|
+
return `https://www.facebook.com/sharer/sharer.php?${params.toString()}`;
|
|
598
|
+
}
|
|
599
|
+
function buildXIntentTweetUrl(pageUrl, text) {
|
|
600
|
+
const params = new URLSearchParams({
|
|
601
|
+
text,
|
|
602
|
+
url: requireAbsoluteHttpUrl(pageUrl)
|
|
603
|
+
});
|
|
604
|
+
return `https://twitter.com/intent/tweet?${params.toString()}`;
|
|
605
|
+
}
|
|
606
|
+
function buildLinkedInShareUrl(pageUrl) {
|
|
607
|
+
const params = new URLSearchParams({
|
|
608
|
+
url: requireAbsoluteHttpUrl(pageUrl)
|
|
609
|
+
});
|
|
610
|
+
return `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`;
|
|
611
|
+
}
|
|
612
|
+
function getSharePageUrlFromLocation(location) {
|
|
613
|
+
return `${location.origin}${location.pathname}`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/components/blog-article-share.tsx
|
|
617
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
618
|
+
var DEFAULT_BUTTON_CLASSNAME = [
|
|
619
|
+
"inline-flex items-center justify-center rounded-lg font-medium transition-colors",
|
|
620
|
+
"focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
621
|
+
"border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
|
|
622
|
+
"dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
|
|
623
|
+
"px-3 py-1.5 text-sm"
|
|
624
|
+
].join(" ");
|
|
625
|
+
var DEFAULT_HEADING_CLASSNAME = "mb-4 text-lg font-semibold text-gray-900 dark:text-white";
|
|
626
|
+
var DEFAULT_SECTION_CLASSNAME = "mt-12 border-t border-gray-200 pt-8 dark:border-gray-700";
|
|
627
|
+
function resolveShareUrl(url) {
|
|
628
|
+
if (url !== void 0) {
|
|
629
|
+
return requireAbsoluteHttpUrl(url);
|
|
630
|
+
}
|
|
631
|
+
if (typeof window === "undefined") {
|
|
632
|
+
throw new Error("Share URL requires a browser location when url is omitted");
|
|
633
|
+
}
|
|
634
|
+
return getSharePageUrlFromLocation(window.location);
|
|
635
|
+
}
|
|
636
|
+
function openShareWindow(href) {
|
|
637
|
+
window.open(href, "_blank", "noopener,noreferrer");
|
|
638
|
+
}
|
|
639
|
+
function BlogArticleShare({
|
|
640
|
+
title: _title,
|
|
641
|
+
text,
|
|
642
|
+
labels,
|
|
643
|
+
url,
|
|
644
|
+
className = DEFAULT_SECTION_CLASSNAME,
|
|
645
|
+
headingClassName = DEFAULT_HEADING_CLASSNAME,
|
|
646
|
+
buttonClassName = DEFAULT_BUTTON_CLASSNAME
|
|
647
|
+
}) {
|
|
648
|
+
const [linkCopied, setLinkCopied] = (0, import_react3.useState)(false);
|
|
649
|
+
const shareUrl = (0, import_react3.useCallback)(() => resolveShareUrl(url), [url]);
|
|
650
|
+
const onCopy = (0, import_react3.useCallback)(async () => {
|
|
651
|
+
try {
|
|
652
|
+
await navigator.clipboard.writeText(shareUrl());
|
|
653
|
+
setLinkCopied(true);
|
|
654
|
+
setTimeout(() => setLinkCopied(false), 2e3);
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
}, [shareUrl]);
|
|
658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, children: [
|
|
659
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: headingClassName, children: labels.heading }),
|
|
660
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
|
|
661
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
662
|
+
"button",
|
|
663
|
+
{
|
|
664
|
+
type: "button",
|
|
665
|
+
className: buttonClassName,
|
|
666
|
+
onClick: () => {
|
|
667
|
+
openShareWindow(buildXIntentTweetUrl(shareUrl(), text));
|
|
668
|
+
},
|
|
669
|
+
children: [
|
|
670
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
671
|
+
"svg",
|
|
672
|
+
{
|
|
673
|
+
className: "mr-2 h-4 w-4",
|
|
674
|
+
fill: "currentColor",
|
|
675
|
+
viewBox: "0 0 24 24",
|
|
676
|
+
"aria-hidden": "true",
|
|
677
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.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" })
|
|
678
|
+
}
|
|
679
|
+
),
|
|
680
|
+
labels.x
|
|
681
|
+
]
|
|
682
|
+
}
|
|
683
|
+
),
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
685
|
+
"button",
|
|
686
|
+
{
|
|
687
|
+
type: "button",
|
|
688
|
+
className: buttonClassName,
|
|
689
|
+
onClick: () => {
|
|
690
|
+
openShareWindow(buildFacebookSharerUrl(shareUrl()));
|
|
691
|
+
},
|
|
692
|
+
children: [
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
694
|
+
"svg",
|
|
695
|
+
{
|
|
696
|
+
className: "mr-2 h-4 w-4",
|
|
697
|
+
fill: "currentColor",
|
|
698
|
+
viewBox: "0 0 24 24",
|
|
699
|
+
"aria-hidden": "true",
|
|
700
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.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" })
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
labels.facebook
|
|
704
|
+
]
|
|
705
|
+
}
|
|
706
|
+
),
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
708
|
+
"button",
|
|
709
|
+
{
|
|
710
|
+
type: "button",
|
|
711
|
+
className: buttonClassName,
|
|
712
|
+
onClick: () => {
|
|
713
|
+
openShareWindow(buildLinkedInShareUrl(shareUrl()));
|
|
714
|
+
},
|
|
715
|
+
children: [
|
|
716
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
717
|
+
"svg",
|
|
718
|
+
{
|
|
719
|
+
className: "mr-2 h-4 w-4",
|
|
720
|
+
fill: "currentColor",
|
|
721
|
+
viewBox: "0 0 24 24",
|
|
722
|
+
"aria-hidden": "true",
|
|
723
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.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" })
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
labels.linkedin
|
|
727
|
+
]
|
|
728
|
+
}
|
|
729
|
+
),
|
|
730
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
731
|
+
"button",
|
|
732
|
+
{
|
|
733
|
+
type: "button",
|
|
734
|
+
className: buttonClassName,
|
|
735
|
+
onClick: () => void onCopy(),
|
|
736
|
+
children: [
|
|
737
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
738
|
+
"svg",
|
|
739
|
+
{
|
|
740
|
+
className: "mr-2 h-4 w-4",
|
|
741
|
+
fill: "none",
|
|
742
|
+
stroke: "currentColor",
|
|
743
|
+
viewBox: "0 0 24 24",
|
|
744
|
+
"aria-hidden": "true",
|
|
745
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
746
|
+
"path",
|
|
747
|
+
{
|
|
748
|
+
strokeLinecap: "round",
|
|
749
|
+
strokeLinejoin: "round",
|
|
750
|
+
strokeWidth: 2,
|
|
751
|
+
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"
|
|
752
|
+
}
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
),
|
|
756
|
+
linkCopied ? labels.linkCopied : labels.copyLink
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
)
|
|
760
|
+
] })
|
|
761
|
+
] });
|
|
762
|
+
}
|
|
763
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
764
|
+
0 && (module.exports = {
|
|
765
|
+
BlogArticleBody,
|
|
766
|
+
BlogArticleMarks,
|
|
767
|
+
BlogArticleShare,
|
|
768
|
+
BlogLinkedLinksDisplay,
|
|
769
|
+
BlogPrintableButtons,
|
|
770
|
+
buildFacebookSharerUrl,
|
|
771
|
+
buildLinkedInShareUrl,
|
|
772
|
+
buildXIntentTweetUrl,
|
|
773
|
+
getSharePageUrlFromLocation,
|
|
774
|
+
requireAbsoluteHttpUrl
|
|
775
|
+
});
|
|
776
|
+
//# sourceMappingURL=index.cjs.map
|