fix template
This commit is contained in:
1 parent
c8207d6ee5
commit
00efde8f22
8 files changed
+538
No files matched your search
@@ -12,11 +12,13 @@
|
||||
"dependencies": {
|
||||
"axios": "^0.26.1",
|
||||
"axios-rate-limit": "^1.3.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nanoid": "^3.3.2",
|
||||
"wanakana": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/node": "^17.0.23",
|
||||
"@types/wanakana": "^4.0.3",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import path from 'path';
|
||||
|
||||
import { AnkiConnect, IAnkiConnectActions } from '@/ankiconnect';
|
||||
import {
|
||||
ensureDirSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
writeFileSync,
|
||||
} from 'fs-extra';
|
||||
|
||||
export class AnkiTemplateEditor {
|
||||
$connect = new AnkiConnect();
|
||||
|
||||
templateDir = 'template';
|
||||
rootDir = path.resolve(this.templateDir, this.modelName);
|
||||
styleFilename = 'style.css';
|
||||
|
||||
constructor(public modelName: string) {
|
||||
ensureDirSync(this.rootDir);
|
||||
}
|
||||
|
||||
async get() {
|
||||
const d = await this.$connect.api('modelTemplates', {
|
||||
modelName: this.modelName,
|
||||
});
|
||||
|
||||
Object.entries(d).map(([cardName, sideDict]) => {
|
||||
const folderDir = path.resolve(this.rootDir, cardName);
|
||||
ensureDirSync(folderDir);
|
||||
Object.entries(sideDict).map(([side, html]) => {
|
||||
writeFileSync(path.resolve(folderDir, side + '.html'), html);
|
||||
});
|
||||
});
|
||||
|
||||
const s = await this.$connect.api('modelStyling', {
|
||||
modelName: this.modelName,
|
||||
});
|
||||
|
||||
writeFileSync(path.resolve(this.rootDir, this.styleFilename), s.css);
|
||||
}
|
||||
|
||||
async set() {
|
||||
const templates: IAnkiConnectActions['updateModelTemplates']['params']['model']['templates'] =
|
||||
{};
|
||||
|
||||
for (const p of readdirSync(this.rootDir)) {
|
||||
if (p === this.styleFilename) {
|
||||
await this.$connect.api('updateModelStyling', {
|
||||
model: {
|
||||
name: this.modelName,
|
||||
css: readFileSync(
|
||||
path.resolve(this.rootDir, this.styleFilename),
|
||||
'utf-8',
|
||||
),
|
||||
},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
readdirSync(path.resolve(this.rootDir, p)).map((p1) => {
|
||||
const side = p1.replace(/\.[^\.]+$/, '') as 'Front' | 'Back';
|
||||
const sideMap = templates[p] || {};
|
||||
sideMap[side] = readFileSync(
|
||||
path.resolve(this.rootDir, p, p1),
|
||||
'utf-8',
|
||||
);
|
||||
templates[p] = sideMap;
|
||||
});
|
||||
}
|
||||
|
||||
await this.$connect.api('updateModelTemplates', {
|
||||
model: {
|
||||
name: this.modelName,
|
||||
templates,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
new AnkiTemplateEditor('jp.takoboto').set();
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
{{FrontSide}}
|
||||
|
||||
<hr id=answer>
|
||||
|
||||
{{#SentenceAudio}}
|
||||
{{SentenceAudio}}
|
||||
{{/SentenceAudio}}
|
||||
|
||||
{{^SentenceAudio}}
|
||||
{{#Sentence}}
|
||||
[sound:https://peaceful-brushlands-36451.herokuapp.com/api/tts?lang=ja&is_sentence=true&q={{kanji:Sentence}}]
|
||||
{{/Sentence}}
|
||||
{{/SentenceAudio}}
|
||||
|
||||
<p>{{furigana:Japanese}}</p>
|
||||
|
||||
{{#JapaneseAlt}}
|
||||
<p>{{JapaneseAlt}}</p>
|
||||
{{/JapaneseAlt}}
|
||||
|
||||
<p>
|
||||
{{#Pitch}}
|
||||
{{Pitch}}
|
||||
{{/Pitch}}
|
||||
|
||||
{{^Pitch}}
|
||||
{{Reading}}
|
||||
{{/Pitch}}
|
||||
|
||||
{{^JapaneseAudio}}
|
||||
<el-tts q="{{kanji:Japanese}}" />
|
||||
{{/JapaneseAudio}}
|
||||
|
||||
{{#JapaneseAudio}}
|
||||
<el-tts src="{{JapaneseAudio}}" />
|
||||
{{/JapaneseAudio}}
|
||||
</p>
|
||||
|
||||
{{#Sentence}}
|
||||
<p>
|
||||
<div>
|
||||
{{Sentence}}
|
||||
|
||||
{{^SentenceAudio}}
|
||||
<el-tts q="{{kanji:Sentence}}" is-sentence />
|
||||
{{/SentenceAudio}}
|
||||
|
||||
{{#SentenceAudio}}
|
||||
<el-tts src="{{SentenceAudio}}" />
|
||||
{{/SentenceAudio}}
|
||||
</div>
|
||||
{{SentenceMeaning}}
|
||||
</p>
|
||||
{{/Sentence}}
|
||||
|
||||
<details>
|
||||
<summary>References</summary>
|
||||
{{#Takoboto}}
|
||||
<a href="{{Takoboto}}">Takoboto</a>・
|
||||
{{/Takoboto}}
|
||||
{{#Akebi}}
|
||||
<a href="{{Akebi}}">Akebi</a>・
|
||||
{{/Akebi}}
|
||||
<a class="ext-link" id="alc">ALC</a>・
|
||||
<a class="ext-link" id="weblio">Weblio</a>・
|
||||
<a class="ext-link" id="goo">Goo</a>・
|
||||
<a class="ext-link" id="yourei">用例</a>・
|
||||
<a class="ext-link" id="youglish">YouGlish</a>・
|
||||
<a class="ext-link" id="ImmersionKit">ImmersionKit</a>・
|
||||
<a href="https://peaceful-brushlands-36451.herokuapp.com?lang=ja&q={{kanji:Japanese}}">Audios</a>
|
||||
</details>
|
||||
|
||||
{{#Mnemonic}}
|
||||
<details>
|
||||
<summary>Mnemonic</summary>
|
||||
<p>{{Mnemonic}}</p>
|
||||
</details>
|
||||
{{/Mnemonic}}
|
||||
|
||||
<script type="module">
|
||||
let linkEntries = {}
|
||||
try { linkEntries = JSON.parse('{{LinkEntries}}') } catch (e) { }
|
||||
|
||||
document.querySelectorAll('a.ext-link').forEach(a => {
|
||||
switch (a.id) {
|
||||
case 'alc':
|
||||
a.href = `https://eow.alc.co.jp/search?q=${encodeURIComponent(linkEntries.alc || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'weblio':
|
||||
a.href = `https://www.weblio.jp/content/${encodeURIComponent(linkEntries.weblio || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'goo':
|
||||
a.href = `https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent(linkEntries.goo || '{{kanji:Japanese}}')}/m1u/`
|
||||
break
|
||||
case 'yourei':
|
||||
a.href = `http://yourei.jp/${encodeURIComponent(linkEntries.yourei || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'youglish':
|
||||
a.href = `https://youglish.com/pronounce/${encodeURIComponent(linkEntries.youglish || '{{kanji:Japanese}}')}/japanese?`
|
||||
break
|
||||
case 'ImmersionKit':
|
||||
a.href = `https://v2.immersionkit.com/search?category=drama&keyword=${encodeURIComponent(linkEntries.ImmersionKit || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
function speak(q = '', lang = 'ja-JP') {
|
||||
if (window.AnkiDroidJS) {
|
||||
AnkiDroidJS.ankiTtsSetLanguage?.(lang)
|
||||
AnkiDroidJS.ankiTtsSpeak?.(q)
|
||||
}
|
||||
}
|
||||
|
||||
class TTSElement extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.attachShadow({ mode: 'open' })
|
||||
|
||||
this.wrapper = document.createElement('span')
|
||||
this.wrapper.innerText = '▶️'
|
||||
this.wrapper.style.cursor = 'pointer'
|
||||
this.shadowRoot.append(this.wrapper)
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
let src = this.getAttribute('src')
|
||||
if (!src) {
|
||||
const q = this.getAttribute('q')
|
||||
const isSentence = this.hasAttribute('is-sentence')
|
||||
if (q) {
|
||||
src = `https://peaceful-brushlands-36451.herokuapp.com/api/tts?lang=ja&is_sentence=${isSentence}&q=${encodeURIComponent(q)}`
|
||||
}
|
||||
}
|
||||
|
||||
const soundTag = {
|
||||
pre: '[sound:',
|
||||
post: ']'
|
||||
}
|
||||
if (src.startsWith(soundTag.pre) && src.endsWith(soundTag.post)) {
|
||||
src = src.substring(soundTag.pre, src.length - soundTag.post)
|
||||
}
|
||||
|
||||
try {
|
||||
if (src) {
|
||||
const elAudio = document.createElement('audio')
|
||||
|
||||
const setSrc = () => {
|
||||
const elSource = document.createElement('source')
|
||||
elSource.src = src
|
||||
elAudio.append(elSource)
|
||||
}
|
||||
|
||||
elAudio.style.display = 'none'
|
||||
this.wrapper.append(elAudio)
|
||||
|
||||
this.onclick = async () => {
|
||||
if (!elAudio.querySelector('source')) {
|
||||
setSrc()
|
||||
}
|
||||
elAudio.play()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.onclick = () => {
|
||||
speak(q)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
customElements.define('el-tts', TTSElement)
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
const removeHTML = (h) => {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = h
|
||||
return div.innerText
|
||||
}
|
||||
|
||||
const reJa = /[\p{sc=Han}\p{sc=Katakana}\p{sc=Hiragana}]/u
|
||||
document.querySelectorAll('.has-tts').forEach((el) => {
|
||||
const isSentence = el.classList.contains('is-sentence')
|
||||
const newEls = el.innerHTML.split(/<br *\/?>/g).map((t) => {
|
||||
t = removeHTML(t)
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.append(t)
|
||||
|
||||
if (reJa.test(t)) {
|
||||
const elTTS = document.createElement('el-tts')
|
||||
elTTS.setAttribute('q', t)
|
||||
if (isSentence) elTTS.setAttribute('is-sentence', 'true')
|
||||
div.append(elTTS)
|
||||
}
|
||||
|
||||
return div
|
||||
})
|
||||
|
||||
el.textContent = ''
|
||||
el.append(...newEls)
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,7 @@
|
||||
{{^MeaningQuiz}}
|
||||
<p>{{Meaning}}</p>
|
||||
{{/MeaningQuiz}}
|
||||
|
||||
{{#MeaningQuiz}}
|
||||
<p>{{MeaningQuiz}}</p>
|
||||
{{/MeaningQuiz}}
|
||||
@@ -0,0 +1,197 @@
|
||||
{{FrontSide}}
|
||||
|
||||
<hr id=answer>
|
||||
|
||||
{{#JapaneseAudio}}
|
||||
{{JapaneseAudio}}
|
||||
{{/JapaneseAudio}}
|
||||
|
||||
{{^JapaneseAudio}}
|
||||
[sound:https://peaceful-brushlands-36451.herokuapp.com/api/tts?lang=ja&q={{kanji:Japanese}}]
|
||||
{{/JapaneseAudio}}
|
||||
|
||||
<p>{{JapaneseAlt}}</p>
|
||||
|
||||
<p>
|
||||
{{#Pitch}}
|
||||
{{Pitch}}
|
||||
{{/Pitch}}
|
||||
|
||||
{{^Pitch}}
|
||||
{{Reading}}
|
||||
{{/Pitch}}
|
||||
</p>
|
||||
|
||||
<p>{{Meaning}}</p>
|
||||
|
||||
{{#Sentence}}
|
||||
<p>
|
||||
<div>
|
||||
{{furigana:Sentence}}
|
||||
|
||||
{{^SentenceAudio}}
|
||||
<el-tts q="{{kanji:Sentence}}" is-sentence />
|
||||
{{/SentenceAudio}}
|
||||
|
||||
{{#SentenceAudio}}
|
||||
<el-tts src="{{SentenceAudio}}" />
|
||||
{{/SentenceAudio}}
|
||||
</div>
|
||||
{{SentenceMeaning}}
|
||||
</p>
|
||||
{{/Sentence}}
|
||||
|
||||
<details>
|
||||
<summary>References</summary>
|
||||
{{#Takoboto}}
|
||||
<a href="{{Takoboto}}">Takoboto</a>・
|
||||
{{/Takoboto}}
|
||||
{{#Akebi}}
|
||||
<a href="{{Akebi}}">Akebi</a>・
|
||||
{{/Akebi}}
|
||||
<a class="ext-link" id="alc">ALC</a>・
|
||||
<a class="ext-link" id="weblio">Weblio</a>・
|
||||
<a class="ext-link" id="goo">Goo</a>・
|
||||
<a class="ext-link" id="yourei">用例</a>・
|
||||
<a class="ext-link" id="youglish">YouGlish</a>・
|
||||
<a class="ext-link" id="ImmersionKit">ImmersionKit</a>・
|
||||
<a href="https://peaceful-brushlands-36451.herokuapp.com?lang=ja&q={{kanji:Japanese}}">Audios</a>
|
||||
</details>
|
||||
|
||||
{{#Mnemonic}}
|
||||
<details>
|
||||
<summary>Mnemonic</summary>
|
||||
<p>{{Mnemonic}}</p>
|
||||
</details>
|
||||
{{/Mnemonic}}
|
||||
|
||||
<script type="module">
|
||||
let linkEntries = {}
|
||||
try { linkEntries = JSON.parse('{{LinkEntries}}') } catch (e) { }
|
||||
|
||||
document.querySelectorAll('a.ext-link').forEach(a => {
|
||||
switch (a.id) {
|
||||
case 'alc':
|
||||
a.href = `https://eow.alc.co.jp/search?q=${encodeURIComponent(linkEntries.alc || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'weblio':
|
||||
a.href = `https://www.weblio.jp/content/${encodeURIComponent(linkEntries.weblio || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'goo':
|
||||
a.href = `https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent(linkEntries.goo || '{{kanji:Japanese}}')}/m1u/`
|
||||
break
|
||||
case 'yourei':
|
||||
a.href = `http://yourei.jp/${encodeURIComponent(linkEntries.yourei || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
case 'youglish':
|
||||
a.href = `https://youglish.com/pronounce/${encodeURIComponent(linkEntries.youglish || '{{kanji:Japanese}}')}/japanese?`
|
||||
break
|
||||
case 'ImmersionKit':
|
||||
a.href = `https://v2.immersionkit.com/search?category=drama&keyword=${encodeURIComponent(linkEntries.ImmersionKit || '{{kanji:Japanese}}')}`
|
||||
break
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
function speak(q = '', lang = 'ja-JP') {
|
||||
if (window.AnkiDroidJS) {
|
||||
AnkiDroidJS.ankiTtsSetLanguage?.(lang)
|
||||
AnkiDroidJS.ankiTtsSpeak?.(q)
|
||||
}
|
||||
}
|
||||
|
||||
class TTSElement extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.attachShadow({ mode: 'open' })
|
||||
|
||||
this.wrapper = document.createElement('span')
|
||||
this.wrapper.innerText = '▶️'
|
||||
this.wrapper.style.cursor = 'pointer'
|
||||
this.shadowRoot.append(this.wrapper)
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
let src = this.getAttribute('src')
|
||||
if (!src) {
|
||||
const q = this.getAttribute('q')
|
||||
const isSentence = this.hasAttribute('is-sentence')
|
||||
if (q) {
|
||||
src = `https://peaceful-brushlands-36451.herokuapp.com/api/tts?lang=ja&is_sentence=${isSentence}=${encodeURIComponent(q)}`
|
||||
}
|
||||
}
|
||||
|
||||
const soundTag = {
|
||||
pre: '[sound:',
|
||||
post: ']'
|
||||
}
|
||||
if (src.startsWith(soundTag.pre) && src.endsWith(soundTag.post)) {
|
||||
src = src.substring(soundTag.pre, src.length - soundTag.post)
|
||||
}
|
||||
|
||||
try {
|
||||
if (src) {
|
||||
const elAudio = document.createElement('audio')
|
||||
|
||||
const setSrc = () => {
|
||||
const elSource = document.createElement('source')
|
||||
elSource.src = src
|
||||
elAudio.append(elSource)
|
||||
}
|
||||
|
||||
elAudio.style.display = 'none'
|
||||
this.wrapper.append(elAudio)
|
||||
|
||||
this.onclick = async () => {
|
||||
if (!elAudio.querySelector('source')) {
|
||||
setSrc()
|
||||
}
|
||||
elAudio.play()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.onclick = () => {
|
||||
speak(q)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
customElements.define('el-tts', TTSElement)
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
const removeHTML = (h) => {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = h
|
||||
return div.innerText
|
||||
}
|
||||
|
||||
const reJa = /[\p{sc=Han}\p{sc=Katakana}\p{sc=Hiragana}]/u
|
||||
document.querySelectorAll('.has-tts').forEach((el) => {
|
||||
const isSentence = el.classList.contains('is-sentence')
|
||||
const newEls = el.innerHTML.split(/<br *\/?>/g).map((t) => {
|
||||
t = removeHTML(t)
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.append(t)
|
||||
|
||||
if (reJa.test(t)) {
|
||||
const elTTS = document.createElement('el-tts')
|
||||
elTTS.setAttribute('q', t)
|
||||
if (isSentence) elTTS.setAttribute('is-sentence', 'true')
|
||||
div.append(elTTS)
|
||||
}
|
||||
|
||||
return div
|
||||
})
|
||||
|
||||
el.textContent = ''
|
||||
el.append(...newEls)
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1 @@
|
||||
<p>{{furigana:Japanese}}</p>
|
||||
@@ -0,0 +1,31 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap');
|
||||
|
||||
.card {
|
||||
font-family: 'Noto Sans JP';
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.low-pitch {
|
||||
text-decoration: none;
|
||||
border-bottom: 3px dashed #512da8;
|
||||
}
|
||||
|
||||
.high-pitch {
|
||||
font-weight: normal;
|
||||
border-left: 3px dashed #512da8;
|
||||
border-top: 3px dashed #512da8;
|
||||
border-right: 3px dashed #512da8;
|
||||
}
|
||||
|
||||
.high-pitch-unterminated {
|
||||
font-weight: normal;
|
||||
border-left: 3px dashed #512da8;
|
||||
border-top: 3px dashed #512da8;
|
||||
}
|
||||
@@ -936,6 +936,15 @@ fs-extra@^10.0.1:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
|
||||
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
|
||||
Reference in new issue
Block a user