@windcoder/astro-theme-orchid 0.4.4 → 0.4.5
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/package.json +1 -1
- package/src/components/PostResources.astro +75 -0
- package/src/i18n/en.ts +3 -0
- package/src/i18n/ja.ts +3 -0
- package/src/i18n/ko.ts +3 -0
- package/src/i18n/ru.ts +3 -0
- package/src/i18n/zh-CN.ts +3 -0
- package/src/i18n/zh-TW.ts +3 -0
- package/src/routes/posts/[...slug].astro +3 -0
- package/src/schema.ts +14 -0
- package/src/scripts/share-poster.ts +2 -0
- package/src/styles/global.css +92 -0
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* 文章资源区:frontmatter resources(name/url/password)在正文后渲染资源卡。
|
|
4
|
+
* 按 url 域名识别品牌图标(gitee/github),其余地址用通用外链图标;
|
|
5
|
+
* password 有值时展示提取码。新地址类型无需改组件,直接复用 name/url 展示。
|
|
6
|
+
*/
|
|
7
|
+
import { t } from "../i18n/index.js";
|
|
8
|
+
|
|
9
|
+
interface ResourceItem {
|
|
10
|
+
name: string;
|
|
11
|
+
url: string;
|
|
12
|
+
password?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
resources?: ResourceItem[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { resources } = Astro.props;
|
|
20
|
+
const items = (resources ?? []).filter((r) => r.url && r.url.trim() !== "");
|
|
21
|
+
|
|
22
|
+
type IconKind = "gitee" | "github" | "link";
|
|
23
|
+
function iconFor(url: string): IconKind {
|
|
24
|
+
let host = "";
|
|
25
|
+
try {
|
|
26
|
+
host = new URL(url).hostname.replace(/^www\./, "");
|
|
27
|
+
} catch {
|
|
28
|
+
return "link";
|
|
29
|
+
}
|
|
30
|
+
if (host === "gitee.com") return "gitee";
|
|
31
|
+
if (host === "github.com") return "github";
|
|
32
|
+
return "link";
|
|
33
|
+
}
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
items.length > 0 && (
|
|
38
|
+
<section class="post-resources card reveal" aria-label={t("postResources")}>
|
|
39
|
+
<h2 class="post-resources-title">
|
|
40
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
|
|
41
|
+
{t("postResources")}
|
|
42
|
+
</h2>
|
|
43
|
+
<ul class="post-resources-list">
|
|
44
|
+
{items.map((r) => {
|
|
45
|
+
const kind = iconFor(r.url);
|
|
46
|
+
return (
|
|
47
|
+
<li class="post-resources-item">
|
|
48
|
+
<a href={r.url} target="_blank" rel="noopener">
|
|
49
|
+
<span class="post-resources-icon" aria-hidden="true">
|
|
50
|
+
{kind === "gitee" && (
|
|
51
|
+
<svg viewBox="0 0 1024 1024" fill="currentColor"><path d="M512 1024C229.222 1024 0 794.778 0 512S229.222 0 512 0s512 229.222 512 512-229.222 512-512 512zm259.149-568.883h-290.74a25.293 25.293 0 0 0-25.292 25.293l-0.081 63.206c0 13.952 11.315 25.293 25.267 25.293h177.024c13.978 0 25.293 11.315 25.293 25.267v12.646a75.853 75.853 0 0 1-75.853 75.853h-240.23a25.293 25.293 0 0 1-25.267-25.293V417.203a75.853 75.853 0 0 1 75.827-75.853h353.946a25.293 25.293 0 0 0 25.267-25.292l0.077-63.207a25.293 25.293 0 0 0-25.268-25.293H417.152a189.62 189.62 0 0 0-189.62 189.645V771.15c0 13.977 11.316 25.293 25.294 25.293h372.94a170.65 170.65 0 0 0 170.65-170.65V480.384a25.293 25.293 0 0 0-25.267-25.267z"/></svg>
|
|
52
|
+
)}
|
|
53
|
+
{kind === "github" && (
|
|
54
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
55
|
+
)}
|
|
56
|
+
{kind === "link" && (
|
|
57
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
|
58
|
+
)}
|
|
59
|
+
</span>
|
|
60
|
+
<span class="post-resources-name">{r.name}</span>
|
|
61
|
+
{r.password && r.password.trim() !== "" && (
|
|
62
|
+
<span class="post-resources-password">
|
|
63
|
+
{t("resourcePassword")}
|
|
64
|
+
<code>{r.password}</code>
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
<svg class="post-resources-ext" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>
|
|
68
|
+
</a>
|
|
69
|
+
</li>
|
|
70
|
+
);
|
|
71
|
+
})}
|
|
72
|
+
</ul>
|
|
73
|
+
</section>
|
|
74
|
+
)
|
|
75
|
+
}
|
package/src/i18n/en.ts
CHANGED
|
@@ -146,6 +146,9 @@ const en: Record<UiKey, string> = {
|
|
|
146
146
|
"No series yet. Add a series field to a post's frontmatter to create one.",
|
|
147
147
|
seriesProgress: "{cur} / {total}",
|
|
148
148
|
seriesCatalog: "Series catalog",
|
|
149
|
+
/* Post resources: resource card rendered from frontmatter resources */
|
|
150
|
+
postResources: "Related resources",
|
|
151
|
+
resourcePassword: "Code",
|
|
149
152
|
/* Music player (§4.36) */
|
|
150
153
|
musicOpen: "Music player",
|
|
151
154
|
musicClose: "Collapse player",
|
package/src/i18n/ja.ts
CHANGED
|
@@ -150,6 +150,9 @@ const ja: Record<UiKey, string> = {
|
|
|
150
150
|
"シリーズがまだありません。記事の frontmatter に series を記入すると作成できます。",
|
|
151
151
|
seriesProgress: "{cur} / {total}",
|
|
152
152
|
seriesCatalog: "シリーズ目次",
|
|
153
|
+
/* 記事リソース:frontmatter resources から描画されるリソースカード */
|
|
154
|
+
postResources: "関連リソース",
|
|
155
|
+
resourcePassword: "パスワード",
|
|
153
156
|
/* 音楽プレイヤー */
|
|
154
157
|
musicOpen: "音楽プレイヤー",
|
|
155
158
|
musicClose: "プレイヤーを閉じる",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -149,6 +149,9 @@ const ko: Record<UiKey, string> = {
|
|
|
149
149
|
"아직 시리즈가 없습니다. 글 frontmatter에 series를 입력하면 생성됩니다.",
|
|
150
150
|
seriesProgress: "{cur} / {total}",
|
|
151
151
|
seriesCatalog: "시리즈 목차",
|
|
152
|
+
/* 게시글 리소스: frontmatter resources 로 렌더링되는 리소스 카드 */
|
|
153
|
+
postResources: "관련 자료",
|
|
154
|
+
resourcePassword: "추출 코드",
|
|
152
155
|
/* 음악 플레이어 */
|
|
153
156
|
musicOpen: "음악 플레이어",
|
|
154
157
|
musicClose: "플레이어 접기",
|
package/src/i18n/ru.ts
CHANGED
|
@@ -150,6 +150,9 @@ const ru: Record<UiKey, string> = {
|
|
|
150
150
|
"Серий пока нет. Заполните поле series в frontmatter записи, чтобы создать.",
|
|
151
151
|
seriesProgress: "{cur} / {total}",
|
|
152
152
|
seriesCatalog: "Содержание серии",
|
|
153
|
+
/* Ресурсы статьи: карточка из frontmatter resources */
|
|
154
|
+
postResources: "Материалы",
|
|
155
|
+
resourcePassword: "Код доступа",
|
|
153
156
|
/* Музыкальный плеер */
|
|
154
157
|
musicOpen: "Музыкальный плеер",
|
|
155
158
|
musicClose: "Свернуть плеер",
|
package/src/i18n/zh-CN.ts
CHANGED
|
@@ -166,6 +166,9 @@ const zhCN = {
|
|
|
166
166
|
seriesEmpty: "还没有专题,在文章 frontmatter 中填写 series 字段即可创建。",
|
|
167
167
|
seriesProgress: "{cur} / {total}",
|
|
168
168
|
seriesCatalog: "专题目录",
|
|
169
|
+
/* 文章资源区:frontmatter resources 渲染的资源卡 */
|
|
170
|
+
postResources: "相关资源",
|
|
171
|
+
resourcePassword: "提取码",
|
|
169
172
|
/* 音乐播放器(§4.36):data-labels 经组件 data-labels 属性带入客户端 */
|
|
170
173
|
musicOpen: "音乐播放器",
|
|
171
174
|
musicClose: "收起播放器",
|
package/src/i18n/zh-TW.ts
CHANGED
|
@@ -145,6 +145,9 @@ const zhTW: Record<UiKey, string> = {
|
|
|
145
145
|
seriesEmpty: "還沒有專題,在文章 frontmatter 中填寫 series 欄位即可建立。",
|
|
146
146
|
seriesProgress: "{cur} / {total}",
|
|
147
147
|
seriesCatalog: "專題目錄",
|
|
148
|
+
/* 文章資源區:frontmatter resources 渲染的資源卡 */
|
|
149
|
+
postResources: "相關資源",
|
|
150
|
+
resourcePassword: "提取碼",
|
|
148
151
|
/* 音樂播放器 */
|
|
149
152
|
musicOpen: "音樂播放器",
|
|
150
153
|
musicClose: "收起播放器",
|
|
@@ -10,6 +10,7 @@ import EncryptedContent from "virtual:theme/components/EncryptedContent.astro";
|
|
|
10
10
|
import License from "virtual:theme/components/License.astro";
|
|
11
11
|
import OutdatedCard from "virtual:theme/components/OutdatedCard.astro";
|
|
12
12
|
import PostNav from "virtual:theme/components/PostNav.astro";
|
|
13
|
+
import PostResources from "virtual:theme/components/PostResources.astro";
|
|
13
14
|
import RecommendedPost from "virtual:theme/components/RecommendedPost.astro";
|
|
14
15
|
import Sidebar from "virtual:theme/components/Sidebar.astro";
|
|
15
16
|
import Toc from "virtual:theme/components/Toc.astro";
|
|
@@ -303,6 +304,8 @@ const jsonLd = {
|
|
|
303
304
|
{/* 过时提醒(§4.11):许可卡之前展示;加密文章不渲染 */}
|
|
304
305
|
{!d.password && <OutdatedCard updated={d.updated ?? d.published} published={d.published} />}
|
|
305
306
|
<License licenseName={d.licenseName} licenseUrl={d.licenseUrl} enabled={d.license} />
|
|
307
|
+
{/* 文章资源区:frontmatter resources 非空时渲染;加密文章随正文语义一并隐藏 */}
|
|
308
|
+
{!d.password && <PostResources resources={d.resources} />}
|
|
306
309
|
<PostNav prev={prev ?? undefined} next={next ?? undefined} />
|
|
307
310
|
{/* 支持卡(P2):打赏入口 + 分享海报(share-poster.ts 仍按 #qy-share-btn 与 data-* 绑定) */}
|
|
308
311
|
{
|
package/src/schema.ts
CHANGED
|
@@ -1639,6 +1639,20 @@ export function definePostSchema(extensions: z.ZodRawShape = {}) {
|
|
|
1639
1639
|
* 组名直接来自本字段文本,无 i18n 参与。
|
|
1640
1640
|
*/
|
|
1641
1641
|
seriesGroup: z.string().optional(),
|
|
1642
|
+
/**
|
|
1643
|
+
* 文章相关资源:正文后渲染资源卡区。name 展示名、url 跳转地址、
|
|
1644
|
+
* password 提取码(可空)。按 url 域名识别品牌图标(gitee/github),
|
|
1645
|
+
* 其余地址类型直接复用通用外链图标展示,无需再改 schema。
|
|
1646
|
+
*/
|
|
1647
|
+
resources: z
|
|
1648
|
+
.array(
|
|
1649
|
+
z.object({
|
|
1650
|
+
name: z.string(),
|
|
1651
|
+
url: z.string(),
|
|
1652
|
+
password: z.string().optional(),
|
|
1653
|
+
}),
|
|
1654
|
+
)
|
|
1655
|
+
.optional(),
|
|
1642
1656
|
})
|
|
1643
1657
|
.extend(extensions);
|
|
1644
1658
|
}
|
|
@@ -451,6 +451,8 @@ export async function openSharePoster(btn: HTMLElement) {
|
|
|
451
451
|
img.src = poster;
|
|
452
452
|
img.hidden = false;
|
|
453
453
|
}
|
|
454
|
+
/* 生成完成:移除 loading(否则转圈常驻在海报上方) */
|
|
455
|
+
modal.querySelector<HTMLElement>(".poster-loading")?.remove();
|
|
454
456
|
if (saveBtn) {
|
|
455
457
|
saveBtn.disabled = false;
|
|
456
458
|
saveBtn.addEventListener("click", () => {
|
package/src/styles/global.css
CHANGED
|
@@ -3339,6 +3339,94 @@ html.dark .outdated-warning {
|
|
|
3339
3339
|
border-radius: var(--radius-sm);
|
|
3340
3340
|
background: var(--bg-soft);
|
|
3341
3341
|
}
|
|
3342
|
+
/* 文章资源区:frontmatter resources 渲染的资源卡(License 之后、上下篇之前) */
|
|
3343
|
+
.post-resources {
|
|
3344
|
+
margin-top: 28px;
|
|
3345
|
+
padding: 16px 20px;
|
|
3346
|
+
}
|
|
3347
|
+
.post-resources-title {
|
|
3348
|
+
display: flex;
|
|
3349
|
+
align-items: center;
|
|
3350
|
+
gap: 8px;
|
|
3351
|
+
margin: 0 0 10px;
|
|
3352
|
+
font-size: 15.5px;
|
|
3353
|
+
font-weight: 700;
|
|
3354
|
+
color: var(--ink);
|
|
3355
|
+
}
|
|
3356
|
+
.post-resources-title svg {
|
|
3357
|
+
width: 16px;
|
|
3358
|
+
height: 16px;
|
|
3359
|
+
color: var(--accent);
|
|
3360
|
+
}
|
|
3361
|
+
.post-resources-list {
|
|
3362
|
+
list-style: none;
|
|
3363
|
+
margin: 0;
|
|
3364
|
+
padding: 0;
|
|
3365
|
+
display: grid;
|
|
3366
|
+
gap: 4px;
|
|
3367
|
+
}
|
|
3368
|
+
.post-resources-item a {
|
|
3369
|
+
display: flex;
|
|
3370
|
+
align-items: center;
|
|
3371
|
+
gap: 10px;
|
|
3372
|
+
padding: 7px 8px;
|
|
3373
|
+
border-radius: var(--radius-sm);
|
|
3374
|
+
font-size: 14px;
|
|
3375
|
+
color: var(--ink-2);
|
|
3376
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
3377
|
+
}
|
|
3378
|
+
.post-resources-item a:hover {
|
|
3379
|
+
background: var(--bg-soft);
|
|
3380
|
+
color: var(--accent-deep);
|
|
3381
|
+
}
|
|
3382
|
+
.post-resources-icon {
|
|
3383
|
+
flex: none;
|
|
3384
|
+
display: inline-flex;
|
|
3385
|
+
width: 18px;
|
|
3386
|
+
height: 18px;
|
|
3387
|
+
color: var(--muted);
|
|
3388
|
+
}
|
|
3389
|
+
.post-resources-item a:hover .post-resources-icon {
|
|
3390
|
+
color: var(--accent);
|
|
3391
|
+
}
|
|
3392
|
+
.post-resources-icon svg {
|
|
3393
|
+
width: 100%;
|
|
3394
|
+
height: 100%;
|
|
3395
|
+
}
|
|
3396
|
+
.post-resources-name {
|
|
3397
|
+
min-width: 0;
|
|
3398
|
+
overflow: hidden;
|
|
3399
|
+
text-overflow: ellipsis;
|
|
3400
|
+
white-space: nowrap;
|
|
3401
|
+
}
|
|
3402
|
+
.post-resources-password {
|
|
3403
|
+
flex: none;
|
|
3404
|
+
display: inline-flex;
|
|
3405
|
+
align-items: center;
|
|
3406
|
+
gap: 4px;
|
|
3407
|
+
margin-left: auto;
|
|
3408
|
+
font-size: 12.5px;
|
|
3409
|
+
color: var(--muted);
|
|
3410
|
+
}
|
|
3411
|
+
.post-resources-password code {
|
|
3412
|
+
padding: 1px 6px;
|
|
3413
|
+
border-radius: 4px;
|
|
3414
|
+
border: 1px solid var(--border);
|
|
3415
|
+
background: var(--bg-soft);
|
|
3416
|
+
font-size: 12px;
|
|
3417
|
+
color: var(--ink-2);
|
|
3418
|
+
}
|
|
3419
|
+
.post-resources-ext {
|
|
3420
|
+
flex: none;
|
|
3421
|
+
width: 14px;
|
|
3422
|
+
height: 14px;
|
|
3423
|
+
color: var(--muted);
|
|
3424
|
+
opacity: 0.6;
|
|
3425
|
+
}
|
|
3426
|
+
.post-resources-item a:hover .post-resources-ext {
|
|
3427
|
+
color: var(--accent);
|
|
3428
|
+
opacity: 1;
|
|
3429
|
+
}
|
|
3342
3430
|
.post-nav {
|
|
3343
3431
|
margin-top: 24px;
|
|
3344
3432
|
display: grid;
|
|
@@ -5592,6 +5680,10 @@ a[data-encoded-email] {
|
|
|
5592
5680
|
height: auto;
|
|
5593
5681
|
border-radius: var(--radius-sm);
|
|
5594
5682
|
}
|
|
5683
|
+
/* display:block 会覆盖 [hidden] 的 UA 样式,显式声明防生成前空框露出 */
|
|
5684
|
+
.poster-img[hidden] {
|
|
5685
|
+
display: none;
|
|
5686
|
+
}
|
|
5595
5687
|
.poster-loading {
|
|
5596
5688
|
display: flex;
|
|
5597
5689
|
align-items: center;
|