diff --git a/components/search/Card.vue b/components/search/Card.vue new file mode 100644 index 00000000..f1c59d5f --- /dev/null +++ b/components/search/Card.vue @@ -0,0 +1,56 @@ + + + \ No newline at end of file diff --git a/components/search/FetchSearch.vue b/components/search/FetchSearch.vue new file mode 100644 index 00000000..2c6d1a71 --- /dev/null +++ b/components/search/FetchSearch.vue @@ -0,0 +1,71 @@ + + + \ No newline at end of file diff --git a/components/search/Search.vue b/components/search/Search.vue new file mode 100644 index 00000000..376719ca --- /dev/null +++ b/components/search/Search.vue @@ -0,0 +1,72 @@ + + + \ No newline at end of file diff --git a/components/search/SearchAsync.vue b/components/search/SearchAsync.vue new file mode 100644 index 00000000..7f70ab8f --- /dev/null +++ b/components/search/SearchAsync.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/components/search/SearchHanzi.vue b/components/search/SearchHanzi.vue new file mode 100644 index 00000000..01645407 --- /dev/null +++ b/components/search/SearchHanzi.vue @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/components/search/SearchHelp.vue b/components/search/SearchHelp.vue new file mode 100644 index 00000000..8e6db1d9 --- /dev/null +++ b/components/search/SearchHelp.vue @@ -0,0 +1,68 @@ + \ No newline at end of file diff --git a/components/search/assets/baidu.png b/components/search/assets/baidu.png new file mode 100644 index 00000000..3a7494f4 Binary files /dev/null and b/components/search/assets/baidu.png differ diff --git a/components/search/assets/handian.png b/components/search/assets/handian.png new file mode 100644 index 00000000..d20b2108 Binary files /dev/null and b/components/search/assets/handian.png differ diff --git a/components/search/assets/yedian.png b/components/search/assets/yedian.png new file mode 100644 index 00000000..520608d4 Binary files /dev/null and b/components/search/assets/yedian.png differ diff --git a/components/search/assets/zitong.svg b/components/search/assets/zitong.svg new file mode 100644 index 00000000..6c706f78 --- /dev/null +++ b/components/search/assets/zitong.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/components/search/share.ts b/components/search/share.ts new file mode 100644 index 00000000..8a2e7610 --- /dev/null +++ b/components/search/share.ts @@ -0,0 +1,109 @@ +export * from '../train/share' +import { HanziCard, ZigenCardMap, HanziCardMap, fetchJsonWithCache } from "../train/share"; +import { useRoute } from "vitepress"; + +// 用于缓存四角号码数据 +export let cache: Record = {} + +export interface ZigenAndKey { + zigen: string + key: string +} +/** 在Card.vue里使用 */ +export type ZigenAndKeyArray = ReadonlyArray +export type ReformatHandler = (hanziInfo: HanziCard, zigenMap: ZigenCardMap) => ZigenAndKeyArray + +/** 按照汉字信息里的拆分, 从字根map里找相应的大码, 作为编码, + * 要确保 hanzi 信息里有拆分数据 + */ +const easyReformatHandler: ReformatHandler = (hanziInfo, zigenMap) => [...hanziInfo.comp!].map( + zg => ({ + zigen: zg, + key: zigenMap.get(zg)!.key + }) +) + +/** 汉字信息里有key数据, 直接把字根和key封装起来, + * 如果编码比拆分长, 则把超出部分的编码设为空 */ +const readKeyReformatHandler: ReformatHandler = (hanziInfo, _) => { + const zigens = [...hanziInfo.comp!] + return [...hanziInfo.key!].map((key, i) => ({ + key, + zigen: zigens[i] || '', + })) +} + +export interface SearchCardsProps { + zi: string + data: ZigenAndKeyArray +} + +export type SearchCardsPropsArray = ReadonlyArray + +/** + * 用户输入一串字符串, 转换为一组 Card.vue用的数据 + */ +export function textToCardsProps(text: string, hanziMap: HanziCardMap, zigenMap?: ZigenCardMap, reformat?: ReformatHandler): SearchCardsPropsArray { + // 没有字根数据,只可能是汉字数据里有keys + if (!zigenMap) { + const fmt = reformat || readKeyReformatHandler + return [...text] + .filter(zi => hanziMap.has(zi)) + .map(zi => ({ + zi, + //@ts-expect-error + data: fmt(hanziMap.get(zi)!, undefined) + })) + } + const result: SearchCardsProps[] = [] + for (const zi of text) { + // 过滤没有字根数据的汉字 + if (!hanziMap.has(zi)) continue + const hanziData = hanziMap.get(zi)! + // 自定义的匹配方式, 只有奕码需要 + if (reformat) { + result.push({ zi, data: reformat(hanziData, zigenMap) }) + continue + } + // 默认的匹配方式 + if ('key' in hanziData) + result.push({ zi, data: readKeyReformatHandler(hanziData, zigenMap) }) + else + result.push({ zi, data: easyReformatHandler(hanziData, zigenMap) }) + } + return result +} + +/** 四角号码用的JSON文件处理 + * 补齐4码 + */ +export function prehandleJson(json: object) { + const result: Record = {} + for (const [k, v] of Object.entries(json)) { + result[k] = v + if (k.length > 4) { + const tempKey = k.slice(0, 4) + if (tempKey in result) { + result[tempKey] += v + } else { + result[tempKey] = v + } + } + } + return result +} + +/** 从vitepress的路由里推断当前使用的方案英文名称 */ +export function getSchemaNameFromRoute() { + const route = useRoute() + const routeSplit = route.path.split('/') + if (routeSplit[1] === 'yima') return routeSplit[2] + return routeSplit[1] +} + +/** 请求汉字、字根的JSON文件 如果没有填JSON名,会根据URl推断一个 */ +export async function useFetchJson(json: string | undefined, schemaName: string, jsonMainName: string) { + if (json) + return await fetchJsonWithCache(json) + return await fetchJsonWithCache(`/${schemaName}/${jsonMainName}.json`) as object[] +} diff --git a/components/train/CardLayout.vue b/components/train/CardLayout.vue new file mode 100644 index 00000000..6a398829 --- /dev/null +++ b/components/train/CardLayout.vue @@ -0,0 +1,61 @@ + + + \ No newline at end of file diff --git a/components/train/TrainHanzi.vue b/components/train/TrainHanzi.vue new file mode 100644 index 00000000..e9351bd1 --- /dev/null +++ b/components/train/TrainHanzi.vue @@ -0,0 +1,73 @@ + + + \ No newline at end of file diff --git a/components/train/TrainZigen.vue b/components/train/TrainZigen.vue new file mode 100644 index 00000000..d6cb45d5 --- /dev/null +++ b/components/train/TrainZigen.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/components/train/anki/TrainAnki.vue b/components/train/anki/TrainAnki.vue new file mode 100644 index 00000000..993d20dd --- /dev/null +++ b/components/train/anki/TrainAnki.vue @@ -0,0 +1,76 @@ + + + diff --git a/components/train/anki/sm2.ts b/components/train/anki/sm2.ts new file mode 100644 index 00000000..9b6764d2 --- /dev/null +++ b/components/train/anki/sm2.ts @@ -0,0 +1,169 @@ +/** + * 改编自 RemNote 的算法。 + * @see https://www.remnote.io/p/help/document/8g5xHDmGSotpAR9Pc + * + * 学习阶段会固定在 1min 6min 10min后进行。 -- 1 + * 当达到5级速度,进入指数复习阶段: + * + * 复习阶段回答错误,进入再学习阶段。 -- 2 + * + * 再学习阶段如同前述, 直到5级速度。 -- 3 + */ + +/** forget hard good easy */ +export type SmGrade = 0 | 1 | 2 | 3 + +export interface SmCard { + /** 学习、复习、再学习 */ + state: 1 | 2 | 3 + /** 回顾的时间戳 */ + due: number + /** 上一次复习的时间戳 */ + last: number + /** 容易度,指数阶段用 */ + ease: number + /** 困难分类值,指数阶段用*/ + difficulty?: number + /** 上一次指数阶段最后间隔(天), + * 只在 指数转再学习阶段写,在学习转指数结算读 */ + lastSpan?: number +} + +export const getTimeStamp = () => new Date().getTime() +const getThisMorning = () => { + const d = new Date() + // 凌晨当成上一天 + if (d.getHours() < 4) { + const t = d.getTime() + d.setTime(t - msPerDay / 2) + } + d.setHours(4, 0, 0, 0) + return d.getTime() +} +const thisMorningTimsStamp = getThisMorning() + +export const createCard = (): SmCard => ({ + state: 1, + due: getTimeStamp(), + last: getTimeStamp(), + ease: 2.3, +}) + +const msPerDay = 86400_000 + +const msToDay = (ms: number) => Math.round(ms / msPerDay) + +const dayToMs = (day: number) => day * msPerDay + +export function supermemo(card: T, grade: SmGrade): T { + const now = getTimeStamp() + const today = thisMorningTimsStamp + // 学习阶段 + if (card.state === 1) { + + if (grade === 3) { // 进入指数阶段 + const spanDays = msToDay(now - card.last) + 1 + const a = card.ease * 1.4 + const due = thisMorningTimsStamp + dayToMs(spanDays * a) + console.log('review after ' + Math.round(spanDays * a) + ' days.'); + return { + ...card, + state: 2, + due, + last: now, + ease: 2.3, + difficulty: 1 + } + } + + // 不熟练 + const reviewMinutes = [1, 6, 10] as const + const repetitionMs = reviewMinutes[grade] * 60 * 1000 + const due = now + repetitionMs + return { + ...card, + due + } + } + + // 指数阶段 + if (card.state === 2) { + // 再复习 + if (grade === 0) { + return { + ...card, + state: 3, + due: now + 6e4 /** 1 minute */, + last: now, + lastSpan: msToDay(now - card.last), + } + } + let ease: number, a: number + + switch (grade) { + case 1: + ease = card.ease - 0.15 + if (ease < 1.3) + ease = 1.3 + a = 1.2 + break; + case 2: + ease = card.ease + a = 2 + break; + default: + ease = card.ease + 0.15 + if (ease > 2.5) { + ease = 2.5 + } + a = ease * 0.14 + break; + } + + const spanDays = msToDay(today - card.last) + let delayDays = msToDay(card.due - today) + if (delayDays < 0) + delayDays = 0 + const repetitionDays = a * (spanDays + delayDays / card.difficulty!) + console.log(`review after ${Math.round(repetitionDays)} later.`); + const due = today + dayToMs(repetitionDays) + const difficulty = 1 << (3 - grade) + return { + ...card, + due, + last: today, + ease, + difficulty, + } + } + + // 再学习阶段 + + // 回到指数阶段 + if (grade === 3) { + let ease = card.ease + 0.15 + if (ease > 2.5) + ease = 2.5 + const a = ease * 1.4 + const spanDays = msToDay(now - card.last) + 1 + const repetitionDays = a * (spanDays + card.lastSpan! * 2) + console.log(`review after ${Math.round(repetitionDays)} later.`); + const due = thisMorningTimsStamp + dayToMs(repetitionDays) + return { + ...card, + state: 2, + due, + last: now, + ease, + difficulty: 1 + } + } + // 不熟练 + const reviewMinutes = [1, 6, 10] as const + const repetitionMs = reviewMinutes[grade] * 60 * 1000 + const due = now + repetitionMs + return { ...card, due } +} + + + diff --git a/components/train/anki/useAnki.ts b/components/train/anki/useAnki.ts new file mode 100644 index 00000000..d6bafd1e --- /dev/null +++ b/components/train/anki/useAnki.ts @@ -0,0 +1,110 @@ +import { supermemo, getTimeStamp, SmCard, SmGrade } from "./sm2"; +import { useLocalStorage } from "@vueuse/core"; +import { shallowRef } from "vue"; + +interface Card extends SmCard { + /** 用户列表相应的项目的索引号 */ + index: number +} + +const performanceNow = () => performance.now() + +export function useAnki(cards: T[], name: string) { + /** 推到未来复习的项目,只在内部使用 */ + const storageRef = useLocalStorage(`yima_${name}_anki`, []) + + /** 学习的进度,用户只读 */ + const progress = useLocalStorage(`yima_${name}_progress`, { + meet: 0, + familiar: 0, + }) + + /** 被用户使用的项目 */ + const card = shallowRef() + /** 用户学习的卡片, + * 如果从localstorage仓库来,那就是第一张 + * 如果从cards来,那就是第 progress.meet 张 */ + let isFromStorage: boolean + + let startTime = performanceNow() + + const setNextCard = () => { + const now = getTimeStamp() + storageRef.value.sort((a, b) => a.due - b.due) + const store = storageRef.value + // 仓库有没有到期的 + if (store.length && store[0].due <= now) { + isFromStorage = true + card.value = cards[store[0].index] + return; + } + // 有没有未学习的 + const meet = progress.value.meet + if (meet < cards.length) { + isFromStorage = false + card.value = cards[meet] + return; + } + // 被迫学习旧知识 + isFromStorage = true + card.value = cards[store[0].index] + } + + setNextCard() + + const answer = (right = true) => { + + //计算grade + let grade: SmGrade = 0 + if (right) { + const elapse = performanceNow() - startTime + if (elapse < 1000) + grade = 3 + else if (elapse < 2500) + grade = 2 + else + grade = 1 + } + + // 修改仓库数据 + if (isFromStorage) { + storageRef.value[0] = supermemo(storageRef.value[0], grade) + } else { + const now = getTimeStamp() + const newCard = supermemo({ + state: 1, + due: now, + last: now, + ease: 2.3, + index: progress.value.meet + }, grade) + storageRef.value.push(newCard) + progress.value.meet += 1 + } + + // 修改进度 + if (grade === 3) { + progress.value.familiar += 1 + } + + // 取出下一张卡片 + setNextCard() + + startTime = performanceNow() + + } + + const restart = () => { + storageRef.value = [] + progress.value.familiar = 0 + progress.value.meet = 0 + setNextCard() + } + + return { + progress, + answer, + card, + restart, + } +} \ No newline at end of file diff --git a/components/train/basic/TrainCard.vue b/components/train/basic/TrainCard.vue new file mode 100644 index 00000000..16f44813 --- /dev/null +++ b/components/train/basic/TrainCard.vue @@ -0,0 +1,80 @@ + + + diff --git a/components/train/basic/useReview.ts b/components/train/basic/useReview.ts new file mode 100644 index 00000000..a553bc9e --- /dev/null +++ b/components/train/basic/useReview.ts @@ -0,0 +1,79 @@ +import { useLocalStorage } from "@vueuse/core"; +import { shallowRef } from 'vue' + +type Record = [count: number, index: number] + +export function useReview(name: string, cards: readonly T[]) { + if (cards.length < 100) throw new Error(`卡片至少100张:${cards.length}`); + + const emptyRecord = () => Array.from({ length: cards.length }, (_, i) => [-1, i] as Record) + const storageRef = useLocalStorage(`yima_${name}_records`, emptyRecord) + + const cardLength = cards.length + if (storageRef.value.length < cardLength) { + for (let i = storageRef.value.length; i < cardLength; i++) { + storageRef.value.push([-1, i]) + } + } else if (storageRef.value.length > cardLength) { + storageRef.value = storageRef.value.filter(v => v[1] < cardLength) + } + + storageRef.value.sort((a, b) => { + if (a[0] === 8 && b[0] < 8) + return 1 + if (b[0] === 8 && a[0] < 8) + return -1 + return 0 + }) + + const scanProgress = () => storageRef.value.reduce((p, c) => p + Number(c[0] > 1), 0) + const progress = shallowRef(scanProgress()) + const card = shallowRef(cards[storageRef.value[0][1]]) + const isFirst = shallowRef(storageRef.value[0][0] === -1) + + const restart = () => { + storageRef.value = emptyRecord() + progress.value = 0 + isFirst.value = true + card.value = cards[0] + } + + const maxIndex = cards.length - 1 + const moveSteps = [3, 9, 21, 36, 60, 100]; + const maxMoveStepsIndex = moveSteps.length - 1 + + const answer = (correct: boolean) => { + if (!correct) { + if (storageRef.value[0][0] > 1) + progress.value -= 1 + storageRef.value[0][0] = -1 + isFirst.value = true + return + } + + const firstRecord = storageRef.value[0] + const firstCount = ++firstRecord[0] + if (firstCount === 2) { + progress.value += 1 + } + + // move first card to middle parts in storage + let step = 0 + // too much trained times, move the card to last + if (firstCount > maxMoveStepsIndex) { + firstRecord[0] = 8 + step = maxIndex + } else { + step = moveSteps[firstCount] + if (step > maxIndex) + step = maxIndex + } + + storageRef.value.copyWithin(0, 1, step + 1) + storageRef.value[step] = firstRecord + + card.value = cards[storageRef.value[0][1]] + isFirst.value = storageRef.value[0][0] === -1 + } + return { progress, card, isFirst, restart, answer } +} \ No newline at end of file diff --git a/components/train/share.ts b/components/train/share.ts new file mode 100644 index 00000000..365c93d6 --- /dev/null +++ b/components/train/share.ts @@ -0,0 +1,58 @@ +import { withBase } from "vitepress"; + +export const cache: Record = {} +const fetchCache: Record = {} + + +export interface ZigenCard { + /** 字根用字 */ + name: string; + /** 所在按键(大码) */ + key: string; + /** 相关的汉字 */ + rel: string; + /** 类型,易码用,笔划、二笔 */ + kind?: 'b' | 'eb' + /** 小码所在键,奕码用 */ + secondary?: string +} + +/** 汉字信息 */ +export interface HanziCard { + /** 汉字 */ + name: string, + /** 编码 */ + key?: string, + /** 拆分 */ + comp?: string, +} + +export type Card = ZigenCard | HanziCard + +/** 汉字 - 汉字信息的Map数据 */ +export type HanziCardMap = Map + +/** 字根 - 字根信息的Map数据 */ +export type ZigenCardMap = Map + +export async function fetchJsonWithCache(url: string) { + if (url in fetchCache) + return fetchCache[url] + + let urlFixed = url + if (url[0] === '/') { + urlFixed = withBase(url) + } + + try { + const req = await fetch(urlFixed) + const json = await req.json() + fetchCache[url] = json + return json + + } catch (error) { + if (error instanceof Error) + alert(`无法下载或解析《${url}》文件:${error.cause}`) + throw error + } +} \ No newline at end of file diff --git a/components/train/startConfette.js b/components/train/startConfette.js new file mode 100644 index 00000000..24d9cd07 --- /dev/null +++ b/components/train/startConfette.js @@ -0,0 +1,43 @@ + +import confetti from "canvas-confetti"; + +export function startConfette() { + var duration = 5000; + var animationEnd = Date.now() + duration; + var defaults = { + startVelocity: 30, + spread: 360, + ticks: 60, + zIndex: 0, + particleCount: 30, + shapes: [ + confetti.shapeFromText({ text: '快', scalar: 7 }), + confetti.shapeFromText({ text: '强', scalar: 8 }), + confetti.shapeFromText({ text: '😆', scalar: 4 }), + confetti.shapeFromText({ text: '🎉', scalar: 5 }), + confetti.shapeFromText({ text: '👍', scalar: 5 }), + ], + scalar: 2, + }; + + function randomInRange(min, max) { + return Math.random() * (max - min) + min; + } + + var interval = setInterval(function () { + var timeLeft = animationEnd - Date.now(); + + if (timeLeft <= 0) { + return clearInterval(interval); + } + + var particleCount = 50 * (timeLeft / duration); + // since particles fall down, start a bit higher than random + confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }); + confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }); + confetti({ + ...defaults, shapes: ['circle'], + particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } + }); + }, 250); +} \ No newline at end of file diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 0d1b755d..bb9d2c05 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -6,7 +6,7 @@ const base = "/jdh/"; export default defineConfig({ base, title: "简单鹤", - description: "简单鹤,简单的鹤,好喝的鹤", + description: "简单的鹤,好喝的鹤", themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ @@ -15,16 +15,15 @@ export default defineConfig({ sidebar: [ { - text: 'Examples', items: [ - { text: 'Markdown Examples', link: '/markdown-examples' }, - { text: 'Runtime API Examples', link: '/api-examples' } + { text: '教程', link: '/tutorial' }, + { text: '字根练习', link: '/gen'} ] } ], socialLinks: [ - { icon: 'github', link: 'https://github.com/vuejs/vitepress' } + { icon: 'github', link: 'https://github.com/Flauver/jdh/' } ] } }) diff --git a/docs/api-examples.md b/docs/api-examples.md deleted file mode 100644 index 6bd8bb5c..00000000 --- a/docs/api-examples.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -outline: deep ---- - -# Runtime API Examples - -This page demonstrates usage of some of the runtime APIs provided by VitePress. - -The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: - -```md - - -## Results - -### Theme Data -
{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
-``` - - - -## Results - -### Theme Data -
{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
- -## More - -Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). diff --git a/docs/gen.md b/docs/gen.md new file mode 100644 index 00000000..032b885f --- /dev/null +++ b/docs/gen.md @@ -0,0 +1,11 @@ +--- +aside: false +--- + + +# 简单鹤字根练习 + + \ No newline at end of file diff --git a/docs/high.ts b/docs/high.ts new file mode 100644 index 00000000..a1bb63be --- /dev/null +++ b/docs/high.ts @@ -0,0 +1,2 @@ +/** 高亮的字根 */ +export const high = "" \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 0bf10352..2287624a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,21 +5,18 @@ layout: home hero: name: "简单鹤" text: "简单的鹤,好喝的鹤" - tagline: 乱序字根,单字低重,字词不重,手感好的最强音形 + tagline: 乱序字根,单字低重,字词不重,手感好的音形 actions: - theme: brand - text: Markdown Examples - link: /markdown-examples - - theme: alt - text: API Examples - link: /api-examples + text: 教程 + link: /tutorial features: - - title: Feature A - details: Lorem ipsum dolor sit amet, consectetur adipiscing elit - - title: Feature B - details: Lorem ipsum dolor sit amet, consectetur adipiscing elit - - title: Feature C - details: Lorem ipsum dolor sit amet, consectetur adipiscing elit + - title: 乱序字根 + details: 使用字根记忆程序学习字根,最快能在一个小时内掌握简单鹤的字根 + - title: 单字低重 + details: 让你轻松盲打 + - title: 字词不重 + details: 简单鹤采用了极端的字词避重方式,在算码的时候尽量让更多的字处在三码位,剩下的四码单字则全部放在次选,好让四码首选永远可以畅快打词。 --- diff --git a/docs/markdown-examples.md b/docs/markdown-examples.md deleted file mode 100644 index f9258a55..00000000 --- a/docs/markdown-examples.md +++ /dev/null @@ -1,85 +0,0 @@ -# Markdown Extension Examples - -This page demonstrates some of the built-in markdown extensions provided by VitePress. - -## Syntax Highlighting - -VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: - -**Input** - -````md -```js{4} -export default { - data () { - return { - msg: 'Highlighted!' - } - } -} -``` -```` - -**Output** - -```js{4} -export default { - data () { - return { - msg: 'Highlighted!' - } - } -} -``` - -## Custom Containers - -**Input** - -```md -::: info -This is an info box. -::: - -::: tip -This is a tip. -::: - -::: warning -This is a warning. -::: - -::: danger -This is a dangerous warning. -::: - -::: details -This is a details block. -::: -``` - -**Output** - -::: info -This is an info box. -::: - -::: tip -This is a tip. -::: - -::: warning -This is a warning. -::: - -::: danger -This is a dangerous warning. -::: - -::: details -This is a details block. -::: - -## More - -Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). diff --git a/docs/public/jdh/zigen.json b/docs/public/jdh/zigen.json new file mode 100644 index 00000000..66624b6f --- /dev/null +++ b/docs/public/jdh/zigen.json @@ -0,0 +1 @@ +[{"name": "1", "rel": "", "key": "d"}, {"name": "2", "rel": "", "key": "p"}, {"name": "5", "rel": "", "key": "r"}, {"name": "4", "rel": "", "key": "h"}, {"name": "口", "rel": "", "key": "o"}, {"name": "3", "rel": "", "key": "u"}, {"name": "人 全字头 卧人 入", "rel": "", "key": "e"}, {"name": "土 里无日 士", "rel": "", "key": "g"}, {"name": "八 丷 冫", "rel": "", "key": "f"}, {"name": "日 冒字头 曰", "rel": "", "key": "v"}, {"name": "目 白 自 臼", "rel": "", "key": "s"}, {"name": "亻", "rel": "", "key": "x"}, {"name": "十 左字框", "rel": "", "key": "s"}, {"name": "月 青字底", "rel": "", "key": "l"}, {"name": "宀 亠 冖", "rel": "", "key": "i"}, {"name": "二 三", "rel": "", "key": "u"}, {"name": "小 少 拣字边无七 恭字底", "rel": "", "key": "i"}, {"name": "厶 龴", "rel": "", "key": "p"}, {"name": "辶 走 廴", "rel": "", "key": "q"}, {"name": "又", "rel": "", "key": "l"}, {"name": "大", "rel": "", "key": "x"}, {"name": "忄 心", "rel": "", "key": "n"}, {"name": "女", "rel": "", "key": "y"}, {"name": "扌", "rel": "", "key": "j"}, {"name": "匕 化字边 比字旁 东二 七 龙下角", "rel": "", "key": "a"}, {"name": "文 夂 攵", "rel": "", "key": "j"}, {"name": "子", "rel": "", "key": "a"}, {"name": "儿", "rel": "", "key": "k"}, {"name": "木", "rel": "", "key": "w"}, {"name": "丁 可", "rel": "", "key": "j"}, {"name": "王 龶 玉 丰 拜字边", "rel": "", "key": "a"}, {"name": "巴 己 㔾 巳 已", "rel": "", "key": "t"}, {"name": "寸", "rel": "", "key": "k"}, {"name": "卩 阝 节字底", "rel": "", "key": "b"}, {"name": "讠 音 言", "rel": "", "key": "c"}, {"name": "尔字头 负字头", "rel": "", "key": "g"}, {"name": "刀 刂", "rel": "", "key": "i"}, {"name": "力 为", "rel": "", "key": "u"}, {"name": "干 千 于", "rel": "", "key": "s"}, {"name": "冂 门 周字框", "rel": "", "key": "o"}, {"name": "夕 然左角 死 歹", "rel": "", "key": "f"}, {"name": "氵", "rel": "", "key": "k"}, {"name": "学字头 尚字头 爫 釆", "rel": "", "key": "h"}, {"name": "艹 带字头", "rel": "", "key": "z"}, {"name": "乂", "rel": "", "key": "y"}, {"name": "广 厂", "rel": "", "key": "r"}, {"name": "几 风省", "rel": "", "key": "i"}, {"name": "彐 录字头", "rel": "", "key": "s"}, {"name": "山", "rel": "", "key": "p"}, {"name": "彳", "rel": "", "key": "h"}, {"name": "未 失 夫 末 朱", "rel": "", "key": "e"}, {"name": "贝", "rel": "", "key": "m"}, {"name": "纟 丝", "rel": "", "key": "e"}, {"name": "囗", "rel": "", "key": "n"}, {"name": "立", "rel": "", "key": "b"}, {"name": "田", "rel": "", "key": "w"}, {"name": "马", "rel": "", "key": "z"}, {"name": "手 龵", "rel": "", "key": "o"}, {"name": "工", "rel": "", "key": "p"}, {"name": "西 覀", "rel": "", "key": "p"}, {"name": "灬", "rel": "", "key": "n"}, {"name": "戈 戋 弋 尧字头", "rel": "", "key": "a"}, {"name": "巾", "rel": "", "key": "y"}, {"name": "止 止变", "rel": "", "key": "r"}, {"name": "尸 户", "rel": "", "key": "q"}, {"name": "犭 犬", "rel": "", "key": "b"}, {"name": "牛 告字头 牜", "rel": "", "key": "b"}, {"name": "车 车旁", "rel": "", "key": "t"}, {"name": "方", "rel": "", "key": "f"}, {"name": "水 氺", "rel": "", "key": "o"}, {"name": "皿 罒", "rel": "", "key": "a"}, {"name": "廿 其字头 甘", "rel": "", "key": "v"}, {"name": "火", "rel": "", "key": "t"}, {"name": "竹头 竹", "rel": "", "key": "n"}, {"name": "勿 句", "rel": "", "key": "i"}, {"name": "衤 礻", "rel": "", "key": "h"}, {"name": "米", "rel": "", "key": "l"}, {"name": "足 足旁", "rel": "", "key": "g"}, {"name": "而", "rel": "", "key": "b"}, {"name": "虫 贵字头", "rel": "", "key": "f"}, {"name": "钅", "rel": "", "key": "d"}, {"name": "丬 北字旁", "rel": "", "key": "l"}, {"name": "电", "rel": "", "key": "e"}, {"name": "石", "rel": "", "key": "t"}, {"name": "雨 雨头", "rel": "", "key": "y"}, {"name": "麻", "rel": "", "key": "d"}, {"name": "衣", "rel": "", "key": "k"}, {"name": "气", "rel": "", "key": "s"}, {"name": "习", "rel": "", "key": "d"}, {"name": "疒", "rel": "", "key": "m"}, {"name": "鸟", "rel": "", "key": "c"}, {"name": "五", "rel": "", "key": "g"}, {"name": "册 扁字心", "rel": "", "key": "j"}, {"name": "鱼", "rel": "", "key": "q"}, {"name": "鬼", "rel": "", "key": "s"}, {"name": "舟", "rel": "", "key": "t"}, {"name": "付", "rel": "", "key": "w"}, {"name": "厉", "rel": "", "key": "e"}, {"name": "骨", "rel": "", "key": "c"}, {"name": "恭喜你,背完了简单鹤字根!", "rel": "", "key": "NULL"}] \ No newline at end of file diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 00000000..639a3005 --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,14 @@ +# 教程 + +## 入门 +### 观前提醒 +本教程面向已经学会小鹤音形的人 +### 简单鹤与小鹤音形的区别 +1. 简单鹤字根为乱序排列,非音托字根 +2. 简单鹤后两码依然取首末,但相交也拆 例:由 = 日 + 丨 +3. 简单鹤四码单字统一放在次选。简单鹤采用了极端的字词避重方式,在算码的时候尽量让更多的字处在三码位,剩下的四码单字则全部放在次选,好让四码首选永远可以畅快打词 + +### 入门步骤 +1. 使用字根练习器练习简单鹤字根 +2. 加入简单鹤QQ群 605928107 +3. 打文章练习 diff --git a/package.json b/package.json index 01ecaca2..2d428a46 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "docs:preview": "vitepress preview docs" }, "dependencies": { + "@vueuse/core": "^11.1.0", "vue": "^3.4.29" }, "devDependencies": { @@ -22,6 +23,7 @@ "@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue-jsx": "^4.0.0", "@vue/tsconfig": "^0.5.1", + "daisyui": "^4.12.12", "npm-run-all2": "^6.2.0", "typescript": "~5.4.0", "vite": "^5.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d138641..e2e65190 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@vueuse/core': + specifier: ^11.1.0 + version: 11.1.0(vue@3.5.8(typescript@5.4.5)) vue: specifier: ^3.4.29 version: 3.5.8(typescript@5.4.5) @@ -27,6 +30,9 @@ importers: '@vue/tsconfig': specifier: ^0.5.1 version: 0.5.1 + daisyui: + specifier: ^4.12.12 + version: 4.12.12(postcss@8.4.47) npm-run-all2: specifier: ^6.2.0 version: 6.2.3 @@ -720,6 +726,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + caniuse-lite@1.0.30001663: resolution: {integrity: sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==} @@ -759,9 +769,25 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-selector-tokenizer@0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + culori@3.3.0: + resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + daisyui@4.12.12: + resolution: {integrity: sha512-xmCZ4piuWOjhNyB0VDKczB5vKFCipTA7UxaZNOzCz6cT8kvWgv5BDtUo+Hk9gOFufByOlfuBdzLpfhY5GsebTQ==} + engines: {node: '>=16.9.0'} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -804,6 +830,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + focus-trap@7.6.0: resolution: {integrity: sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==} @@ -953,6 +982,12 @@ packages: engines: {node: '>=0.10'} hasBin: true + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + postcss@8.4.47: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} @@ -1884,6 +1919,8 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.24.0) + camelcase-css@2.0.1: {} + caniuse-lite@1.0.30001663: {} ccount@2.0.1: {} @@ -1920,8 +1957,26 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-selector-tokenizer@0.8.0: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + + cssesc@3.0.0: {} + csstype@3.1.3: {} + culori@3.3.0: {} + + daisyui@4.12.12(postcss@8.4.47): + dependencies: + css-selector-tokenizer: 0.8.0 + culori: 3.3.0 + picocolors: 1.1.0 + postcss-js: 4.0.1(postcss@8.4.47) + transitivePeerDependencies: + - postcss + de-indent@1.0.2: {} debug@4.3.7: @@ -1970,6 +2025,8 @@ snapshots: estree-walker@2.0.2: {} + fastparse@1.1.2: {} + focus-trap@7.6.0: dependencies: tabbable: 6.2.0 @@ -2104,6 +2161,11 @@ snapshots: pidtree@0.6.0: {} + postcss-js@4.0.1(postcss@8.4.47): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.47 + postcss@8.4.47: dependencies: nanoid: 3.3.7