@haklex/rich-renderer-linkcard 0.0.40 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,1108 +1,16 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
+ import { jsxs, jsx } from "react/jsx-runtime";
5
5
  import { $createLinkNode, $isLinkNode } from "@lexical/link";
6
6
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
7
7
  import { $getNodeByKey, $createTextNode, $createParagraphNode, $insertNodes, $createNodeSelection, $setSelection, PASTE_COMMAND, $getSelection, $isRangeSelection, $isParagraphNode, COMMAND_PRIORITY_LOW } from "lexical";
8
- import { Star, Link, ExternalLink, RemoveFormatting, Unlink, CreditCard, Globe } from "lucide-react";
9
- import { useMemo, useState, useRef, useEffect, useCallback, createElement, createContext, use } from "react";
10
- import { vars } from "@haklex/rich-style-token";
11
- import { $isLinkCardNode, LinkCardNode, createRendererDecoration, LinkCardRenderer as LinkCardRenderer$1 } from "@haklex/rich-editor";
8
+ import { Link, ExternalLink, RemoveFormatting, Unlink, CreditCard } from "lucide-react";
9
+ import { useState, useRef, useEffect, useCallback, createElement, useMemo } from "react";
10
+ import { A as editWrapper, B as semanticClassNames, C as editPanel, D as editUrlRow, E as editLinkIcon, F as editInput, G as editActions, H as editActionButton, I as editActionButtonEnd, r as matchUrl } from "./LinkCardRenderer-BXL61dFZ.js";
11
+ import { L, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, u, v, w, x, y, z } from "./LinkCardRenderer-BXL61dFZ.js";
12
+ import { $isLinkCardNode, LinkCardNode, createRendererDecoration, LinkCardRenderer } from "@haklex/rich-editor";
12
13
  import { Popover, PopoverTrigger, PopoverPanel } from "@haklex/rich-editor-ui";
13
- import { useInView } from "react-intersection-observer";
14
- const arxivPlugin = {
15
- name: "arxiv",
16
- displayName: "arXiv Paper",
17
- priority: 80,
18
- typeClass: "academic",
19
- matchUrl(url) {
20
- if (url.hostname !== "arxiv.org") return null;
21
- const match = url.pathname.match(/\/(abs|pdf)\/(\d{4}\.\d+(?:v\d+)?)/i);
22
- if (!match) return null;
23
- return { id: match[2].toLowerCase(), fullUrl: url.toString() };
24
- },
25
- isValidId(id) {
26
- return /^\d{4}\.\d+(?:v\d+)?$/.test(id);
27
- },
28
- async fetch(id) {
29
- const response = await fetch(
30
- `https://export.arxiv.org/api/query?id_list=${id}`
31
- );
32
- const text = await response.text();
33
- const parser = new DOMParser();
34
- const xmlDoc = parser.parseFromString(text, "application/xml");
35
- const entry = xmlDoc.getElementsByTagName("entry")[0];
36
- const title2 = entry.getElementsByTagName("title")[0].textContent;
37
- const authors = entry.getElementsByTagName("author");
38
- const authorNames = Array.from(authors).map(
39
- (author) => author.getElementsByTagName("name")[0].textContent
40
- );
41
- return {
42
- title: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
43
- /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: title2 }),
44
- /* @__PURE__ */ jsx("span", { style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
45
- "span",
46
- {
47
- style: {
48
- display: "inline-flex",
49
- alignItems: "center",
50
- gap: "4px",
51
- fontSize: vars.typography.fontSizeMd,
52
- color: "#fb923c"
53
- },
54
- children: /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: id })
55
- }
56
- ) })
57
- ] }),
58
- desc: authorNames.length > 1 ? `${authorNames[0]} et al.` : authorNames[0]
59
- };
60
- }
61
- };
62
- function toCamelCase(str) {
63
- return str.replaceAll(/_([a-z])/g, (_, c) => c.toUpperCase());
64
- }
65
- function camelcaseKeys(obj) {
66
- if (Array.isArray(obj)) return obj.map(camelcaseKeys);
67
- if (obj !== null && typeof obj === "object") {
68
- return Object.fromEntries(
69
- Object.entries(obj).map(([k, v]) => [toCamelCase(k), camelcaseKeys(v)])
70
- );
71
- }
72
- return obj;
73
- }
74
- async function fetchJsonWithContext(url, context, provider, init) {
75
- const providerAdapter = provider && context?.adapters ? context.adapters[provider] : void 0;
76
- if (providerAdapter) {
77
- return providerAdapter.request(url, init);
78
- }
79
- if (context?.fetchJson) {
80
- return context.fetchJson(url, init);
81
- }
82
- const response = await fetch(url, init);
83
- if (!response.ok) {
84
- throw new Error(`Request failed: ${response.status}`);
85
- }
86
- return response.json();
87
- }
88
- async function fetchGitHubApi(url, context) {
89
- const path = url.replace("https://api.github.com", "");
90
- return fetchJsonWithContext(
91
- `https://api.github.com${path}`,
92
- context,
93
- "github"
94
- );
95
- }
96
- const LanguageToColorMap = {
97
- typescript: "#2b7489",
98
- javascript: "#f1e05a",
99
- html: "#e34c26",
100
- java: "#b07219",
101
- go: "#00add8",
102
- vue: "#2c3e50",
103
- css: "#563d7c",
104
- yaml: "#cb171e",
105
- json: "#292929",
106
- markdown: "#083fa1",
107
- csharp: "#178600",
108
- "c#": "#178600",
109
- c: "#555555",
110
- cpp: "#f34b7d",
111
- "c++": "#f34b7d",
112
- python: "#3572a5",
113
- lua: "#000080",
114
- vimscript: "#199f4b",
115
- shell: "#89e051",
116
- dockerfile: "#384d54",
117
- ruby: "#701516",
118
- php: "#4f5d95",
119
- lisp: "#3fb68b",
120
- kotlin: "#F18E33",
121
- rust: "#dea584",
122
- dart: "#00B4AB",
123
- swift: "#ffac45",
124
- "objective-c": "#438eff",
125
- "objective-c++": "#6866fb",
126
- r: "#198ce7",
127
- matlab: "#e16737",
128
- scala: "#c22d40",
129
- sql: "#e38c00",
130
- perl: "#0298c3"
131
- };
132
- const bangumiTypeMap = {
133
- subject: "subjects",
134
- character: "characters",
135
- person: "persons"
136
- };
137
- const allowedBangumiTypes = Object.keys(bangumiTypeMap);
138
- function hslToHex(h, s, l) {
139
- s /= 100;
140
- l /= 100;
141
- const a = s * Math.min(l, 1 - l);
142
- const f = (n) => {
143
- const k = (n + h / 30) % 12;
144
- const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
145
- return Math.round(255 * color).toString(16).padStart(2, "0");
146
- };
147
- return `#${f(0)}${f(8)}${f(4)}`;
148
- }
149
- function generateColor(str, saturation = [30, 35], lightness = [60, 70]) {
150
- let hash = 0;
151
- for (let i = 0; i < str.length; i++) {
152
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
153
- }
154
- const sRange = saturation[1] - saturation[0] + 1;
155
- const lRange = lightness[1] - lightness[0] + 1;
156
- const h = Math.abs(hash) % 360;
157
- const s = saturation[0] + Math.abs(hash >> 8) % sRange;
158
- const l = lightness[0] + Math.abs(hash >> 16) % lRange;
159
- return hslToHex(h, s, l);
160
- }
161
- function stripMarkdown(text) {
162
- return text.replaceAll(/!\[.*?\]\(.*?\)/g, "").replaceAll(/\[([^\]]*)\]\(.*?\)/g, "$1").replaceAll(/[*_~`#>]/g, "").replaceAll(/\n+/g, " ").trim();
163
- }
164
- function getDifficultyColor(difficulty) {
165
- switch (difficulty) {
166
- case "Easy":
167
- return "#00BFA5";
168
- case "Medium":
169
- return "#FFA726";
170
- case "Hard":
171
- return "#F44336";
172
- default:
173
- return "#757575";
174
- }
175
- }
176
- const leetcodePlugin = {
177
- name: "leetcode",
178
- displayName: "LeetCode",
179
- priority: 65,
180
- typeClass: "wide",
181
- provider: "leetcode",
182
- matchUrl(url) {
183
- if (url.hostname !== "leetcode.cn" && url.hostname !== "leetcode.com")
184
- return null;
185
- const parts = url.pathname.split("/").filter(Boolean);
186
- if (parts[0] !== "problems" || !parts[1]) return null;
187
- return { id: parts[1], fullUrl: url.toString() };
188
- },
189
- isValidId(id) {
190
- return typeof id === "string" && id.length > 0;
191
- },
192
- async fetch(id, _meta, context) {
193
- const body = {
194
- query: `query questionData($titleSlug: String!) {
195
- question(titleSlug: $titleSlug) {translatedTitle
196
- difficulty
197
- likes
198
- topicTags { translatedName
199
- }
200
- stats
201
- }
202
- }
203
- `,
204
- variables: { titleSlug: id }
205
- };
206
- const questionData = await fetchJsonWithContext(
207
- "https://leetcode.cn/graphql/",
208
- context,
209
- "leetcode",
210
- {
211
- method: "POST",
212
- headers: { "Content-Type": "application/json" },
213
- body: JSON.stringify(body)
214
- }
215
- );
216
- const questionTitleData = camelcaseKeys(questionData.data.question);
217
- const stats = JSON.parse(questionTitleData.stats);
218
- return {
219
- title: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
220
- /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: questionTitleData.translatedTitle }),
221
- /* @__PURE__ */ jsx("span", { style: { flexShrink: 0 }, children: questionTitleData.likes > 0 && /* @__PURE__ */ jsxs(
222
- "span",
223
- {
224
- style: {
225
- display: "inline-flex",
226
- alignItems: "center",
227
- gap: "4px",
228
- fontSize: vars.typography.fontSizeMd,
229
- color: "#fb923c"
230
- },
231
- children: [
232
- "👍",
233
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: questionTitleData.likes })
234
- ]
235
- }
236
- ) })
237
- ] }),
238
- desc: /* @__PURE__ */ jsxs(Fragment, { children: [
239
- /* @__PURE__ */ jsx(
240
- "span",
241
- {
242
- style: {
243
- marginRight: "16px",
244
- fontWeight: "bold",
245
- color: getDifficultyColor(questionTitleData.difficulty)
246
- },
247
- children: questionTitleData.difficulty
248
- }
249
- ),
250
- /* @__PURE__ */ jsx("span", { style: { overflow: "hidden" }, children: questionTitleData.topicTags.map((tag) => tag.translatedName).join(" / ") }),
251
- /* @__PURE__ */ jsxs("span", { style: { float: "right", overflow: "hidden" }, children: [
252
- "AR: ",
253
- stats.acRate
254
- ] })
255
- ] }),
256
- image: "https://upload.wikimedia.org/wikipedia/commons/1/19/LeetCode_logo_black.png",
257
- color: getDifficultyColor(questionTitleData.difficulty)
258
- };
259
- }
260
- };
261
- const githubCommitPlugin = {
262
- name: "gh-commit",
263
- displayName: "GitHub Commit",
264
- priority: 95,
265
- typeClass: "github",
266
- provider: "github",
267
- matchUrl(url) {
268
- if (url.hostname !== "github.com") return null;
269
- const parts = url.pathname.split("/").filter(Boolean);
270
- if (parts.length < 4 || parts[2] !== "commit") return null;
271
- const [owner, repo, , commitId] = parts;
272
- return {
273
- id: `${owner}/${repo}/commit/${commitId}`,
274
- fullUrl: url.toString()
275
- };
276
- },
277
- isValidId(id) {
278
- const parts = id.split("/");
279
- return parts.length === 4 && parts.every((p) => p.length > 0) && parts[2] === "commit";
280
- },
281
- async fetch(id, _meta, context) {
282
- const [owner, repo, , commitId] = id.split("/");
283
- const response = await fetchGitHubApi(
284
- `https://api.github.com/repos/${owner}/${repo}/commits/${commitId}`,
285
- context
286
- );
287
- const data = camelcaseKeys(response);
288
- return {
289
- title: /* @__PURE__ */ jsx("span", { style: { fontWeight: "normal" }, children: data.commit.message.replace(/Signed-off-by:.+/s, "").trim() }),
290
- desc: /* @__PURE__ */ jsxs(
291
- "span",
292
- {
293
- style: {
294
- display: "flex",
295
- flexWrap: "wrap",
296
- alignItems: "center",
297
- gap: "4px 12px",
298
- fontFamily: vars.typography.fontMono
299
- },
300
- children: [
301
- /* @__PURE__ */ jsxs(
302
- "span",
303
- {
304
- style: {
305
- display: "inline-flex",
306
- alignItems: "center",
307
- gap: "8px"
308
- },
309
- children: [
310
- /* @__PURE__ */ jsxs("span", { style: { color: "#238636" }, children: [
311
- "+",
312
- data.stats.additions
313
- ] }),
314
- /* @__PURE__ */ jsxs("span", { style: { color: "#f85149" }, children: [
315
- "-",
316
- data.stats.deletions
317
- ] })
318
- ]
319
- }
320
- ),
321
- /* @__PURE__ */ jsx("span", { style: { fontSize: vars.typography.fontSizeMd }, children: data.sha.slice(0, 7) }),
322
- /* @__PURE__ */ jsxs("span", { style: { fontSize: vars.typography.fontSizeMd, opacity: 0.8 }, children: [
323
- owner,
324
- "/",
325
- repo
326
- ] })
327
- ]
328
- }
329
- ),
330
- image: data.author?.avatarUrl
331
- };
332
- }
333
- };
334
- const githubDiscussionPlugin = {
335
- name: "gh-discussion",
336
- displayName: "GitHub Discussion",
337
- priority: 95,
338
- typeClass: "github",
339
- provider: "github",
340
- matchUrl(url) {
341
- if (url.hostname !== "github.com") return null;
342
- if (!url.pathname.includes("/discussions/")) return null;
343
- const parts = url.pathname.split("/").filter(Boolean);
344
- if (parts.length < 4 || parts[2] !== "discussions") return null;
345
- const discussionNumber = parts[3];
346
- if (!/^\d+$/.test(discussionNumber)) return null;
347
- const [owner, repo] = parts;
348
- return {
349
- id: `${owner}/${repo}/${discussionNumber}`,
350
- fullUrl: url.toString()
351
- };
352
- },
353
- isValidId(id) {
354
- const parts = id.split("/");
355
- return parts.length === 3 && parts.every((p) => p.length > 0) && /^\d+$/.test(parts[2]);
356
- },
357
- async fetch(id, _meta, context) {
358
- const [owner, repo, discussionNumber] = id.split("/");
359
- const response = await fetchGitHubApi(
360
- `https://api.github.com/repos/${owner}/${repo}/discussions/${discussionNumber}`,
361
- context
362
- );
363
- const data = camelcaseKeys(response);
364
- const categoryName = data.category?.name || "Discussion";
365
- return {
366
- title: `Discussion: ${data.title}`,
367
- desc: /* @__PURE__ */ jsxs(
368
- "span",
369
- {
370
- style: {
371
- display: "flex",
372
- flexWrap: "wrap",
373
- alignItems: "center",
374
- gap: "4px 12px",
375
- fontFamily: vars.typography.fontMono
376
- },
377
- children: [
378
- /* @__PURE__ */ jsx(
379
- "span",
380
- {
381
- style: {
382
- borderRadius: "4px",
383
- backgroundColor: "rgba(128,128,128,0.15)",
384
- padding: "2px 6px",
385
- fontSize: vars.typography.fontSizeXs
386
- },
387
- children: categoryName
388
- }
389
- ),
390
- /* @__PURE__ */ jsxs("span", { style: { fontSize: vars.typography.fontSizeMd, opacity: 0.8 }, children: [
391
- "#",
392
- discussionNumber,
393
- " · ",
394
- owner,
395
- "/",
396
- repo
397
- ] })
398
- ]
399
- }
400
- ),
401
- image: data.user?.avatarUrl
402
- };
403
- }
404
- };
405
- const stateColors = {
406
- open: "#238636",
407
- closed: "#f85149"
408
- };
409
- const stateTextMap$1 = {
410
- open: "Open",
411
- closed: "Closed"
412
- };
413
- const githubIssuePlugin = {
414
- name: "gh-issue",
415
- displayName: "GitHub Issue",
416
- priority: 95,
417
- typeClass: "github",
418
- provider: "github",
419
- matchUrl(url) {
420
- if (url.hostname !== "github.com") return null;
421
- if (!url.pathname.includes("/issues/")) return null;
422
- const parts = url.pathname.split("/").filter(Boolean);
423
- if (parts.length < 4 || parts[2] !== "issues") return null;
424
- const issueNumber = parts[3];
425
- if (!/^\d+$/.test(issueNumber)) return null;
426
- const [owner, repo] = parts;
427
- return { id: `${owner}/${repo}/${issueNumber}`, fullUrl: url.toString() };
428
- },
429
- isValidId(id) {
430
- const parts = id.split("/");
431
- return parts.length === 3 && parts.every((p) => p.length > 0) && /^\d+$/.test(parts[2]);
432
- },
433
- async fetch(id, _meta, context) {
434
- const [owner, repo, issueNumber] = id.split("/");
435
- const response = await fetchGitHubApi(
436
- `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`,
437
- context
438
- );
439
- const data = camelcaseKeys(response);
440
- const color = stateColors[data.state] || "#6b7280";
441
- const stateText = stateTextMap$1[data.state] || data.state;
442
- return {
443
- title: `Issue: ${data.title}`,
444
- color,
445
- desc: /* @__PURE__ */ jsxs(
446
- "span",
447
- {
448
- style: {
449
- display: "flex",
450
- flexWrap: "wrap",
451
- alignItems: "center",
452
- gap: "4px 12px",
453
- fontFamily: vars.typography.fontMono
454
- },
455
- children: [
456
- /* @__PURE__ */ jsx("span", { style: { color }, children: stateText }),
457
- /* @__PURE__ */ jsxs("span", { style: { fontSize: vars.typography.fontSizeMd, opacity: 0.8 }, children: [
458
- "#",
459
- issueNumber,
460
- " · ",
461
- owner,
462
- "/",
463
- repo
464
- ] })
465
- ]
466
- }
467
- ),
468
- image: data.user?.avatarUrl
469
- };
470
- }
471
- };
472
- const getPrState = (data) => {
473
- if (data.merged) return "merged";
474
- return data.state;
475
- };
476
- const stateColorMap = {
477
- open: "#238636",
478
- merged: "#8957e5",
479
- closed: "#f85149"
480
- };
481
- const stateTextMap = {
482
- open: "Open",
483
- merged: "Merged",
484
- closed: "Closed"
485
- };
486
- const githubPrPlugin = {
487
- name: "gh-pr",
488
- displayName: "GitHub Pull Request",
489
- priority: 95,
490
- typeClass: "github",
491
- provider: "github",
492
- matchUrl(url) {
493
- if (url.hostname !== "github.com") return null;
494
- if (!url.pathname.includes("/pull/")) return null;
495
- const parts = url.pathname.split("/").filter(Boolean);
496
- if (parts.length < 4 || parts[2] !== "pull") return null;
497
- const [owner, repo, , prNumber] = parts;
498
- return { id: `${owner}/${repo}/${prNumber}`, fullUrl: url.toString() };
499
- },
500
- isValidId(id) {
501
- const parts = id.split("/");
502
- return parts.length === 3 && parts.every((p) => p.length > 0);
503
- },
504
- async fetch(id, _meta, context) {
505
- const [owner, repo, prNumber] = id.split("/");
506
- const response = await fetchGitHubApi(
507
- `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`,
508
- context
509
- );
510
- const data = camelcaseKeys(response);
511
- const state = getPrState(data);
512
- const color = stateColorMap[state];
513
- const stateText = stateTextMap[state];
514
- return {
515
- title: `PR: ${data.title}`,
516
- color,
517
- desc: /* @__PURE__ */ jsxs(
518
- "span",
519
- {
520
- style: {
521
- display: "flex",
522
- flexWrap: "wrap",
523
- alignItems: "center",
524
- gap: "4px 12px",
525
- fontFamily: vars.typography.fontMono
526
- },
527
- children: [
528
- /* @__PURE__ */ jsx("span", { style: { color }, children: stateText }),
529
- /* @__PURE__ */ jsxs(
530
- "span",
531
- {
532
- style: {
533
- display: "inline-flex",
534
- alignItems: "center",
535
- gap: "8px",
536
- whiteSpace: "nowrap"
537
- },
538
- children: [
539
- /* @__PURE__ */ jsxs("span", { style: { color: "#238636" }, children: [
540
- "+",
541
- data.additions
542
- ] }),
543
- /* @__PURE__ */ jsxs("span", { style: { color: "#f85149" }, children: [
544
- "-",
545
- data.deletions
546
- ] })
547
- ]
548
- }
549
- ),
550
- /* @__PURE__ */ jsxs("span", { style: { fontSize: vars.typography.fontSizeMd, opacity: 0.8 }, children: [
551
- owner,
552
- "/",
553
- repo
554
- ] })
555
- ]
556
- }
557
- ),
558
- image: data.user.avatarUrl
559
- };
560
- }
561
- };
562
- function formatStargazers(n) {
563
- return n.toLocaleString("en-US");
564
- }
565
- const githubRepoPlugin = {
566
- name: "gh-repo",
567
- displayName: "GitHub Repository",
568
- priority: 100,
569
- typeClass: "github",
570
- provider: "github",
571
- matchUrl(url) {
572
- if (url.hostname !== "github.com") return null;
573
- const parts = url.pathname.split("/").filter(Boolean);
574
- if (parts.length !== 2) return null;
575
- const [owner, repo] = parts;
576
- if (!owner || !repo) return null;
577
- return { id: `${owner}/${repo}`, fullUrl: url.toString() };
578
- },
579
- isValidId(id) {
580
- const parts = id.split("/");
581
- return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
582
- },
583
- async fetch(id, _meta, context) {
584
- const [owner, repo] = id.split("/");
585
- const response = await fetchGitHubApi(
586
- `https://api.github.com/repos/${owner}/${repo}`,
587
- context
588
- );
589
- const data = camelcaseKeys(response);
590
- return {
591
- title: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
592
- /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: data.name }),
593
- /* @__PURE__ */ jsx("span", { style: { flexShrink: 0 }, children: data.stargazersCount > 0 && /* @__PURE__ */ jsxs(
594
- "span",
595
- {
596
- style: {
597
- display: "inline-flex",
598
- alignItems: "center",
599
- gap: "4px",
600
- fontSize: vars.typography.fontSizeMd,
601
- color: "#fb923c"
602
- },
603
- children: [
604
- /* @__PURE__ */ jsx(Star, { size: 14, strokeWidth: 2, "aria-hidden": true }),
605
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: formatStargazers(data.stargazersCount) })
606
- ]
607
- }
608
- ) })
609
- ] }),
610
- desc: data.description,
611
- image: data.owner.avatarUrl,
612
- color: LanguageToColorMap[data.language?.toLowerCase()]
613
- };
614
- }
615
- };
616
- const bangumiPlugin = {
617
- name: "bangumi",
618
- displayName: "Bangumi",
619
- priority: 70,
620
- typeClass: "media",
621
- provider: "bangumi",
622
- matchUrl(url) {
623
- if (url.hostname !== "bgm.tv" && url.hostname !== "bangumi.tv") return null;
624
- const parts = url.pathname.split("/").filter(Boolean);
625
- if (parts.length < 2) return null;
626
- const [type, realId] = parts;
627
- if (!allowedBangumiTypes.includes(type)) return null;
628
- return { id: `${type}/${realId}`, fullUrl: url.toString(), meta: { type } };
629
- },
630
- isValidId(id) {
631
- const [type, realId] = id.split("/");
632
- return allowedBangumiTypes.includes(type) && realId?.length > 0;
633
- },
634
- async fetch(id, _meta, context) {
635
- const [type, realId] = id.split("/");
636
- const json = await fetchJsonWithContext(
637
- `https://api.bgm.tv/v0/${bangumiTypeMap[type]}/${realId}`,
638
- context,
639
- "bangumi"
640
- );
641
- let title2 = "";
642
- let originalTitle = "";
643
- if (type === "subject") {
644
- if (json.name_cn && json.name_cn !== json.name && json.name_cn !== "") {
645
- title2 = json.name_cn;
646
- originalTitle = json.name;
647
- } else {
648
- title2 = json.name;
649
- originalTitle = json.name;
650
- }
651
- } else if (type === "character" || type === "person") {
652
- const { infobox } = json;
653
- infobox.forEach(
654
- (item) => {
655
- if (item.key === "简体中文名") {
656
- title2 = typeof item.value === "string" ? item.value : item.value[0].v;
657
- } else if (item.key === "别名") {
658
- const aliases = item.value;
659
- aliases.forEach((alias) => {
660
- originalTitle += `${alias.v} / `;
661
- });
662
- originalTitle = originalTitle.slice(0, -3);
663
- }
664
- }
665
- );
666
- }
667
- const starStyle = {
668
- display: "inline-flex",
669
- flexShrink: 0,
670
- alignItems: "center",
671
- gap: "4px",
672
- alignSelf: "center",
673
- fontSize: vars.typography.fontSizeXs,
674
- color: "#fb923c"
675
- };
676
- return {
677
- title: /* @__PURE__ */ jsxs(
678
- "span",
679
- {
680
- style: {
681
- display: "flex",
682
- flexWrap: "wrap",
683
- alignItems: "flex-end",
684
- gap: "8px"
685
- },
686
- children: [
687
- /* @__PURE__ */ jsx("span", { children: title2 }),
688
- title2 !== originalTitle && /* @__PURE__ */ jsxs(
689
- "span",
690
- {
691
- style: { fontSize: vars.typography.fontSizeMd, opacity: 0.7 },
692
- children: [
693
- "(",
694
- originalTitle,
695
- ")"
696
- ]
697
- }
698
- ),
699
- type === "subject" && /* @__PURE__ */ jsxs(
700
- "span",
701
- {
702
- style: {
703
- display: "inline-flex",
704
- flexShrink: 0,
705
- alignItems: "center",
706
- gap: "12px",
707
- alignSelf: "center"
708
- },
709
- children: [
710
- /* @__PURE__ */ jsxs("span", { style: starStyle, children: [
711
- "★",
712
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: json.rating.score > 0 && json.rating.score.toFixed(1) })
713
- ] }),
714
- /* @__PURE__ */ jsxs("span", { style: starStyle, children: [
715
- "☆",
716
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: json.collection && json.collection.on_hold + json.collection.dropped + json.collection.wish + json.collection.collect + json.collection.doing })
717
- ] })
718
- ]
719
- }
720
- ),
721
- (type === "character" || type === "person") && /* @__PURE__ */ jsxs("span", { style: starStyle, children: [
722
- "☆",
723
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: json.stat.collects > 0 && json.stat.collects })
724
- ] })
725
- ]
726
- }
727
- ),
728
- desc: /* @__PURE__ */ jsx("span", { style: { overflow: "visible", whiteSpace: "pre-wrap" }, children: json.summary }),
729
- image: json.images.grid,
730
- color: generateColor(title2),
731
- classNames: {
732
- image: "link-card__image--poster",
733
- cardRoot: "link-card--reversed"
734
- }
735
- };
736
- }
737
- };
738
- const neteaseMusicPlugin = {
739
- name: "netease-music-song",
740
- displayName: "Netease Music Song",
741
- priority: 60,
742
- typeClass: "wide",
743
- provider: "netease-music",
744
- matchUrl(url) {
745
- if (url.hostname !== "music.163.com") return null;
746
- if (!url.pathname.includes("/song") && !url.hash.includes("/song"))
747
- return null;
748
- const urlString = url.toString().replaceAll("/#/", "/");
749
- const _url = new URL(urlString);
750
- const id = _url.searchParams.get("id");
751
- if (!id) return null;
752
- return { id, fullUrl: url.toString() };
753
- },
754
- isValidId(id) {
755
- return id.length > 0;
756
- },
757
- async fetch(id, _meta, context) {
758
- const songData = await fetchJsonWithContext(
759
- `https://music.163.com/song/${id}`,
760
- context,
761
- "netease-music"
762
- );
763
- const songInfo = songData.songs[0];
764
- const albumInfo = songInfo.al;
765
- const singerInfo = songInfo.ar;
766
- return {
767
- title: /* @__PURE__ */ jsxs(Fragment, { children: [
768
- /* @__PURE__ */ jsx("span", { children: songInfo.name }),
769
- songInfo.tns && /* @__PURE__ */ jsx(
770
- "span",
771
- {
772
- style: {
773
- marginLeft: "8px",
774
- fontSize: vars.typography.fontSizeMd,
775
- color: "#a1a1aa"
776
- },
777
- children: songInfo.tns[0]
778
- }
779
- )
780
- ] }),
781
- desc: /* @__PURE__ */ jsxs(Fragment, { children: [
782
- /* @__PURE__ */ jsxs("span", { style: { display: "block" }, children: [
783
- /* @__PURE__ */ jsx("span", { style: { fontWeight: "bold" }, children: "歌手:" }),
784
- /* @__PURE__ */ jsx("span", { children: singerInfo.map((p) => p.name).join(" / ") })
785
- ] }),
786
- /* @__PURE__ */ jsxs("span", { style: { display: "block" }, children: [
787
- /* @__PURE__ */ jsx("span", { style: { fontWeight: "bold" }, children: "专辑:" }),
788
- /* @__PURE__ */ jsx("span", { children: albumInfo.name })
789
- ] })
790
- ] }),
791
- image: albumInfo.picUrl,
792
- color: "#e72d2c"
793
- };
794
- }
795
- };
796
- const qqMusicPlugin = {
797
- name: "qq-music-song",
798
- displayName: "QQ Music Song",
799
- priority: 60,
800
- typeClass: "wide",
801
- provider: "qq-music",
802
- matchUrl(url) {
803
- if (url.hostname !== "y.qq.com") return null;
804
- if (!url.pathname.includes("/songDetail/")) return null;
805
- const parts = url.pathname.split("/");
806
- const songDetailIndex = parts.indexOf("songDetail");
807
- if (songDetailIndex === -1 || !parts[songDetailIndex + 1]) return null;
808
- return { id: parts[songDetailIndex + 1], fullUrl: url.toString() };
809
- },
810
- isValidId(id) {
811
- return typeof id === "string" && id.length > 0;
812
- },
813
- async fetch(id, _meta, context) {
814
- const songData = await fetchJsonWithContext(
815
- `https://y.qq.com/song/${id}`,
816
- context,
817
- "qq-music"
818
- );
819
- const songInfo = songData.data[0];
820
- const albumId = songInfo.album.mid;
821
- return {
822
- title: /* @__PURE__ */ jsxs(Fragment, { children: [
823
- /* @__PURE__ */ jsx("span", { children: songInfo.title }),
824
- songInfo.subtitle && /* @__PURE__ */ jsx(
825
- "span",
826
- {
827
- style: {
828
- marginLeft: "8px",
829
- fontSize: vars.typography.fontSizeMd,
830
- color: "#a1a1aa"
831
- },
832
- children: songInfo.subtitle
833
- }
834
- )
835
- ] }),
836
- desc: /* @__PURE__ */ jsxs(Fragment, { children: [
837
- /* @__PURE__ */ jsxs("span", { style: { display: "block" }, children: [
838
- /* @__PURE__ */ jsx("span", { style: { fontWeight: "bold" }, children: "歌手:" }),
839
- /* @__PURE__ */ jsx("span", { children: songInfo.singer.map((p) => p.name).join(" / ") })
840
- ] }),
841
- /* @__PURE__ */ jsxs("span", { style: { display: "block" }, children: [
842
- /* @__PURE__ */ jsx("span", { style: { fontWeight: "bold" }, children: "专辑:" }),
843
- /* @__PURE__ */ jsx("span", { children: songInfo.album.name })
844
- ] })
845
- ] }),
846
- image: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumId}.jpg?max_age=2592000`,
847
- color: "#31c27c"
848
- };
849
- }
850
- };
851
- const TMDB_LANGUAGE_BY_LOCALE = {
852
- zh: "zh-CN",
853
- en: "en-US",
854
- ja: "ja-JP"
855
- };
856
- const getCurrentLocale = () => {
857
- if (typeof document !== "undefined") {
858
- const lang = document.documentElement?.lang?.trim();
859
- if (lang) return lang;
860
- }
861
- if (typeof window !== "undefined") {
862
- const firstSegment = window.location.pathname.split("/").find((s) => s.length > 0);
863
- if (firstSegment) return firstSegment;
864
- }
865
- return "en";
866
- };
867
- const tmdbPlugin = {
868
- name: "tmdb",
869
- displayName: "The Movie Database",
870
- priority: 70,
871
- typeClass: "media",
872
- provider: "tmdb",
873
- matchUrl(url) {
874
- if (!url.hostname.includes("themoviedb.org")) return null;
875
- const parts = url.pathname.split("/").filter(Boolean);
876
- if (parts.length < 2) return null;
877
- const [type, realId] = parts;
878
- const canParsedTypes = ["tv", "movie"];
879
- if (!canParsedTypes.includes(type) || !realId) return null;
880
- return { id: `${type}/${realId}`, fullUrl: url.toString(), meta: { type } };
881
- },
882
- isValidId(id) {
883
- const [type, realId] = id.split("/");
884
- const canParsedTypes = ["tv", "movie"];
885
- return canParsedTypes.includes(type) && realId?.length > 0;
886
- },
887
- async fetch(id, _meta, context) {
888
- const [type, realId] = id.split("/");
889
- const locale = getCurrentLocale();
890
- const userLanguage = TMDB_LANGUAGE_BY_LOCALE[locale] || (typeof navigator !== "undefined" ? navigator.language : "en-US") || "en-US";
891
- const json = await fetchJsonWithContext(
892
- `https://api.themoviedb.org/3/${type}/${realId}?language=${userLanguage}`,
893
- context,
894
- "tmdb"
895
- );
896
- const title2 = type === "tv" ? json.name : json.title;
897
- const originalTitle = type === "tv" ? json.original_name : json.original_title;
898
- return {
899
- title: /* @__PURE__ */ jsxs(
900
- "span",
901
- {
902
- style: {
903
- display: "flex",
904
- flexWrap: "wrap",
905
- alignItems: "flex-end",
906
- gap: "8px"
907
- },
908
- children: [
909
- /* @__PURE__ */ jsx("span", { children: title2 }),
910
- title2 !== originalTitle && /* @__PURE__ */ jsxs(
911
- "span",
912
- {
913
- style: { fontSize: vars.typography.fontSizeMd, opacity: 0.7 },
914
- children: [
915
- "(",
916
- originalTitle,
917
- ")"
918
- ]
919
- }
920
- ),
921
- /* @__PURE__ */ jsxs(
922
- "span",
923
- {
924
- style: {
925
- display: "inline-flex",
926
- flexShrink: 0,
927
- alignItems: "center",
928
- gap: "4px",
929
- alignSelf: "center",
930
- fontSize: vars.typography.fontSizeXs,
931
- color: "#fb923c"
932
- },
933
- children: [
934
- "★",
935
- /* @__PURE__ */ jsx("span", { style: { fontFamily: "sans-serif", fontWeight: 500 }, children: json.vote_average > 0 && json.vote_average.toFixed(1) })
936
- ]
937
- }
938
- )
939
- ]
940
- }
941
- ),
942
- desc: /* @__PURE__ */ jsx("span", { style: { overflow: "visible", whiteSpace: "pre-wrap" }, children: json.overview }),
943
- image: `https://image.tmdb.org/t/p/w500${json.poster_path}`,
944
- color: generateColor(title2 || originalTitle || id),
945
- classNames: {
946
- image: "link-card__image--poster",
947
- cardRoot: "link-card--reversed"
948
- }
949
- };
950
- }
951
- };
952
- function createMxSpacePlugin(config) {
953
- const webHost = new URL(config.webUrl).hostname;
954
- return {
955
- name: "self",
956
- displayName: "MxSpace Article",
957
- priority: 10,
958
- provider: "mx-space",
959
- matchUrl(url) {
960
- if (webHost !== url.hostname) return null;
961
- if (!url.pathname.startsWith("/posts/") && !url.pathname.startsWith("/notes/")) {
962
- return null;
963
- }
964
- return { id: url.pathname.slice(1), fullUrl: url.toString() };
965
- },
966
- isValidId(id) {
967
- const [type, ...rest] = id.split("/");
968
- if (type !== "posts" && type !== "notes") return false;
969
- if (type === "posts") return rest.length === 2;
970
- return rest.length === 1;
971
- },
972
- async fetch(id, _meta, context) {
973
- const [type, ...rest] = id.split("/");
974
- let data = { title: "", text: "" };
975
- if (type === "posts") {
976
- const [cate, slug] = rest;
977
- data = await fetchJsonWithContext(
978
- `posts/${cate}/${slug}`,
979
- context,
980
- "mx-space"
981
- );
982
- } else if (type === "notes") {
983
- const [nid] = rest;
984
- const response = await fetchJsonWithContext(
985
- `notes/${nid}`,
986
- context,
987
- "mx-space"
988
- );
989
- data = response.data || response;
990
- }
991
- const coverImage = data.cover || data.meta?.cover;
992
- const spotlightColor = generateColor(data.title);
993
- return {
994
- title: data.title,
995
- desc: data.summary || `${stripMarkdown(data.text).slice(0, 50)}...`,
996
- image: coverImage || data.images?.[0]?.src,
997
- color: spotlightColor
998
- };
999
- }
1000
- };
1001
- }
1002
- const mxSpacePlugin = {
1003
- name: "self",
1004
- displayName: "MxSpace Article",
1005
- priority: 10,
1006
- provider: "mx-space",
1007
- matchUrl(_url) {
1008
- return null;
1009
- },
1010
- isValidId(id) {
1011
- const [type, ...rest] = id.split("/");
1012
- if (type !== "posts" && type !== "notes") return false;
1013
- if (type === "posts") return rest.length === 2;
1014
- return rest.length === 1;
1015
- },
1016
- async fetch(id, _meta, context) {
1017
- const [type, ...rest] = id.split("/");
1018
- let data = { title: "", text: "" };
1019
- if (type === "posts") {
1020
- const [cate, slug] = rest;
1021
- data = await fetchJsonWithContext(
1022
- `posts/${cate}/${slug}`,
1023
- context,
1024
- "mx-space"
1025
- );
1026
- } else if (type === "notes") {
1027
- const [nid] = rest;
1028
- const response = await fetchJsonWithContext(
1029
- `notes/${nid}`,
1030
- context,
1031
- "mx-space"
1032
- );
1033
- data = response.data || response;
1034
- }
1035
- const coverImage = data.cover || data.meta?.cover;
1036
- const spotlightColor = generateColor(data.title);
1037
- return {
1038
- title: data.title,
1039
- desc: data.summary || `${stripMarkdown(data.text).slice(0, 50)}...`,
1040
- image: coverImage || data.images?.[0]?.src,
1041
- color: spotlightColor
1042
- };
1043
- }
1044
- };
1045
- const plugins = [
1046
- githubRepoPlugin,
1047
- githubCommitPlugin,
1048
- githubPrPlugin,
1049
- githubIssuePlugin,
1050
- githubDiscussionPlugin,
1051
- arxivPlugin,
1052
- tmdbPlugin,
1053
- bangumiPlugin,
1054
- qqMusicPlugin,
1055
- neteaseMusicPlugin,
1056
- leetcodePlugin,
1057
- mxSpacePlugin
1058
- ].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
1059
- function getPluginByName(name) {
1060
- return plugins.find((p) => p.name === name);
1061
- }
1062
- const pluginMap = new Map(
1063
- plugins.map((p) => [p.name, p])
1064
- );
1065
- function matchUrl(url, pluginRegistry = plugins) {
1066
- try {
1067
- const parsed = new URL(url);
1068
- for (const plugin of pluginRegistry) {
1069
- const match = plugin.matchUrl(parsed);
1070
- if (match) return { plugin, match };
1071
- }
1072
- } catch {
1073
- }
1074
- return null;
1075
- }
1076
- function useUrlMatcher(url, pluginRegistry = plugins) {
1077
- return useMemo(() => {
1078
- if (!url) return null;
1079
- return matchUrl(url, pluginRegistry);
1080
- }, [url, pluginRegistry]);
1081
- }
1082
- var semanticClassNames = { card: "link-card", cardShortDesc: "link-card--short-desc", cardSkeleton: "link-card--skeleton", cardError: "link-card--error", bg: "link-card__bg", image: "link-card__image", icon: "link-card__icon", content: "link-card__content", title: "link-card__title", titleText: "link-card__title-text", desc: "link-card__desc", desc2: "link-card__desc-2", editWrapper: "rich-link-card-edit-wrapper", editPanel: "rich-link-card-edit-panel", editUrlRow: "rich-link-card-edit-url-row", editLinkIcon: "rich-link-card-edit-link-icon", editInput: "rich-link-card-edit-input", editActions: "rich-link-card-edit-actions", editActionButton: "rich-link-card-edit-action-btn", editActionButtonEnd: "rich-link-card-edit-action-btn--end" };
1083
- var card = "_13weebv0";
1084
- var cardShortDesc = "_13weebv1";
1085
- var bg = "_13weebv2";
1086
- var image = "_13weebv3";
1087
- var icon = "_13weebv5";
1088
- var content = "_13weebv6";
1089
- var title = "_13weebv7";
1090
- var titleText = "_13weebv8";
1091
- var desc = "_13weebva";
1092
- var desc2 = "_13weebvb";
1093
- var cardSkeleton = "_13weebvj";
1094
- var cardError = "_13weebvl";
1095
- var editWrapper = "_13weebvm";
1096
- var editPanel = "_13weebvn";
1097
- var editUrlRow = "_13weebvo";
1098
- var editLinkIcon = "_13weebvp";
1099
- var editInput = "_13weebvr";
1100
- var editActions = "_13weebvs";
1101
- var editActionButton = "_13weebvt";
1102
- var editActionButtonEnd = "_13weebvu";
1103
- var typeCardModifier = { media: "_13weebvg", github: "_13weebvh", academic: "_13weebvi", wide: "_13weebve", full: "_13weebvf" };
1104
- var semanticTypeClassNames = { media: "link-card--media", github: "link-card--github", academic: "link-card--academic", wide: "link-card--wide", full: "link-card--full" };
1105
- var semanticClassToStyle = { "link-card--short-desc": "_13weebv1", "link-card--skeleton": "_13weebvj", "link-card--error": "_13weebvl", "link-card--reversed": "_13weebvd", "link-card--wide": "_13weebve", "link-card--full": "_13weebvf", "link-card--media": "_13weebvg", "link-card--github": "_13weebvh", "link-card--academic": "_13weebvi", "link-card__image--poster": "_13weebv4" };
1106
14
  function LinkCardEditDecorator({
1107
15
  nodeKey,
1108
16
  payload,
@@ -1142,9 +50,9 @@ function LinkCardEditDecorator({
1142
50
  const node = $getNodeByKey(nodeKey);
1143
51
  if (!$isLinkCardNode(node)) return;
1144
52
  const nodeUrl = node.getUrl();
1145
- const { title: title2 } = payload;
53
+ const { title } = payload;
1146
54
  const linkNode = $createLinkNode(nodeUrl);
1147
- linkNode.append($createTextNode(title2 || nodeUrl));
55
+ linkNode.append($createTextNode(title || nodeUrl));
1148
56
  const paragraph = $createParagraphNode();
1149
57
  paragraph.append(linkNode);
1150
58
  node.replace(paragraph);
@@ -1153,12 +61,12 @@ function LinkCardEditDecorator({
1153
61
  setOpen(false);
1154
62
  }, [editor, nodeKey, payload]);
1155
63
  const handleKeyDown = useCallback(
1156
- (e) => {
1157
- if (e.key === "Enter") {
1158
- e.preventDefault();
64
+ (e2) => {
65
+ if (e2.key === "Enter") {
66
+ e2.preventDefault();
1159
67
  commitUrl();
1160
- } else if (e.key === "Escape") {
1161
- e.preventDefault();
68
+ } else if (e2.key === "Escape") {
69
+ e2.preventDefault();
1162
70
  setUrl(payload.url);
1163
71
  }
1164
72
  },
@@ -1223,7 +131,7 @@ function LinkCardEditDecorator({
1223
131
  className: `${editInput} ${semanticClassNames.editInput}`,
1224
132
  type: "url",
1225
133
  value: url,
1226
- onChange: (e) => setUrl(e.target.value),
134
+ onChange: (e2) => setUrl(e2.target.value),
1227
135
  onBlur: commitUrl,
1228
136
  onKeyDown: handleKeyDown,
1229
137
  placeholder: "https://..."
@@ -1315,7 +223,7 @@ const _LinkCardEditNode = class _LinkCardEditNode extends LinkCardNode {
1315
223
  };
1316
224
  const rendererEl = createRendererDecoration(
1317
225
  "LinkCard",
1318
- LinkCardRenderer$1,
226
+ LinkCardRenderer,
1319
227
  payload
1320
228
  );
1321
229
  return createElement(LinkCardEditDecorator, {
@@ -1374,269 +282,6 @@ function ConvertToLinkCardAction({
1374
282
  "To Card"
1375
283
  ] });
1376
284
  }
1377
- const ctx = createContext(void 0);
1378
- const LinkCardFetchProvider = ctx.Provider;
1379
- function useLinkCardFetchContext() {
1380
- return use(ctx);
1381
- }
1382
- function useCardFetcher(options) {
1383
- const { source, plugin, id, fallbackUrl, enabled = true, context } = options;
1384
- const [loading, setLoading] = useState(true);
1385
- const [isError, setIsError] = useState(false);
1386
- const [fullUrl] = useState(fallbackUrl || "javascript:;");
1387
- const [cardInfo, setCardInfo] = useState();
1388
- const isValid = useMemo(() => {
1389
- if (!enabled || !plugin) return false;
1390
- return plugin.isValidId(id);
1391
- }, [plugin, enabled, id]);
1392
- const fetchInfo = useCallback(async () => {
1393
- if (!plugin || !isValid) return;
1394
- setLoading(true);
1395
- setIsError(false);
1396
- try {
1397
- const data = await plugin.fetch(id, void 0, context);
1398
- setCardInfo(data);
1399
- } catch (err) {
1400
- console.error(
1401
- `[LinkCard] Error fetching ${source || plugin.name} data:`,
1402
- err
1403
- );
1404
- setIsError(true);
1405
- } finally {
1406
- setLoading(false);
1407
- }
1408
- }, [context, plugin, isValid, id, source]);
1409
- const { ref } = useInView({
1410
- triggerOnce: true,
1411
- onChange(inView) {
1412
- if (!inView || !enabled) return;
1413
- fetchInfo();
1414
- }
1415
- });
1416
- return {
1417
- loading,
1418
- isError,
1419
- cardInfo,
1420
- fullUrl,
1421
- isValid,
1422
- ref
1423
- };
1424
- }
1425
- function LinkCardSkeleton({
1426
- source,
1427
- className
1428
- }) {
1429
- const plugin = source ? pluginMap.get(source) : void 0;
1430
- const typeClass = plugin?.typeClass;
1431
- const typeStyleClass = typeClass ? typeCardModifier[typeClass] : "";
1432
- const typeSemanticClass = typeClass ? semanticTypeClassNames[typeClass] : "";
1433
- return /* @__PURE__ */ jsxs(
1434
- "span",
1435
- {
1436
- "data-hide-print": true,
1437
- "data-source": source || void 0,
1438
- className: [
1439
- card,
1440
- semanticClassNames.card,
1441
- cardSkeleton,
1442
- semanticClassNames.cardSkeleton,
1443
- typeStyleClass,
1444
- typeSemanticClass,
1445
- className
1446
- ].filter(Boolean).join(" "),
1447
- children: [
1448
- /* @__PURE__ */ jsx("span", { className: `${image} ${semanticClassNames.image}` }),
1449
- /* @__PURE__ */ jsxs(
1450
- "span",
1451
- {
1452
- className: `${content} ${semanticClassNames.content}`,
1453
- children: [
1454
- /* @__PURE__ */ jsx("span", { className: `${title} ${semanticClassNames.title}`, children: /* @__PURE__ */ jsx(
1455
- "span",
1456
- {
1457
- className: `${titleText} ${semanticClassNames.titleText}`
1458
- }
1459
- ) }),
1460
- /* @__PURE__ */ jsx("span", { className: `${desc} ${semanticClassNames.desc}` }),
1461
- /* @__PURE__ */ jsx(
1462
- "span",
1463
- {
1464
- className: `${desc} ${semanticClassNames.desc} ${desc2} ${semanticClassNames.desc2}`
1465
- }
1466
- )
1467
- ]
1468
- }
1469
- )
1470
- ]
1471
- }
1472
- );
1473
- }
1474
- function FallbackIcon({ favicon }) {
1475
- const [faviconFailed, setFaviconFailed] = useState(false);
1476
- return /* @__PURE__ */ jsx("span", { className: `${icon} ${semanticClassNames.icon}`, children: favicon && !faviconFailed ? /* @__PURE__ */ jsx("img", { src: favicon, alt: "", onError: () => setFaviconFailed(true) }) : /* @__PURE__ */ jsx(Globe, { "aria-hidden": "true" }) });
1477
- }
1478
- function mapSemanticClasses(classNames) {
1479
- if (!classNames) return "";
1480
- return classNames.split(/\s+/).filter(Boolean).map(
1481
- (cls) => semanticClassToStyle[cls] ? `${semanticClassToStyle[cls]} ${cls}` : cls
1482
- ).join(" ");
1483
- }
1484
- const LinkCardRenderer = (props) => {
1485
- const {
1486
- url,
1487
- title: title$1,
1488
- description,
1489
- favicon,
1490
- image: image$1,
1491
- source: explicitSource,
1492
- id: explicitId,
1493
- className,
1494
- plugins: plugins$1 = plugins,
1495
- fetchContext: fetchContextProp
1496
- } = props;
1497
- const contextValue = useLinkCardFetchContext();
1498
- const fetchContext = fetchContextProp ?? contextValue;
1499
- const pluginMap2 = useMemo(
1500
- () => new Map(plugins$1.map((plugin) => [plugin.name, plugin])),
1501
- [plugins$1]
1502
- );
1503
- const urlMatch = useUrlMatcher(
1504
- !explicitSource || !explicitId ? url : void 0,
1505
- plugins$1
1506
- );
1507
- const source = explicitSource || urlMatch?.plugin.name;
1508
- const id = explicitId || urlMatch?.match.id;
1509
- const matchedFullUrl = urlMatch?.match.fullUrl;
1510
- const useDynamicFetch = !!source && !!id;
1511
- const selectedPlugin = source ? pluginMap2.get(source) : void 0;
1512
- const { loading, isError, cardInfo, fullUrl, isValid, ref } = useCardFetcher({
1513
- source,
1514
- plugin: selectedPlugin,
1515
- id: id || "",
1516
- fallbackUrl: matchedFullUrl || url,
1517
- enabled: useDynamicFetch,
1518
- context: fetchContext
1519
- });
1520
- const typeClass = selectedPlugin?.typeClass;
1521
- const typeStyleClass = typeClass ? typeCardModifier[typeClass] : "";
1522
- const typeSemanticClass = typeClass ? semanticTypeClassNames[typeClass] : "";
1523
- const finalTitle = cardInfo?.title || title$1 || url;
1524
- const finalDesc = cardInfo?.desc || description;
1525
- const finalImage = cardInfo?.image || image$1;
1526
- const finalColor = cardInfo?.color;
1527
- const classNames = cardInfo?.classNames || {};
1528
- const mappedCardRootClass = mapSemanticClasses(classNames.cardRoot);
1529
- const mappedImageClass = mapSemanticClasses(classNames.image);
1530
- const [shortDesc, setShortDesc] = useState(false);
1531
- const descRef = useRef(null);
1532
- useEffect(() => {
1533
- const el = descRef.current;
1534
- if (!el || !finalDesc) {
1535
- setShortDesc(false);
1536
- return;
1537
- }
1538
- const style = getComputedStyle(el);
1539
- const lineHeight = Number.parseFloat(style.lineHeight) || 1.5 * 14;
1540
- const maxTwoLines = lineHeight * 2;
1541
- setShortDesc(el.scrollHeight <= maxTwoLines + 1);
1542
- }, [finalDesc, finalTitle]);
1543
- if (useDynamicFetch && !isValid) {
1544
- return null;
1545
- }
1546
- if (useDynamicFetch && loading) {
1547
- return /* @__PURE__ */ jsx(
1548
- "a",
1549
- {
1550
- "data-hide-print": true,
1551
- ref,
1552
- href: fullUrl,
1553
- target: "_blank",
1554
- rel: "noopener noreferrer",
1555
- children: /* @__PURE__ */ jsx(LinkCardSkeleton, { source })
1556
- }
1557
- );
1558
- }
1559
- const hasImage = !!finalImage;
1560
- const showImagePlaceholder = useDynamicFetch && isError && !hasImage;
1561
- return /* @__PURE__ */ jsxs(
1562
- "a",
1563
- {
1564
- "data-hide-print": true,
1565
- ref: useDynamicFetch ? ref : void 0,
1566
- className: [
1567
- card,
1568
- semanticClassNames.card,
1569
- typeStyleClass,
1570
- typeSemanticClass,
1571
- shortDesc && cardShortDesc,
1572
- shortDesc && semanticClassNames.cardShortDesc,
1573
- useDynamicFetch && (loading || isError) && cardSkeleton,
1574
- useDynamicFetch && (loading || isError) && semanticClassNames.cardSkeleton,
1575
- useDynamicFetch && isError && cardError,
1576
- useDynamicFetch && isError && semanticClassNames.cardError,
1577
- "not-prose",
1578
- className,
1579
- mappedCardRootClass
1580
- ].filter(Boolean).join(" "),
1581
- "data-source": source || void 0,
1582
- href: useDynamicFetch ? fullUrl : url,
1583
- target: "_blank",
1584
- rel: "noopener noreferrer",
1585
- style: {
1586
- borderColor: finalColor ? `${finalColor}30` : void 0
1587
- },
1588
- children: [
1589
- finalColor && /* @__PURE__ */ jsx(
1590
- "div",
1591
- {
1592
- className: `${bg} ${semanticClassNames.bg}`,
1593
- style: {
1594
- backgroundColor: finalColor,
1595
- opacity: 0.04
1596
- }
1597
- }
1598
- ),
1599
- hasImage || showImagePlaceholder ? /* @__PURE__ */ jsx(
1600
- "span",
1601
- {
1602
- className: [
1603
- image,
1604
- semanticClassNames.image,
1605
- mappedImageClass
1606
- ].filter(Boolean).join(" "),
1607
- "data-image": finalImage || "",
1608
- style: {
1609
- backgroundImage: finalImage ? `url(${finalImage})` : void 0
1610
- }
1611
- }
1612
- ) : /* @__PURE__ */ jsx(FallbackIcon, { favicon }),
1613
- /* @__PURE__ */ jsxs(
1614
- "span",
1615
- {
1616
- className: `${content} ${semanticClassNames.content}`,
1617
- children: [
1618
- /* @__PURE__ */ jsx("span", { className: `${title} ${semanticClassNames.title}`, children: /* @__PURE__ */ jsx(
1619
- "span",
1620
- {
1621
- className: `${titleText} ${semanticClassNames.titleText}`,
1622
- children: finalTitle
1623
- }
1624
- ) }),
1625
- finalDesc && /* @__PURE__ */ jsx(
1626
- "span",
1627
- {
1628
- ref: descRef,
1629
- className: `${desc} ${semanticClassNames.desc}`,
1630
- children: finalDesc
1631
- }
1632
- )
1633
- ]
1634
- }
1635
- )
1636
- ]
1637
- }
1638
- );
1639
- };
1640
285
  function PasteLinkCardPlugin({
1641
286
  pluginRegistry
1642
287
  }) {
@@ -1671,35 +316,35 @@ function PasteLinkCardPlugin({
1671
316
  export {
1672
317
  $createLinkCardEditNode,
1673
318
  ConvertToLinkCardAction,
1674
- LanguageToColorMap,
319
+ L as LanguageToColorMap,
1675
320
  LinkCardEditDecorator,
1676
321
  LinkCardEditNode,
1677
- LinkCardFetchProvider,
1678
- LinkCardRenderer,
1679
- LinkCardSkeleton,
322
+ a as LinkCardFetchProvider,
323
+ b as LinkCardRenderer,
324
+ c as LinkCardSkeleton,
1680
325
  PasteLinkCardPlugin,
1681
- arxivPlugin,
1682
- bangumiPlugin,
1683
- camelcaseKeys,
1684
- createMxSpacePlugin,
1685
- fetchGitHubApi,
1686
- fetchJsonWithContext,
1687
- generateColor,
1688
- getPluginByName,
1689
- githubCommitPlugin,
1690
- githubDiscussionPlugin,
1691
- githubIssuePlugin,
1692
- githubPrPlugin,
1693
- githubRepoPlugin,
1694
- leetcodePlugin,
326
+ d as arxivPlugin,
327
+ e as bangumiPlugin,
328
+ f as camelcaseKeys,
329
+ g as createMxSpacePlugin,
330
+ h as fetchGitHubApi,
331
+ i as fetchJsonWithContext,
332
+ j as generateColor,
333
+ k as getPluginByName,
334
+ l as githubCommitPlugin,
335
+ m as githubDiscussionPlugin,
336
+ n as githubIssuePlugin,
337
+ o as githubPrPlugin,
338
+ p as githubRepoPlugin,
339
+ q as leetcodePlugin,
1695
340
  linkCardEditNodes,
1696
341
  matchUrl,
1697
- mxSpacePlugin,
1698
- neteaseMusicPlugin,
1699
- pluginMap,
1700
- plugins,
1701
- qqMusicPlugin,
1702
- tmdbPlugin,
1703
- useLinkCardFetchContext,
1704
- useUrlMatcher
342
+ s as mxSpacePlugin,
343
+ t as neteaseMusicPlugin,
344
+ u as pluginMap,
345
+ v as plugins,
346
+ w as qqMusicPlugin,
347
+ x as tmdbPlugin,
348
+ y as useLinkCardFetchContext,
349
+ z as useUrlMatcher
1705
350
  };