feat: 🎸 support carousel in md

This commit is contained in:
zhangqingwu
2024-09-12 21:24:16 +08:00
committed by Lyric Wai
parent 4a4296032e
commit 06ccb634eb
9 changed files with 185 additions and 69 deletions

View File

@@ -5,6 +5,7 @@ import sup from "markdown-it-sup";
import sub from "markdown-it-sub";
import mark from "markdown-it-mark";
import ins from "markdown-it-ins";
import carousel from "./lib/markdown-it-carousel";
// import markdownit from 'markdown-it'
@@ -94,7 +95,8 @@ export default withMermaid(
link: "/sounds-of-american-english/0-intro",
collapsed: true,
items: [
{ text: "1. 基础",
{
text: "1. 基础",
link: "/sounds-of-american-english/1-basics",
items: [
{
@@ -104,11 +106,11 @@ export default withMermaid(
{
text: "1.2. 英文字母",
link: "/sounds-of-american-english/1.2-alphabets",
},
]
},
],
},
{
text: "2. 发声器官",
text: "2. 发声器官",
link: "/sounds-of-american-english/2-articulators",
},
{
@@ -149,7 +151,7 @@ export default withMermaid(
text: "3.1.7. aɪ... oʊ",
link: "/sounds-of-american-english/3.1.7-aɪ",
},
]
],
},
{
text: "3.2. 辅音",
@@ -207,7 +209,7 @@ export default withMermaid(
{
text: "3.2.13. w, j",
link: "/sounds-of-american-english/3.2.13-wj",
},
},
{
text: "3.2.14. h",
link: "/sounds-of-american-english/3.2.14-h",
@@ -272,7 +274,7 @@ export default withMermaid(
text: "6.4. 常见词根词缀",
link: "/sounds-of-american-english/6.4-parts-of-words",
},
]
],
},
{
text: "7. 从此之后",
@@ -423,8 +425,8 @@ export default withMermaid(
},
],
},
]
}
],
},
],
"/enjoy-app/": [
@@ -482,7 +484,6 @@ export default withMermaid(
link: "/intro",
},
],
},
socialLinks: [
@@ -494,7 +495,7 @@ export default withMermaid(
},
sitemap: {
hostname: 'https://1000h.org'
hostname: "https://1000h.org",
},
lastUpdated: true,
@@ -509,10 +510,11 @@ export default withMermaid(
md.use(sup);
md.use(mark);
md.use(ins);
md.use(carousel);
},
toc: {
level: [1, 2, 3]
}
level: [1, 2, 3],
},
},
})
);

View File

@@ -0,0 +1,46 @@
import type MarkdownIt from "markdown-it/lib/index.mjs";
import * as cheerio from "cheerio";
export default function carouselPlugin(md: MarkdownIt) {
const html_block = md.renderer.rules.html_block!;
md.renderer.rules.html_block = (...args) => {
const [tokens, idx] = args;
const token = tokens[idx];
try {
if (token.content.match(/class=["']carousel["']/)) {
const $ = cheerio.load(token.content, null, false);
const carousel = $(".carousel");
if (carousel) {
const imgs = Array.from($(".carousel img"))
.map((item) => {
const src = $(item).attr("src");
return `<swiper-slide><img src="${src}" /></swiper-slide>`;
})
.join("");
const template = `
<div class="carousel">
<swiper-container class="carousel-inner" thumbs-swiper=".swiper-thumb" space-between="10" navigation="true" pagination="true">
${imgs}
</swiper-container>
<swiper-container class="swiper-thumb" slides-per-view="4" free-mode="true" space-between="10">
${imgs}
</swiper-container>
</div>
`;
carousel.replaceWith(template);
return $.html();
}
}
} catch (error) {
console.log("convert carousel element error");
}
return html_block(...args);
};
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="speak-word">
<div class="word" >
<div class="word">
{{ props.word }}
</div>
<div v-if="pos" class="pos">
@@ -8,19 +8,32 @@
</div>
<div class="spacer"></div>
<div class="ctrl">
<div class="ctrl-part" :class="item.label" v-for="item, ix in audios" :key="`audio-${ix}`">
<div
class="ctrl-part"
:class="item.label"
v-for="(item, ix) in audios"
:key="`audio-${ix}`"
>
<div v-if="ix !== 0" class="divider"></div>
<button class="play-button" :class="item.label" @click="playAudio(item.label)">
<button
class="play-button"
:class="item.label"
@click="playAudio(item.label)"
>
<span class="accent-label">{{ item.label }}</span>
<img :src="svgUrl(item.label)" class="icon" alt="sound" />
</button>
<audio class="audio" :class="item.label" :src="item.audio" controls="false" ></audio>
<audio
class="audio"
:class="item.label"
:src="item.audio"
controls="false"
></audio>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { getAudioPath } from "../data";
@@ -44,56 +57,55 @@ const props = defineProps({
});
const svgUrl = (accent) => {
if (accent === 'uk') {
return '/images/speaker-brown.svg';
} else if (accent === 'us') {
return '/images/speaker-blue.svg';
if (accent === "uk") {
return "/images/speaker-brown.svg";
} else if (accent === "us") {
return "/images/speaker-blue.svg";
}
return '/images/speaker-black.svg';
}
return "/images/speaker-black.svg";
};
const audioPathUS = computed(() => {
if (props.audioUs) {
return props.audioUs;
}
return getAudioPath(props.word, "us")
return getAudioPath(props.word, "us");
});
const audioPathUK = computed(() => {
if (props.audioUk) {
return props.audioUk;
}
return getAudioPath(props.word, "uk")
return getAudioPath(props.word, "uk");
});
const audioPathOther = computed(() => {
if (props.audioUk) {
return props.audioUk;
}
return getAudioPath(props.word, "other")
return getAudioPath(props.word, "other");
});
const audios = computed(() => {
const ret:any = [];
const ret: any = [];
if (audioPathUS.value) {
ret.push({ label: 'us', audio: audioPathUS.value});
ret.push({ label: "us", audio: audioPathUS.value });
}
if (audioPathUK.value) {
ret.push({ label: 'uk', audio: audioPathUK.value});
ret.push({ label: "uk", audio: audioPathUK.value });
}
if (audioPathOther.value) {
ret.push({ label: 'other', audio: audioPathOther.value});
ret.push({ label: "other", audio: audioPathOther.value });
}
return ret;
});
function playAudio(accent) {
const audioEl:any = document.querySelector(`audio.${accent}`);
const audioEl: any = document.querySelector(`audio.${accent}`);
audioEl.play();
}
</script>
<style lang="scss" scoped>
@import url(./SpeakWord.scss);
</style>

View File

@@ -1,10 +1,10 @@
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import SpeakWord from './components/SpeakWord.vue'
import MyLayout from './layouts/index.vue'
import './style.scss'
import { h } from "vue";
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
import SpeakWord from "./components/SpeakWord.vue";
import MyLayout from "./layouts/index.vue";
import "./style.scss";
export default {
extends: DefaultTheme,
@@ -16,6 +16,6 @@ export default {
// },
enhanceApp({ app, router, siteData }) {
// ...
app.component('SpeakWord', SpeakWord)
}
} satisfies Theme
app.component("SpeakWord", SpeakWord);
},
} satisfies Theme;

View File

@@ -1,10 +1,12 @@
<script setup>
import DefaultTheme from 'vitepress/theme'
import SpeakWordInlineConverter from '../components/SpeakWordInlineConverter.vue'
import ThemedImageSwitch from '../components/ThemedImageSwitch.vue'
import DefaultTheme from "vitepress/theme";
import SpeakWordInlineConverter from "../components/SpeakWordInlineConverter.vue";
import ThemedImageSwitch from "../components/ThemedImageSwitch.vue";
import { register } from "swiper/element/bundle";
const { Layout } = DefaultTheme
const { Layout } = DefaultTheme;
register();
</script>
<template>
@@ -18,12 +20,11 @@ const { Layout } = DefaultTheme
</Layout>
</template>
<style lang="scss" >
<style lang="scss">
@import url(../components/SpeakWord.scss);
.speak-word-wrapper {
display: inline-block;
margin: 0px;
vertical-align: middle;
}
</style>
</style>

View File

@@ -71,12 +71,14 @@
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
--vp-font-family-base: 'SF Pro SC','SF Pro Text','SF Pro Icons','PingFang SC','Helvetica Neue', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Helvetica, Arial, sans-serif;
--vp-font-family-mono: 'Sauce Code Pro', 'Source Code Pro', 'Courier New', monospace;
--vp-font-family-base: "SF Pro SC", "SF Pro Text", "SF Pro Icons",
"PingFang SC", "Helvetica Neue", -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, Oxygen, Ubuntu, Helvetica, Arial, sans-serif;
--vp-font-family-mono: "Sauce Code Pro", "Source Code Pro", "Courier New",
monospace;
--vp-c-text-strong: rgb(63 86 99);
--vp-c-text-em: rgb(91, 4, 17);
}
.dark {
@@ -84,7 +86,6 @@
--vp-c-text-em: rgb(187, 121, 131);
}
body {
font-size: 16px;
}
@@ -156,15 +157,14 @@ body {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}
.vp-doc {
p img, p video {
p img,
p video {
margin: 0 auto;
align-self: center;
}
blockquote > p{
blockquote > p {
font-size: 16px;
}
@@ -172,12 +172,14 @@ body {
font-size: 16px;
}
strong, b {
strong,
b {
color: var(--vp-c-text-strong);
font-weight: bold;
}
em, i {
em,
i {
color: var(--vp-c-text-em);
}
@@ -190,7 +192,6 @@ body {
margin: auto 1px -4px;
}
sup {
vertical-align: top;
position: relative;
@@ -199,7 +200,8 @@ body {
// @TODO 回头撤销对 code 的字体设置,因为 CharisSIL 不是等宽
code {
font-family: "SauceCodePro Nerd Font Mono", "CharisSIL", "DejaVu Sans Mono", "Courier New", monospace;
font-family: "SauceCodePro Nerd Font Mono", "CharisSIL", "DejaVu Sans Mono",
"Courier New", monospace;
}
span.pho {
@@ -212,7 +214,8 @@ body {
// font-size: 100%;
color: var(--vp-code-color);
&.alt {
&::before, &::after {
&::before,
&::after {
content: "/";
display: inline-block;
width: 0.5em;
@@ -227,13 +230,52 @@ audio {
margin-top: -0.2em;
}
video, img {
video,
img {
width: 95%;
}
span.not-display {display: none;}
span.not-display {
display: none;
}
.two-column ol, .two-column ul {
.two-column ol,
.two-column ul {
column-count: 2;
column-gap: 2em;
}
}
.carousel {
swiper-container {
width: 100%;
height: 100%;
}
swiper-slide img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.carousel-inner {
swiper-slide {
align-self: center;
}
}
.swiper-thumb {
padding: 10px 0;
swiper-slide {
width: 25%;
height: 100%;
opacity: 0.4;
cursor: pointer;
}
.swiper-slide-thumb-active {
opacity: 1;
}
}
}

View File

@@ -10,7 +10,7 @@
“**1000 小时**” 看起来并不多,也的确不多。但,它有密度要求。每天 1 小时需要大约 3 年才能凑足 1000 小时;而每天 3 小时只需要 1 年就能攒足…… 首先,后者的效果比前者高出不知道多少倍,其次,前者实际上更难做到更难坚持。所以,做不到**每天至少 3 小时**,还不如不做。
“**就能**” 的意思是说没有例外。“用你的注意力填满 1000 小时” 是 “练成任何你所需要的技能” 的充分必要条件。若是充分必要条件被满足的话,结果就只能如实发生。
“**就能**” 的意思是说没有例外。“用你的注意力填满 1000 小时” 是 “练成任何你所需要的技能” 的充分必要条件。若是充分必要条件被满足的话,结果就只能如实发生。
“**练成**”,不是从书本上 “知道” 或者在哪里 “学到”,不是考试 “及格” 或者 “优秀”,甚至不只是 “做好”,而是 “**精通**” —— 从来少有人做到。
@@ -70,6 +70,6 @@ AI 凶猛。未来咆哮而至。关于人工智能对人类的威胁,以及
—— 吾往矣。
<span style="text-align: right;">**李笑来** *2024* 春节 于 北京</span>
<span style="text-align: right;">**李笑来** _2024_ 春节 于 北京</span>
[^*]: 基尼系数Gini coefficient是 20 世纪初意大利学者科拉多·基尼根据洛伦兹曲线所定义的判断年收入分配公平程度的指标,是比例数值,在 0 和 1 之间。基尼系数越小,年收入分配越平均;基尼系数越大,年收入分配越不平均。
[^*]: 基尼系数Gini coefficient是 20 世纪初意大利学者科拉多·基尼根据洛伦兹曲线所定义的判断年收入分配公平程度的指标,是比例数值,在 0 和 1 之间。基尼系数越小,年收入分配越平均;基尼系数越大,年收入分配越不平均。

View File

@@ -17,5 +17,9 @@
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"dependencies": {
"cheerio": "^1.0.0",
"swiper": "^11.1.12"
}
}

View File

@@ -9,6 +9,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "1000-hours@workspace:1000-hours"
dependencies:
cheerio: "npm:^1.0.0"
markdown-it-footnote: "npm:^4.0.0"
markdown-it-ins: "npm:^4.0.0"
markdown-it-mark: "npm:^4.0.0"
@@ -17,6 +18,7 @@ __metadata:
markdown-it-sup: "npm:^2.0.0"
mermaid: "npm:^11.2.0"
sass: "npm:^1.78.0"
swiper: "npm:^11.1.12"
vitepress: "npm:^1.3.4"
vitepress-plugin-mermaid: "npm:^2.0.16"
vue: "npm:^3.5.4"
@@ -23060,6 +23062,13 @@ __metadata:
languageName: node
linkType: hard
"swiper@npm:^11.1.12":
version: 11.1.12
resolution: "swiper@npm:11.1.12"
checksum: 10c0/0d8047dbbf79c35f3515d12c77dc5f82d911add0b7e4ca74a8d351cb572c3a327adc3b10ac10f11571205b8071037537edcb81763808a60e4b6d0a81efebc851
languageName: node
linkType: hard
"symbol-tree@npm:^3.2.4":
version: 3.2.4
resolution: "symbol-tree@npm:3.2.4"