move to vue-blogify

This commit is contained in:
patarapolw committed 2019-08-22 08:28:55 +07:00
1 parent 4fe597979c
commit bb1e397daa
90 files changed
+10347 -2666

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
> 1%
last 2 versions
+1
View File
@@ -0,0 +1 @@
public/posts/* linguist-vendored
+14 -118
View File
@@ -1,125 +1,21 @@
# Created by https://www.gitignore.io/api/node,macos
# Edit at https://www.gitignore.io/?templates=node,macos
### macOS ###
# General
.DS_Store .DS_Store
.AppleDouble node_modules
.LSOverride /dist
# Icon must end with two \r # local env files
Icon .env.local
.env.*.local
# Thumbnails # Log files
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) # Editor directories and files
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json .idea
.vscode
# Runtime data *.suo
pids *.ntvs*
*.pid *.njsproj
*.seed *.sln
*.pid.lock *.sw?
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# End of https://www.gitignore.io/api/node,macos
/dist/
-6
View File
@@ -1,6 +0,0 @@
[submodule "plugins/cogear-plugin-blog"]
path = plugins/cogear-plugin-blog
url = https://github.com/patarapolw/cogear-plugin-blog.git
[submodule "plugins/cogear-plugin-base-url"]
path = plugins/cogear-plugin-base-url
url = https://github.com/patarapolw/cogear-plugin-base-url.git
+1
View File
@@ -0,0 +1 @@
loglevel=error
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Pacharapol Withayasakpunt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+28 -1
View File
@@ -1 +1,28 @@
Clone using `git clone --recursive git@repo.org:your_repo.git`. # Vue Blogify
Vue CLI-based static blogging platform
## How to use
- Edit `config.yaml`
- Edit layouts (using `*.vue`) in `src/layouts/`
- Add posts in `public/posts`, preferrably in `public/posts/<YEAR>/<MONTH>`. HTML, Pug and Markdown are supported.
## Installation
- I use NPM, and Vue CLI 3, so `npm install`
- Run using `npm run dev`
## Building and deploying
- Build using `npm run build`
- Deploy as static website, maybe using [gh-pages](https://www.npmjs.com/package/gh-pages).
- To deploy to most static websites, set `publicPath` in `vue.config.js` to `/`.
- For non-user GitHub Pages, set `publicPath` to `/<REPO_NAME>`.
- For Cordova, Electron, or local, set `publicPath` to `""`.
- Note that, if `publicPath` is not `/`, [Vue router's History Mode](https://router.vuejs.org/guide/essentials/history-mode.html) might not work.
- For instructions, see <https://cli.vuejs.org/guide/deployment.html>
## Example
See <https://vue-blogify.patarapolw.now.sh>
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
"presets": [
[
"@vue/app",
{
"useBuiltIns": "entry"
}
]
]
}
+17 -22
View File
@@ -1,24 +1,19 @@
title: 中文 Diary title: 中文 Diary
description: tabs:
keywords: - name: 千字文
theme: blog to: /tag/千字文
baseUrl: / - name: 句子
blog: to: /tag/句子
index: "" # Provide posts to index page - name: 分类词汇
regex: "^p/" to: /tag/分类词汇
tagUri: 'tag' posts:
perPage: 5 perPage: 5
split: === author:
title: "中文 Diary" login: polvcode
tags: avatar: 'https://en.gravatar.com/userimage/35152960/7140205ee2a71cbe54d3f2ac43c0370d.jpg?size=24'
- 千字文 link: https://polvcode.dev
- 句子 about: true
- 分类词汇 external:
pages: disqus: zhdiary
^/?p/: google:
layout: post analytics: UA-146246917-1
author:
login: polvcode
avatar: 'https://en.gravatar.com/userimage/35152960/7140205ee2a71cbe54d3f2ac43c0370d.jpg?size=24'
link: https://polvcode.dev
output: dist/
-21
View File
@@ -1,21 +0,0 @@
const ghPages = require("gh-pages");
const {spawnSync} = require("child_process");
const rimraf = require("rimraf");
rimraf.sync("node_modules/gh-pages/.cache")
spawnSync("cogear", ["build"], {
stdio: "inherit"
});
ghPages.publish("dist", {
branch: "master",
repo: "https://git.heroku.com/zhdiary.git"
}, (err) => {
if (err) {
console.error(err);
return;
}
console.log("Posted to https://zhdiary.herokuapp.com")
});
+9068 -1328
View File
File diff suppressed because it is too large. Load diff
+49 -34
View File
@@ -1,41 +1,56 @@
{ {
"name": "zhdiary", "name": "vue-blogify",
"version": "1.0.0", "version": "0.1.0",
"description": "My journey through Chinese language", "private": true,
"main": "server.js", "types": "src/declaration.d.ts",
"scripts": {
"dev": "vue-cli-service serve:async",
"build": "vue-cli-service build:async",
"test": "ts-mocha -p tsconfig.test.json src/**/*.spec.ts",
"deploy": "npm run build && gh-pages -d dist"
},
"dependencies": { "dependencies": {
"@types/jquery": "^3.3.31" "@types/uuid": "^3.4.5",
"bootstrap-vue": "^2.0.0-rc.19",
"core-js": "^2.6.5",
"esm": "^3.2.25",
"fast-glob": "^3.0.4",
"gray-matter": "^4.0.2",
"hyperpug": "^0.1.7",
"hyperscript": "^2.0.2",
"js-yaml": "^3.13.1",
"moment": "^2.24.0",
"showdown": "^1.9.0",
"uuid": "^3.3.3",
"vue": "^2.6.10",
"vue-class-component": "^7.0.2",
"vue-disqus": "^3.0.5",
"vue-property-decorator": "^8.1.0",
"vue-router": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {
"gh-pages": "^2.1.1", "@babel/polyfill": "^7.4.4",
"husky": "^3.0.3", "@types/expect": "^1.20.4",
"hyperscript": "^2.0.2", "@types/mocha": "^5.2.7",
"jstransformer-markdown-it": "^2.1.0", "@vue/cli-plugin-babel": "^3.10.0",
"p-iteration": "^1.1.8", "@vue/cli-plugin-typescript": "^3.10.0",
"pug": "^2.0.4", "@vue/cli-service": "^3.10.0",
"showdown": "^1.9.0", "bootstrap": "^4.3.1",
"webpack": "^4.39.1", "mocha": "^6.2.0",
"webpack-merge": "^4.2.1" "mutationobserver-shim": "^0.3.3",
"node-sass": "^4.12.0",
"popper.js": "^1.15.0",
"portal-vue": "^2.1.4",
"sass-loader": "^7.1.0",
"ts-mocha": "^6.0.0",
"typescript": "^3.4.3",
"vue-cli-plugin-bootstrap-vue": "^0.4.0",
"vue-cli-plugin-pug": "^1.0.7",
"vue-template-compiler": "^2.6.10"
}, },
"scripts": { "vuePlugins": {
"start": "cogear", "service": [
"build": "cogear build", "vue.async.config.js"
"deploy": "node deploy.js" ]
},
"author": {
"name": "Pacharapol Withayasakpunt",
"email": "patarapolw@gmail.com",
"url": "https://polvcode.dev"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/patarapolw/zhdiary.git"
},
"homepage": "https://zhdiary.herokuapp.com",
"husky": {},
"engines": {
"node": "10.16.0",
"npm": "6.10.0"
} }
} }
-8
View File
@@ -1,8 +0,0 @@
{
"name": "custom-render",
"description": "Cogear.JS custom Webpack plugin.",
"version": "1.0.0",
"author": "Pacharapol Withayasakpunt<patarapolw@gmail.com>",
"homepage": "https://github.com/patarapolw/custom-webpack",
"main": "./plugin.js"
}
-7
View File
@@ -1,7 +0,0 @@
const { Markdown } = require("./plugin");
const md = new Markdown();
console.log(md.md2html(`
a | b | c
d | e | f
`));
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}
File renamed without changes.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 440 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 912 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File
File renamed without changes.
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="<%= BASE_URL %>apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="<%= BASE_URL %>favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="<%= BASE_URL %>favicon-16x16.png">
<link rel="manifest" href="<%= BASE_URL %>site.webmanifest">
<link rel="stylesheet" href="<%= BASE_URL %>index.css">
<title>中文 Diary - Record my journey to Chinese language</title>
</head>
<body>
<noscript>
<strong>We're sorry but vue-blogify doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<!-- built files will be auto injected -->
</body>
</html>
@@ -1,7 +1,7 @@
--- ---
title: "千字文" title: "千字文"
date: "2019-08-10" date: "2019-08-10"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/1" title: "千字文 - 1/1"
date: "2019-08-11" date: "2019-08-11"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 天地玄黃,宇宙洪荒。 .w-100.mt-3
h3.text-center 天地玄黃,宇宙洪荒。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://upload.wikimedia.org/wikipedia/commons/a/ab/天-order.gif" height=100) img(src="https://upload.wikimedia.org/wikipedia/commons/a/ab/天-order.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/33618.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/33618.gif" height=100)
figcaption.text-center huang1 figcaption.text-center huang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/2" title: "千字文 - 1/2"
date: "2019-08-12" date: "2019-08-12"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 日月盈昃,辰宿列張。 .w-100.mt-3
h3.text-center 日月盈昃,辰宿列張。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26085.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26085.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/24373.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/24373.gif" height=100)
figcaption.text-center zhang1 figcaption.text-center zhang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/3" title: "千字文 - 1/3"
date: "2019-08-13" date: "2019-08-13"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 寒來暑往,秋收冬藏。 .w-100.mt-3
h3.text-center 寒來暑往,秋收冬藏。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23506.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23506.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/34255.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/34255.gif" height=100)
figcaption.text-center cang2 figcaption.text-center cang2
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/4" title: "千字文 - 1/4"
date: "2019-08-14" date: "2019-08-14"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 閏餘成歲,律呂調陽。 .w-100.mt-3
h3.text-center 閏餘成歲,律呂調陽。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
.hanzi-img 閏 .hanzi-img 閏
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38525.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38525.gif" height=100)
figcaption.text-center yang2 figcaption.text-center yang2
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/5" title: "千字文 - 1/5"
date: "2019-08-15" date: "2019-08-15"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 雲騰致雨,露結為霜。 .w-100.mt-3
h3.text-center 雲騰致雨,露結為霜。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38642.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38642.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38684.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38684.gif" height=100)
figcaption.text-center shuang1 figcaption.text-center shuang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/6" title: "千字文 - 1/6"
date: "2019-08-16" date: "2019-08-16"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 金生麗水,玉出崑岡。 .w-100.mt-3
h3.text-center 金生麗水,玉出崑岡。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/37329.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/37329.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23713.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23713.gif" height=100)
figcaption.text-center gang1 figcaption.text-center gang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/7" title: "千字文 - 1/7"
date: "2019-08-17" date: "2019-08-17"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 劍號巨闕,珠稱夜光。 .w-100.mt-3
h3.text-center 劍號巨闕,珠稱夜光。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/21133.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/21133.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/20809.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/20809.gif" height=100)
figcaption.text-center guang1 figcaption.text-center guang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/8" title: "千字文 - 1/8"
date: "2019-08-18" date: "2019-08-18"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 果珍李柰,菜重芥薑。 .w-100.mt-3
h3.text-center 果珍李柰,菜重芥薑。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26524.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26524.gif" height=100)
@@ -34,7 +35,7 @@ tags:
.hanzi-img 薑 .hanzi-img 薑
figcaption.text-center jiang1 figcaption.text-center jiang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 1/9" title: "千字文 - 1/9"
date: "2019-08-19" date: "2019-08-19"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 海咸河淡,鱗潛羽翔。 .w-100.mt-3
h3.text-center 海咸河淡,鱗潛羽翔。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/28023.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/28023.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/32724.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/32724.gif" height=100)
figcaption.text-center xiang2 figcaption.text-center xiang2
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 2/1" title: "千字文 - 2/1"
date: "2019-08-20" date: "2019-08-20"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 龍師火帝,鳥官人皇。 .w-100.mt-3
h3.text-center 龍師火帝,鳥官人皇。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/40845.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/40845.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/30343.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/30343.gif" height=100)
figcaption.text-center huang1 figcaption.text-center huang1
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,12 +1,13 @@
--- ---
title: "千字文 - 2/2" title: "千字文 - 2/2"
date: "2019-08-21" date: "2019-08-21"
tags: tag:
- 千字文 - 千字文
- 汉字 - 汉字
--- ---
.w-100.mt-3: h3.text-center 始制文字,乃服衣裳。 .w-100.mt-3
h3.text-center 始制文字,乃服衣裳。
.w-100 .w-100
figure.d-inline-block figure.d-inline-block
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/22987.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/22987.gif" height=100)
@@ -34,7 +35,7 @@ tags:
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/35059.gif" height=100) img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/35059.gif" height=100)
figcaption.text-center chang2 figcaption.text-center chang2
| === ===
.w-100.mt-3 .w-100.mt-3
:markdown :markdown
@@ -1,7 +1,7 @@
--- ---
title: "句子 1.1" title: "句子 1.1"
date: "2019-08-16 1:00" date: "2019-08-16 01:00"
tags: tag:
- 千字文 - 千字文
- 句子 - 句子
--- ---
@@ -1,7 +1,7 @@
--- ---
title: "句子 1.2" title: "句子 1.2"
date: "2019-08-17 1:00" date: "2019-08-17 01:00"
tags: tag:
- 千字文 - 千字文
- 句子 - 句子
--- ---
@@ -1,7 +1,7 @@
--- ---
title: "句子 1.3" title: "句子 1.3"
date: "2019-08-17 2:00" date: "2019-08-17 02:00"
tags: tag:
- 千字文 - 千字文
- 句子 - 句子
--- ---
@@ -1,7 +1,7 @@
--- ---
title: "身体1" title: "身体1"
date: "2019-08-18 1:00" date: "2019-08-18 01:00"
tags: tag:
- 分类词汇 - 分类词汇
- 身体 - 身体
--- ---
@@ -15,7 +15,7 @@ tags:
Highlight text and press "x" inside the presentation to speak. Highlight text and press "x" inside the presentation to speak.
| === ===
:markdown :markdown
胳膊 | gēbo | arm 胳膊 | gēbo | arm
File renamed without changes.
+34
View File
@@ -0,0 +1,34 @@
<template lang="pug">
div
b-navbar(toggleable="lg" type="dark" variant="success")
b-navbar-brand(to="/") {{title}}
b-navbar-toggle(target="nav-collapse")
b-collapse#nav-collapse(is-nav)
b-navbar-nav
b-nav-item(v-for="t in tabs" :key="t.name" :to="t.to" :url="t.url") {{t.name}}
b-navbar-nav.ml-auto
b-nav-form(@submit.stop.prevent)
b-form-input.mr-sm-2(size="sm" placeholder="Type to search" style="min-width: 200px" v-model="g.q")
b-nav-item(to="/about") About
b-container.mt-3
b-row
b-col(sm=12 lg=8)
router-view
b-col(sm=12 lg=4)
sidebar
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import Sidebar from "@/layouts/Sidebar.vue";
import { g } from "@/shared";
@Component({
components: {Sidebar}
})
export default class App extends Vue {
private title = g.config.title || "BLOG"
private tabs = g.config.tabs || []
private g = g;
}
</script>
-39
View File
@@ -1,39 +0,0 @@
import "./disqus";
import "./app.scss";
window.addEventListener("keydown", (ev) => {
if (ev.key === "s") {
const s = window.getSelection().toString();
if (s) {
speak(s);
}
}
});
window.onload = () => {
for (const a of Array.from(document.getElementsByTagName("a"))) {
const href = a.href;
console.log(href);
const m = /^slide:github:([^/]+)\/([^/]+)\/(.+)$/.exec(href);
if (m) {
const url = new URL("https://reveal-md.herokuapp.com/");
url.searchParams.append("q", `https://raw.githubusercontent.com/${m[1]}/${m[2]}/master/src/resources/${m[3]}`);
a.href = url.href;
}
}
}
function speak(s: string) {
const allVoices = speechSynthesis.getVoices();
let vs = allVoices.filter((v) => /zh[-_]cn/i.test(v.lang));
if (vs.length === 0) {
vs = allVoices.filter((v) => /zh/i.test(v.lang));
}
if (vs.length > 0) {
const u = new SpeechSynthesisUtterance(s);
u.lang = vs[0].lang;
u.rate = 0.8;
speechSynthesis.speak(u);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+6
View File
@@ -0,0 +1,6 @@
declare const POSTS: Record<string, any>[];
declare const CONFIG: Record<string, any>;
declare const README: string;
declare module "@/plugins/render";
declare module 'vue-disqus';
-15
View File
@@ -1,15 +0,0 @@
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://zhdiary.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
+11
View File
@@ -0,0 +1,11 @@
<template lang="pug">
:markdown
### Sorry, but no post can be found.
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
})
</script>
+20
View File
@@ -0,0 +1,20 @@
<template lang="pug">
b-card
header: h1 Write a post!
.post-content
p Go to <code>./public/posts</code> folder and create your first post, creating <code>first-post.md</code> file filled with the following content:
pre.
---
title: First post
tag:
- news
---
# This is my first post!
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
})
</script>
+116
View File
@@ -0,0 +1,116 @@
<template lang="pug">
b-card.mb-3.post
.post-meta.mb-3
.float-right {{dateString}}
.post-meta-author
a(:href="author.link"): b-img(rounded="circle" :src="author.avatar")
a(:href="author.link") {{author.login}}
h2
b-link(v-if="isTeaser" :to="to") {{title}}
span(v-else="") {{title}}
div
div(v-if="isTeaser" v-html="teaser")
div(v-else="" v-html="content")
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
import moment, { Moment } from "moment";
import matter from "gray-matter";
import HyperPug from "hyperpug";
import { pugFilters, md } from "@/plugins/render";
import { g } from '../shared';
@Component
export default class Post extends Vue {
@Prop() private filename!: string;
@Prop({default: false}) private isTeaser!: boolean;
private matter: any = {};
get title() {
if (this.matter.data) {
return this.matter.data.title || ""
}
return ""
}
get moment(): Moment | null {
if (this.matter.data && this.matter.data.date) {
return moment(this.matter.data.date);
}
return null;
}
get dateString() {
return this.moment ? this.moment.format("ddd D MMMM YYYY") : "";
}
get author() {
const author = (this.matter.data ? this.matter.data.author : null) || g.config.author;
if (!author) {
return {
link: "#",
avatar: "#",
login: "Anonymous"
}
}
return author;
}
get url() {
return this.filename.replace(/^public\//, process.env.BASE_URL || "/")
}
get to() {
const m = this.moment;
if (m) {
return `/p/${m.format("YYYY")}/${m.format("MM")}/${
this.filename
.replace(/^.*\//, "")
.replace(/\.[^.]+$/, "")}`;
}
return "";
}
get teaser() {
const { content } = this.matter;
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
if (m) {
return this.parseContent(m[1]);
}
return this.parseContent(content || "");
}
get content() {
const { content } = this.matter;
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
console.log(m)
if (m) {
return this.parseContent(m[1] + (m[2] || ""));
}
return this.parseContent(content || "");
}
public async mounted() {
console.log(this.url);
this.matter = matter(await (await fetch(this.url)).text());
}
private parseContent(s: string): string {
if (/\.pug$/.test(this.filename)) {
s = new HyperPug(pugFilters).parse(s);
} else if (/\.html?$/.test(this.filename)) {
return s;
}
return md.md2html(s);
}
}
</script>
+84
View File
@@ -0,0 +1,84 @@
<template lang="pug">
div
b-card.mb-3
:markdown
#### Record my journey to Chinese by [@polvcode](https://polvcode.dev)
Highlight text and press "s" to speak.
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
b-card
h3 Tag cloud
span.mr-3(v-for="t in tags" :key="t.name" :class="t.class")
b-link(:to="'/tag/' + t.name") {{t.name}}
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { g } from '../shared';
@Component
export default class Sidebar extends Vue {
get tags() {
const tagList: Record<string, number> = {};
g.posts.forEach((p) => {
for (const t of p.tag || []) {
tagList[t] = (tagList[t] || 0) + 1;
}
});
return Object.keys(tagList).sort().map((t) => {
return {
name: t,
class: (() => {
const count = tagList[t];
if (count > 30) {
return "c30"
} else if (count > 20) {
return "c20"
} else if (count > 10) {
return "c10"
} else if (count > 5) {
return "c5"
} else if (count > 3) {
return "c3"
} else if (count > 1) {
return "c2"
}
return "c1"
})()
}
});
}
}
</script>
<style lang="scss" scoped>
.c30 {
font-size: 40px;
}
.c20 {
font-size: 30px;
}
.c10 {
font-size: 25px;
}
.c5 {
font-size: 20px;
}
.c3 {
font-size: 16px;
}
.c2 {
font-size: 13px;
}
.c1 {
font-size: 10px;
}
</style>
+16
View File
@@ -0,0 +1,16 @@
import '@babel/polyfill';
import 'mutationobserver-shim';
import Vue from 'vue';
import './plugins/bootstrap-vue';
import App from './App.vue';
import router from './router';
import VueDisqus from 'vue-disqus';
import "./plugins/gtag";
Vue.config.productionTip = false
Vue.use(VueDisqus)
new Vue({
router,
render: h => h(App)
}).$mount('#app')
-3
View File
@@ -1,3 +0,0 @@
---
title: '中文 Diary - My Chinese learning diary'
---
+7
View File
@@ -0,0 +1,7 @@
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
+22
View File
@@ -0,0 +1,22 @@
import { g } from "@/shared";
import { dotGetter } from './search';
declare const dataLayer: any[];
async function gAnalytics(id: string) {
var d = document, s = d.createElement('script');
s.src = `https://www.googletagmanager.com/gtag/js?id=${id}`;
s.setAttribute('data-timestamp', new Date().toISOString());
(d.head || d.body).appendChild(s);
(window as any).dataLayer = dataLayer || [];
function gtag(...x: any[]){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', id);
}
const id = dotGetter(g.config, "external.google.analytics");
if (id) {
gAnalytics(id);
}
@@ -1,8 +1,7 @@
const pug = require("pug"); import showdown from "showdown";
const showdown = require("showdown"); import h from "hyperscript";
const h = require("hyperscript");
class Markdown { export class Markdown {
constructor() { constructor() {
this.converter = new showdown.Converter({ this.converter = new showdown.Converter({
parseImgDimensions: true parseImgDimensions: true
@@ -33,25 +32,12 @@ class Markdown {
} }
} }
const md = new Markdown(); export const md = new Markdown();
export const pugFilters = {
module.exports = { markdown: (text) => {
Markdown, return md.md2html(text);
apply() { },
cogear.on("preload.page.parse", (page) => { css: (text) => {
if (page.format === ".pug") { return h("style", {innerHTML: text});
page.__content = pug.compile(page.__content || "", {
filters: {
markdown: (text) => {
return md.md2html(text)
}
}
})()
page.format = ".html";
} else if (page.format === ".md") {
page.__content = md.md2html(page.__content || "")
page.format = ".html";
}
})
} }
} };
+21
View File
@@ -0,0 +1,21 @@
import SearchParser from "./search";
const p = new SearchParser();
describe("SearchParser.doParse", () => {
[
"",
"a",
"a:b",
"a:b c",
"a:b c:d",
"a:b OR c",
"date:2018-08-12",
"date>2018-08-12",
" tag:分类词汇"
].forEach((q) => {
it(q, () => {
console.dir(p.parseFull(q), {depth: null});
})
});
})
+396
View File
@@ -0,0 +1,396 @@
/**
* Adapted from https://github.com/patarapolw/rep2recall-web/blob/master/src/node/engine/search.ts
*/
import uuid from "uuid/v4";
import moment from "moment";
import { g } from '@/shared';
export interface ISearchParserResult {
is: Set<string>;
sortBy: string | null;
desc: boolean | null;
cond: Record<string, any>;
}
export default class SearchParser {
public is: Set<string> = new Set();
public sortBy: string | null = null;
public desc: boolean | null = null;
private readonly anyOf: Set<string> | null = null;
private readonly isString = new Set(["title", "author", "tag"]);
private readonly isDate = new Set(["date"]);
public filterPosts(q: string): Record<string, any>[] {
const cond = this.parse(q);
let ps = g.posts;
if (this.sortBy) {
ps = ps.sort(sorter(this.sortBy, this.desc || false));
} else {
ps = ps.sort(sorter("date", true));
}
ps = ps.filter(mongoFilter(cond));
return ps;
}
public parse(q: string): Record<string, any> {
this.is = new Set();
this.sortBy = null
this.desc = null;
try {
return this._parse(q);
} catch (e) { }
return {};
}
public parseFull(q: string): ISearchParserResult {
return {
cond: this.parse(q),
is: this.is,
sortBy: this.sortBy,
desc: this.desc
}
}
private _parse(q: string): Record<string, any> {
q = q.trim();
for (const method of [
this.removeBrackets,
this.parseSep(" OR "),
this.parseSep(" "),
this.parseNeg,
this.parseFullExpr,
this.parsePartialExpr
]) {
try {
return method.bind(this)(q);
} catch (e) { }
}
throw new Error();
}
private removeBrackets(q: string) {
if (q[0] === "(" && q[q.length - 1] === ")") {
return this.parse(q.substr(1, q.length - 2));
}
throw new Error("Not bracketed");
}
private parseSep(sep: string) {
return (q: string) => {
const brackets: any = {};
q = q.replace(/\([^)]+\)/g, (p0) => {
const id = uuid();
brackets[id] = p0;
return id;
});
const tokens = q.split(sep);
tokens.forEach((t, i) => {
for (const k of Object.keys(brackets)) {
tokens[i] = tokens[i].replace(k, brackets[k]);
}
});
if (tokens.length >= 2) {
const parsedTokens = tokens.map((t) => this._parse(t)).map((t) => t);
if (parsedTokens.length > 1) {
return { [sep === " OR " ? "$or" : "$and"]: parsedTokens };
} else {
return parsedTokens[0];
}
}
throw new Error(`Not separated by '${sep}'`);
}
}
private parseNeg(q: string) {
if (q[0] === "-") {
const sb = "-sortBy:";
if (q.startsWith(sb) && q !== sb) {
this.sortBy = q.substr(sb.length);
return {};
}
return { $not: this.parse(q.substr(1)) };
}
throw new Error("Not negative");
}
private parseFullExpr(q: string) {
const m = /^([\w-]+)(:|~|[><]=?|=)([\S-]+|"[^"]+")$/.exec(q);
if (m) {
let [_, k, op, v]: any[] = m;
if (v.length > 2 && v[0] === '"' && v[v.length - 1] === '"') {
v = v.substr(1, v.length - 2);
} else {
const m1 = /^\d+(?:\.\d+)?$/.exec(v);
if (m1) {
v = parseFloat(v);
}
}
if (k === "is") {
if (v === "due") {
k = "nextReview";
op = "<=";
v = new Date();
} else if (v === "leech") {
k = "srsLevel";
op = "=";
v = 0;
} else if (v === "new") {
k = "nextReview";
op = "=";;
v = "NULL";
} else {
this.is.add(v);
return {};
}
} else if (k === "sortBy") {
this.sortBy = v;
return {};
}
if (op === ":") {
if (k === "due" || k === "nextReview") {
k = "nextReview";
v = "<="
} else if (k === "created" || k === "modified") {
v = "<=";
}
}
if (v === "NULL") {
v = null;
}
if (this.isDate.has(k)) {
if (v === "NOW") {
v = new Date();
} else if (typeof v === "string") {
const m1 = /^([-+]?\\d+)(\\S*)$/.exec(v);
let isMomentParsed = false;
if (m1) {
try {
v = moment().add(parseInt(m1[1]), m1[2] as any).toDate();
isMomentParsed = true;
} catch (e) { }
}
if (!isMomentParsed) {
try {
v = moment(v).toDate()
} catch (e) { }
}
}
}
// if (moment.isDate(v) && op === ":") {
// op = ">";
// }
if (v) {
if (op === ":") {
if (typeof v === "string" || this.isString.has(k)) {
v = { $substr: v };
}
} else if (op === "~") {
v = { $regex: v.toString() };
} else if (op === ">=") {
v = { $gte: v };
} else if (op === ">") {
v = { $gt: v }
} else if (op === "<=") {
v = { $lte: v };
} else if (op === "<") {
v = { $lt: v };
}
}
return { [k]: v };
}
throw new Error("Not full expression");
}
private parsePartialExpr(q: string) {
if (q && q.indexOf(":") === -1) {
const orCond: any[] = [];
if (this.anyOf) {
for (const a of this.anyOf) {
if (this.isString.has(a)) {
orCond.push({ [a]: { $substr: q } });
} else {
orCond.push({ [a]: q });
}
}
} else {
return {"*": {$substr: q}}
}
if (orCond.length > 1) {
return { $or: orCond };
} else if (orCond.length === 1) {
return orCond[0];
}
return {};
}
throw new Error("Not partial expression");
}
}
export function mongoFilter(cond: any) {
return (item: Record<string, any>): boolean => {
for (const k of Object.keys(cond)) {
if (k[0] === "$") {
if (k === "$and") {
return cond[k].every((x: Record<string, any>) => mongoFilter(x)(item));
} else if (k === "$or") {
return cond[k].some((x: Record<string, any>) => mongoFilter(x)(item));
} else if (k === "$not") {
return !mongoFilter(cond[k])(item);
}
} else {
let itemK = dotGetter(item, k);
if (cond[k] && cond[k].constructor === {}.constructor
&& Object.keys(cond[k]).some((k0) => k0[0] === "$")) {
return (() => {
for (const op of Object.keys(cond[k])) {
try {
if (op === "$regex") {
if (Array.isArray(itemK)) {
return itemK.some(new RegExp(cond[k][op].toString(), "i").test);
} else {
return new RegExp(cond[k][op].toString(), "i").test(itemK);
}
} else if (op === "$startswith") {
if (Array.isArray(itemK)) {
return itemK.some((el) => el.startsWith(cond[k][op]));
} else {
return itemK.startsWith(cond[k][op]);
}
} else if (op === "$substr") {
if (Array.isArray(itemK)) {
return itemK.some((el: string) => el.includes(cond[k][op]));
} else {
return itemK.includes(cond[k][op]);
}
} else if (op === "$exists") {
return (itemK === null || itemK === undefined || itemK === "") === cond[k][op];
} else {
let v = itemK;
let v0 = cond[k][op];
try {
[v, v0] = [parseInt(v), parseInt(v0)];
} catch (e) { }
if (op === "$gte") {
return v >= v0;
} else if (op === "$gt") {
return v > v0;
} else if (op === "$lte") {
return v <= v0;
} else if (op === "$lt") {
return v < v0;
}
}
} catch (e) { }
}
return false;
})();
} else if (Array.isArray(itemK)) {
if (!itemK.includes(cond[k])) {
return false;
}
} else if (itemK !== cond[k]) {
return false;
}
}
}
return true;
}
}
export function dotGetter(d: any, k: string) {
let v = d;
for (const kn of k.split(".")) {
if (v && v.constructor === {}.constructor) {
if (kn === "*") {
v = Object.values(v);
} else {
v = v[kn];
if (v === undefined) {
v = {};
}
}
} else if (Array.isArray(v)) {
try {
v = v[parseInt(kn)];
if (v === undefined) {
v = null;
break;
}
} catch (e) {
v = null;
break;
}
}
}
if (v && v.constructor === {}.constructor && Object.keys(v).length === 0) {
v = null;
}
return v;
}
export function sorter(sortBy?: string, desc?: boolean) {
return (a: any, b: any) => {
if (!sortBy) {
return 0;
}
const m = a[sortBy];
const n = b[sortBy];
if (typeof m === typeof n) {
if (typeof m === "string") {
return desc ? n.localeCompare(m) : m.localeCompare(n);
} else if (typeof m === "number") {
return desc ? n - m : m - n;
} else {
return 0;
}
} else {
const typeDict = {
"number": 1,
"string": 2,
"object": 3
} as any;
const tM = typeDict[typeof m] || -1;
const tN = typeDict[typeof n] || -1;
return desc ? tN - tM : tM - tN;
}
}
}
-104
View File
@@ -1,104 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
---
罗马人的黄金时代已过去了。
--
- 罗 马 luó mǎ Rome, capital of Italy
- 黄 金 时 代 huáng jīn shí dài golden age
- 已 yǐ already; to stop
- 过 去 guò qù (in the) past
---
黄河是中国的第二大河流。
--
- 河 流 hé liú river
---
哲学上的玄想出自实际情况。
--
- 哲 学 zhé xué philosophy
- 玄 xuán black; mysterious
- 想 出 xiǎng chū to figure out
- 实 际 情 况 shí jì qíng kuàng actual circumstances
---
宇宙毫无疑问是无限的。
--
- 宇 宙 yǔ zhòu universe; cosmos
- 毫 无 疑 问 háo wú yí wèn certainty
- 无 限 wú xiàn unlimited
---
许多房屋被洪水冲走了。
--
- 许 多 xǔ duō many; a lot of
- 房 屋 fáng wū house; building
- 被 bèi quilt; by
- 洪 水 hóng shuǐ deluge; flood
- 冲 走 chōng zǒu to flush away
---
多么荒唐的故事!
--
- 荒 唐 huāng táng beyond belief
- 故 事 gù shì old practice
---
我当初荒废了岁月。
--
- 当 初 dāng chū at that time
- 荒 废 huāng fèi to abandon (cultivated fields)
- 岁 月 suì yuè years; time
---
资产盈利能力或一些其他的价值,它们的主人。
--
- 资 产 zī chǎn property; assets
- 盈 利 yíng lì profit; gain
- 能 力 néng lì capability; ability
- 或 huò maybe; perhaps; or
- 一 些 yī xiē some; a few
- 其 他 qí tā other; the rest
- 价 值 jià zhí value; worth
- 主 人 zhǔ rén master; host; owner
---
庆祝历史上的每个圣人的诞辰
--
- 庆 祝 qìng zhù to celebrate
- 历 史 上 lì shǐ shàng historical
- 圣 人 shèng rén saint; sage
- 诞 辰 dàn chén birthday
---
那张CD是我儿子的。
-156
View File
@@ -1,156 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
---
一股寒流侵袭了欧洲。
--
- 寒 流 hán liú cold stream
- 侵 袭 qīn xí to invade
- 欧 洲 ōu zhōu Europe
---
天气非常寒冷。
--
- 寒 冷 hán lěng cold (climate)
---
寒风席卷北方。
--
- 席 卷 xí juǎn to engulf; to sweep
- 北 方 běi fāng north
---
暑假期间,我在邮局工作。
--
- 暑 假 shǔ jià summer vacation
- 期 间 qī jiān period of time
- 邮 局 yóu jú post office
---
他们去了爱丁堡避暑。
--
- 爱 丁 堡 ài dīng bǎo Edinburgh, capital of Scotland
- 避 暑 bì shǔ to be away for the summer holidays
---
然而,相反的理论往往也是真的。
--
- 然 而 rán ér however; yet; but
- 相 反 xiāng fǎn opposite; contrary
- 理 论 lǐ lùn theory; to argue
- 往 往 wǎng wǎng often; frequently
---
我不能再往前走了。
--
- 往 前 wǎng qián to move forwards
---
春天和秋天,你更喜欢哪个?
---
今年中秋节是星期一吗?
--
- 中 秋 节 zhōng qiū jié the Mid-Autumn Festival on 15th of 8th lunar month
---
你有没有收据?
--
- 收 据 shōu jù receipt
---
对不起。我收回我的话。
--
- 收 回 shōu huí to regain
---
我今天收到了她的信。
--
- 收 到 shōu dào to receive
---
西藏的好水就是世界的好水。
--
- 西 藏 xī zàng Tibet
- 世 界 shì jiè world
---
他把它藏在了门后。
---
谁在影子里藏着?
--
- 影 子 yǐng zi shadow; reflection
---
我想知道这个冬天在我们的城市会不会下雪。
--
- 城 市 chéng shì city; town
- 下 雪 xià xuě to snow
---
冬季是一年中最冷的季节。
--
- 冬 季 dōng jì winter
- 年 中 nián zhōng within the year
- 季 节 jì jié time; season
---
我们为过冬储备了10吨煤。
--
- 过 冬 guò dōng to get through the winter
- 储 备 chǔ bèi reserves
- 吨 dūn ton (loanword)
- 煤 méi coal
-126
View File
@@ -1,126 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
---
用闰表来测定的格林尼治时间。
--
- 测 定 cè dìng to survey and evaluate
- 格 林 尼 治 gé lín ní zhì Greenwich (former location of Greenwich observatory, at zero longitude)
---
闰的(闰日、闰年的)在日历中加入一天或一月而使历年与太阳年相吻合的
--
- 加 入 jiā rù to become a member
- 日 历 rì lì calendar
- 历 年 lì nián calendar year
- 吻 合 wěn hé to be a good fit
---
尽管现在中国人的生活越来越好了,可是还有改进的余地。
--
- 尽 管 jǐn guǎn despite; although
- 越 来 越 yuè lái yuè more and more
- 改 进 gǎi jìn to improve
- 余 地 yú dì margin; leeway
---
你留在这儿,其余的人去外面
--
- 其 余 qí yú the rest; remaining
---
他和我同岁。
---
你多大岁数了?
---
法律有很多不明确的地方。
--
- 法 律 fǎ lǜ law
- 不 明 确 bù míng què indefinite; unclear
---
嚴以律己, 寬以待人。
--
- 严 以 律 己 yǎn yǐ lǜ jǐ to be strict with oneself (idiom)
- 宽 以 待 人 kuān yǐ dài rén to be lenient with others (idiom)
---
心里正在捉着迷藏,吕校长鞠躬请他演讲,下面一阵鼓掌。
--
- 捉 zhuō to clutch; to grab
- 着 迷 zháo mí to be fascinated
- 校 长 xiào zhǎng (college, university) president
- 鞠 躬 jū gōng to bow
- 演 讲 yǎn jiǎng lecture
- 一 阵 yī zhèn a burst; a fit
- 鼓 掌 gǔ zhǎng to applaud; to clap
---
他强调都市生活的便利方面。
--
- 强 调 qiáng diào to emphasize (a statement)
- 都 市 dū shì city; metropolis
- 生 活 shēng huó life; activity
- 便 利 biàn lì convenient; easy
- 方 面 fāng miàn respect; aspect
---
现今人们没有空调无法生活。
--
- 无 法 wú fǎ unable; incapable
---
众口难调。
---
我想特别强调这一点。
---
如果没有太阳,那所有生物都会死。
---
住家的阳台都对着广
--
- 阳 台 yáng tái balcony; porch
---
这间房照不到很多阳光。
-111
View File
@@ -1,111 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
---
他们俩相爱已经多年。
---
她特别地恨那个男孩。
---
听了这消息我心里很不安。
---
她的优点是临危不乱。
---
多数旧电影片都是黑白的。
---
这个墨颜色很黑。
---
冰太薄,承受不住你的重量。
---
厚厚的云把风景遮住了。
---
我饱了,一点也吃不下了。
---
这孩子不是肚子饿,就是馋嘴。
---
他的境遇很可悲。
---
大厅里充满了他们的欢笑声。
---
他的背上有一个蛇形的红斑。
---
病人服药后腹痛逐止。
---
虽然笨,可是他很刻苦。
---
他很年轻,身子骨很灵巧。
---
这本书的缺点是太长。
---
他把头发剪成时兴的短发。
---
他发誓要为父亲报仇。
---
你不像是忘恩负义的人。
---
安居乐业
---
黑名单
---
厚此薄彼
---
悲欢离合
---
笨手笨脚
---
日短夜长
---
恩将仇报
@@ -1,172 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
source: https://th.hujiang.com/new/p394824/
---
调味料
--
เครื่องปรุงรส
---
沙拉油
--
น้ำมันสลัด
---
蚝油
--
น้ำมันหอย
---
面包屑
--
เศษขนมปัง
---
酦粉
--
แป้งฟู
---
调味汁
--
น้ำปรุงรสอาหาร
---
面粉
--
แป้งข้าวสาลี
---
香料
--
เครื่องเทศ
---
盐
--
เกลือ
---
胡椒
--
พริกไทย
---
蕃茄酱
--
ซอสมะเขือเทศ
---
酱油
--
ซีอิ๊ว
---
糖
--
น้ำตาล
---
辣酱
--
ซอสพริก
---
醋
--
น้ำส้ม
---
黄油
--
เนย
---
花生酱
--
เนยถั่ว
---
椰浆
--
น้ำกะทิ
---
牛奶
--
นม
---
虾酱
--
กะปิ
---
鱼露
--
น้ำปลา
-112
View File
@@ -1,112 +0,0 @@
---
shuffle: true
js:
- https://patarapolw.github.io/s2speak/index.min.js?key=x
---
我们应当用右手拿着坚固的盾牌去捍卫我们在中东的利益。
--
- 应 当 yīng dāng should; ought to
- 右 手 yòu shǒu right hand
- 拿 ná to hold; to seize
- 坚 固 jiān gù firm; firmly; hard
- 盾 牌 dùn pái shield; pretext
- 捍 卫 hàn wèi to defend
- 利 益 lì yì benefit
---
倾盆大雨劈头盖脸地浇了下来。
--
- 倾 盆 大 雨 qīng pén dà yǔ a downpour
- 劈 头 盖 脸 pī tóu gài liǎn lit. splitting the head and covering the face (idiom)
- 浇 jiāo to pour liquid
- 下 来 xià lai to come down
---
我踢足球的時候弄傷了腿。
--
- 踢 tī to kick
- 足 球 zú qiú soccer ball; soccer
- 弄 伤 nòng shāng to bruise
- 腿 tuǐ hip bone
---
你认为汤姆的腰围是多少?
--
- 汤 姆 tāng mǔ Tom (name)
- 腰 围 yāo wéi waist measurement
---
一定是有人偷了你的手錶。
--
- 偷 tōu to steal; to pilfer
- 手 表 shǒu biǎo wrist watch
---
抡锤子的工作使胳膊上的肌肉结成了一块块硬疙瘩。
--
- 抡 lūn to swing (one's arms, a heavy object)
- 锤 子 chuí zi hammer
- 胳 膊 gē bo arm
- 肌 肉 jī ròu muscle; flesh
- 结 成 jié chéng to form
- 硬 yìng hard; stiff; strong
- 疙 瘩 gē da swelling or lump on skin
---
吃些火腿。
--
- 些 xiē some; few; several
- 火 腿 huǒ tuǐ ham
---
他脸上显出一副严肃的神色。
--
- 脸 liǎn face
- 显 出 xiǎn chū to express
- 严 肃 yán sù solemn; grave
- 神 色 shén sè expression; look
---
鸟的翅膀相当于人的胳膊。
--
- 翅 膀 chì bǎng wing
- 相 当 于 xiāng dāng yú equivalent to
- 胳 膊 gē bo arm
---
这事他早就扔在脖子后边了。
--
- 早 就 zǎo jiù already at an earlier time
- 扔 rēng to throw
- 脖 子 bó zi neck
- 后 边 hòu bian back; rear; behind
-3
View File
@@ -1,3 +0,0 @@
{
"root": "."
}
+17
View File
@@ -0,0 +1,17 @@
import Vue from 'vue';
import Router from 'vue-router';
import Search from './views/Search.vue';
import Single from "./views/Single.vue";
import About from "./views/About.vue";
Vue.use(Router);
export default new Router({
mode: "history",
routes: [
{ path: '/', component: Search },
{ path: '/tag/:tag', component: Search },
{ path: "/p/:y/:mo/:name", component: Single },
{ path: "/about", component: About }
]
});
+5
View File
@@ -0,0 +1,5 @@
export const g = {
q: "",
posts: POSTS,
config: CONFIG
};
+13
View File
@@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}
+4
View File
@@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
+11
View File
@@ -0,0 +1,11 @@
export function escapeRegExp(s: string) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
export function normalizeArray(it: any): any {
if (Array.isArray(it)) {
return it[0];
}
return it;
}
+17
View File
@@ -0,0 +1,17 @@
<template lang="pug">
b-card.p-3(v-html="content")
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { md } from "@/plugins/render";
@Component
export default class About extends Vue {
private content: string = "";
public mounted() {
this.content = md.md2html(README);
}
}
</script>
+61
View File
@@ -0,0 +1,61 @@
<template lang="pug">
div
h1.tag(v-if="$route.tag") {{$route.tag}}
div(v-if="posts && posts.length > 0")
post(v-for="p in posts" :filename="p.filename" :is-teaser="true" :key="p.filename")
b-pagination(v-model="page" :total-rows="count" :per-page="perPage")
div(v-else-if="posts")
empty
div(v-else)
first
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import Post from "@/layouts/Post.vue";
import Empty from "@/layouts/Empty.vue";
import First from "@/layouts/First.vue";
import SearchParser from "@/plugins/search";
import { normalizeArray } from "@/util";
import { g } from "@/shared";
@Component({
components: {
Post, Empty, First
}
})
export default class Search extends Vue {
private posts?: any[] = [];
private g = g;
private page: number = 1;
private count: number = 0;
private perPage: number = 5;
public mounted() {
this.page = parseInt(normalizeArray(this.$route.query.page) || "1");
this.updatePosts();
}
@Watch("g.q")
@Watch("page")
@Watch("$route.params")
private async updatePosts() {
const parser = new SearchParser();
let ps = this.$route.params.tag
? parser.filterPosts([this.g.q, `tag=${this.$route.params.tag}`].join(" "))
: parser.filterPosts(this.g.q);
const { perPage } = g.config.posts;
this.perPage = perPage;
this.count = ps.length;
ps = ps.filter((p, i) => {
const iPage = i / perPage;
return iPage >= this.page - 1 && iPage < this.page;
});
this.posts = ps;
}
}
</script>
+49
View File
@@ -0,0 +1,49 @@
<template lang="pug">
div
div(v-if="filename")
post(:is-teaser="false" :filename="filename")
vue-disqus(:shortname="disqus" :identifier="$route.path")
div(v-else="")
empty
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
import Post from "@/layouts/Post.vue";
import Empty from "@/layouts/Empty.vue";
import moment from "moment";
import { g } from '../shared';
import { dotGetter } from '../plugins/search';
@Component({
components: {
Post, Empty
}
})
export default class Search extends Vue {
private filename: string = "";
private disqus: string = dotGetter(g.config, "external.disqus");
public mounted() {
const ps = g.posts.filter((p) => {
const m = moment(p.date);
if (m.format("YYYY") === this.$route.params.y && m.format("MM") === this.$route.params.mo) {
if (p.filename
.replace(/^.*\//, "")
.replace(/\.[^.]+$/, "") === this.$route.params.name) {
return true;
}
}
return false;
});
if (!ps[0]) {
this.filename = "";
}
this.filename = ps[0].filename;
}
}
</script>
-54
View File
@@ -1,54 +0,0 @@
# Cogear.JS theme **blog**
![screenshot](https://github.com/codemotion/cogear-theme-blog/raw/master/screenshot.jpg)
Theme with blog posts on the home page.
Simple responsive design. Posts tags support with navigation and pagination.
# Installation
You need to install [`cogear-plugin-blog`](https://github.com/codemotion/cogear-plugin-blog) to use this theme.
Go to the site folder and install theme with command:
```bash
yarn add cogear-theme-blog
# or
npm install cogear-theme-blog
```
After that you need to edit your site `config.yaml` to set up installed theme.
```yaml
theme: blog
```
# Configuration
Use `cogear-plugin-blog` options in `./config.yaml`:
``` yaml
title: Blog | Cogear.JS – modern static websites generator
description:
keywords:
theme: blog
blog:
index: "" # Provide posts to index page
regex: "^p/"
tagUri: 'tag'
perPage: 5
pages:
^p/:
layout: post
author:
login: User
avatar: 'https://www.gravatar.com/avatar/400caf343d3bab57ab93f63e21a12be7?s=24'
link: https://cogearjs.org
```
# Usage
You may also want to install in with [`cogear-preset-blog`](https://github.com/codemotion/cogear-preset-blog).
# Docs
More info: [https://cogearjs.org/docs/themes](https://cogearjs.org/docs/themes).
-13
View File
@@ -1,13 +0,0 @@
article.post
header: h1 Write a post!
.post-content
p Go to <code>./src/pages/p</code> folder and create your first post, creating <code>first-post.md</code> file filled with the following content:
pre.
---
title: First post
tags:
- news
---
# This is my first post!
p Any questions? More info: <a href="https://github.com/codemotion/cogear-template-blog">https://github.com/codemotion/cogear-template-blog</a>
a(href="https://cogearjs.org"): img(src="https://cogearjs.org/images/cogearjs.jpg")
-13
View File
@@ -1,13 +0,0 @@
head
title=title
link(rel="shortcut icon" href=`${cogear.baseUrl}favicon.ico` type="image/png")
body
.navbar
.container
.brand
a(href="/") Blog
if cogear.blog.tags
ul
each t in cogear.blog.tags
- tName = t[0].toUpperCase() + t.slice(1)
li: a(href=`/tags/${t}`)= tName
-23
View File
@@ -1,23 +0,0 @@
mixin post(post)
.card.p-2.mb-3.post(class=`${post.teaser ? 'teaser' : 'full'}`)
.post-meta.mb-3
.float-right
+date(post.date)
.post-meta--author
a(href=`${post.author.link}`): img.mr-3(src=`${post.author.avatar}`)
a(href=`${post.author.link}`)= post.author.login
header
if post.teaser
h2: a(href=`${cogear.baseUrl}${post.uri}`)= post.title
else
h1= post.title
.post-content!= post.teaser ? post.teaser : post.content
if !post.teaser
ul.mt-3.pagination
li.page-item
a.page-link(href="javascript: history.back()") &larr; Back
mixin date(date)
- let months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
- let d = new Date(date)
- month = months[d.getMonth()]
time= `${month} ${d.getDate()}`
-21
View File
@@ -1,21 +0,0 @@
extends layout.pug
block head
if tag
- tagName = tag[0].toUpperCase() + tag.slice(1)
- title= `${tagName} | ${title}`
if paginator && paginator.current > 1
- title = `Page ${paginator.current} | ${title}`
title= title
block content
if tag
h1.tag= tagName
//- if content
//- article.page!= content
if posts
- let now = new Date()
each post in posts
if new Date(post.date) < now
+post(post)
include paginator
else
include includes/empty
-34
View File
@@ -1,34 +0,0 @@
include includes/mixins
head
block head
title= title
meta(content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1.0, maximum-scale=1" name="viewport")
meta(charset="UTF-8")
//- link(rel="shortcut icon" href=`${cogear.baseUrl}favicon.png` type="image/png")
link(rel="apple-touch-icon" sizes="180x180" href=`/apple-touch-icon.png`)
link(rel="icon" type="image/png" sizes="32x32" href=`/favicon-32x32.png`)
link(rel="icon" type="image/png" sizes="16x16" href=`/favicon-16x16.png`)
link(rel="manifest" href=`/site.manifest`)
link(rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous')
body
nav.navbar.navbar-dark.bg-success.navbar-expand-lg
a.ml-3.navbar-brand(href="/")= cogear.config.blog.title || "BLOG"
button.navbar-toggler(data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false")
span.navbar-toggler-icon
.collapse.navbar-collapse#navbarSupportedContent
if cogear.config.blog.tags
ul.navbar-nav.mr-auto
each t in cogear.config.blog.tags
- tName = t[0].toUpperCase() + t.slice(1)
li.nav-item: a.nav-link(href=`/tag/${t}`)= tName
.container.mt-3
.row
.col-lg-8.col-md-12
block content
.col-lg-4.col-md-12
include sidebar
script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.min.js')
script(src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js' integrity='sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1' crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js' integrity='sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM' crossorigin='anonymous')
-23
View File
@@ -1,23 +0,0 @@
if paginator
nav.mt-3
ul.pagination
if paginator.prev !== null
li.page-item
a.page-link(href=`${cogear.baseUrl}${paginator.prev}`) &larr; Prev
- let page = 1
while page <= paginator.total
- let range = 2
if page == 1 || page == paginator.total || (page >= paginator.current - range && page <= paginator.current + range)
if page == paginator.current
li.page-item.active
a.page-link(href=`${paginator.baseUri}/${page}`)= page
else
li.page-item
a.page-link(href=`${paginator.baseUri}/${page}`)= page
else if paginator.current < paginator.total - range && page == paginator.total - 1 || paginator.current > 1+range && page == range
li.page-item
a.page-link.ellipsis …
- page=page+1
if paginator.next !== null
li.page-item
a.page-link(href=`/${paginator.next}`) Next &rarr;
-12
View File
@@ -1,12 +0,0 @@
extends layout.pug
block content
+post({
title:title,
teaser: false,
tags:tags,
content:content,
date:date,
author: author
})
#disqus_thread
-9
View File
@@ -1,9 +0,0 @@
aside.card.p-3
block sidebar
:markdown-it
#### Record my journey to Chinese by [@polvcode](https://polvcode.dev)
Highlight text and press "s" to speak.
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
script(async src="https://platform.twitter.com/widgets.js" charset="utf-8")
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

-1
View File
@@ -1 +0,0 @@
import "./theme.scss";
-7
View File
@@ -1,7 +0,0 @@
.post {
.post-meta {
img {
border-radius: 50%;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"mocha"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
+84
View File
@@ -0,0 +1,84 @@
require = require("esm")(module)
const glob = require("fast-glob");
const fs = require("fs");
const { pugFilters } = require("./src/plugins/render");
const matter = require("gray-matter");
const yaml = require("js-yaml");
function configureWebpack(webpackConfig) {
for (const rule of webpackConfig.module.rules) {
if (rule.use) {
for (const use of rule.use) {
if (/pug/.test(use.loader)) {
use.options = {filters: pugFilters};
}
}
} else if (rule.oneOf) {
for (const oneOf of rule.oneOf) {
if (oneOf.use) {
for (const use of oneOf.use) {
if (/pug/.test(use.loader)) {
use.options = {filters: pugFilters};
}
}
}
}
}
}
(webpackConfig.devServer || {}).port = 8000;
}
function defineWebpack(webpackConfig, def) {
webpackConfig.plugin("define").tap((args) => {
args[0] = {
...args[0],
...def
}
return args;
});
}
async function generateDefinitions() {
return {
POSTS: JSON.stringify(fs.existsSync("public/posts")
? (await glob("public/posts/**/*.{md,markdown,pug,html}")).map((f) => {
const { data, content } = matter(fs.readFileSync(f, "utf8"));
return {
...data,
teaser: content.split("===")[0],
filename: f
}
}) : []),
CONFIG: JSON.stringify(yaml.safeLoad(fs.readFileSync("config.yaml", "utf8"))),
README: JSON.stringify(fs.readFileSync("README.md", "utf8"))
};
}
module.exports = (api, options) => {
api.registerCommand('build:async', async (args) => {
const def = await generateDefinitions();
api.configureWebpack(configureWebpack);
api.chainWebpack((webpackConfig) => {
defineWebpack(webpackConfig, def);
});
await api.service.run('build', args)
}),
api.registerCommand('serve:async', async (args) => {
const def = await generateDefinitions();
api.configureWebpack(configureWebpack);
api.chainWebpack((webpackConfig) => {
defineWebpack(webpackConfig, def);
});
await api.service.run('serve', args)
})
}
module.exports.defaultModes = {
'build:async': 'production',
'serve:async': 'development'
}
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
publicPath: "/"
}