Ads
Write a chrome plugin in manifest version 3 with following requirement:
1. Yellow highlight a text (p, li, h1, h2, h3 etc.) when hovering, no highlight when moved out
2. When clicking into the highlighted text, convert the Japanese text into Hiragana by fetching the rest api using fetch API http://localhost:3000/convert which with the request and response format:
Request
{
"text": ["日本語の文章", "日本語の文章”, "日本語の文章"]
}
Response
{
"hiragana": [
"日本語(にほんご)の文章(ぶんしょう)",
"日本語(にほんご)の文章(ぶんしょう)",
"日本語(にほんご)の文章(ぶんしょう)"
]
}
3. Revert back to no Hiragana when clicking it again
given a saveToStorage function as:
const saveToStorage = async (kanji, hiragana) => {
const data = { hiragana, mastered: false, archived: false };
chrome.storage.local.set({ [kanji]: JSON.stringify(data) }, function () {
console.log("Data is stored for " + kanji);
});
};
create a function that takes an input as a japanese sentence like: 日本語(にほんご)の文章(ぶんしょう)
and get all the kanji and hiragana to pass to saveToStorage function
// Add yellow highlight on hover
const highlight = (event) => {
event.target.style.backgroundColor = "yellow";
};
// Remove yellow highlight on hover out
const unhighlight = (event) => {
event.target.style.backgroundColor = "";
};
// chrome.storage.local.clear(function() {
// console.log("Chrome storage local has been cleared.");
// });
chrome.storage.local.get(function(items) { console.log(items) });
// Convert Japanese text to Hiragana
const convertToHiragana = async (text) => {
const response = await fetch("http://localhost:3000/convert", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: [text] }),
});
const data = await response.json();
await extractKanjiAndHiragana(data.hiragana[0]);
return data.hiragana[0];
};
// Revert back to original text
const revertToOriginal = (event) => {
event.target.removeEventListener("click", revertToOriginal);
event.target.innerHTML = event.target.dataset.original;
event.target.addEventListener("click", addHiragana);
};
// Add Hiragana
const addHiragana = async (event) => {
if (!event.target.dataset.original) {
event.target.dataset.original = event.target.innerHTML;
}
event.target.innerHTML = await convertToHiragana(event.target.innerHTML);
event.target.removeEventListener("click", addHiragana);
event.target.addEventListener("click", revertToOriginal);
};
async function updateElements() {
const elements = document.querySelectorAll("p, li, h1, h2, h3");
for (const element of elements) {
element.innerHTML = await setTextColor(element.innerHTML);
element.addEventListener("mouseover", highlight);
element.addEventListener("mouseout", unhighlight);
element.addEventListener("click", addHiragana);
}
}
updateElements();
async function setTextColor(text) {
return new Promise((resolve) => {
let coloredText = text;
chrome.storage.local.get(null, function(result) {
let keys = Object.keys(result);
keys.sort((a, b) => b.length - a.length);
let foundKeys = [];
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let index = coloredText.indexOf(key);
if (key === "") {
continue;
}
let skip = false;
foundKeys.forEach(fKey => {
if (fKey.includes(key)) {
skip = true;
}
});
if (skip) {
continue;
}
if (index !== -1) {
foundKeys.push(key);
let data = result[key];
let color = data.mastered ? "green" : "orange";
let beforeText = coloredText.substring(0, index);
let afterText = coloredText.substring(index + key.length);
coloredText = beforeText + "<span style='color:" + color + "'>" + key + "</span>" + afterText;
}
}
console.log(coloredText)
resolve(coloredText);
});
});
}
const saveToStorage = async (kanji, hiragana) => {
const data = { hiragana, mastered: false, archived: false };
if (kanji === "") return;
chrome.storage.local.set({ [kanji]: JSON.stringify(data) }, function () {
console.log("Data is stored for " + kanji + " as " + hiragana);
});
};
const extractKanjiAndHiragana = async (sentence) => {
const kanjiRegex = /[\u4e00-\u9faf]/;
const hiraganaRegex = /[\u3040-\u309f]/;
let kanji = "";
let hiragana = "";
for (const char of sentence) {
if (char === "(") {
hiragana = "";
} else if (char === ")") {
await saveToStorage(kanji, hiragana);
kanji = "";
hiragana = "";
} else if (kanjiRegex.test(char)) {
kanji += char;
} else if (hiraganaRegex.test(char)) {
hiragana += char;
}
}
await saveToStorage(kanji, hiragana);
};
Ads
const translate = async (text) => {
const response = await fetch("http://localhost:3000/translate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
const data = await response.json();
return data.chinese.join("\n").replace(/<\/?span.*?>/g, "");
};
// Add Hiragana
const addHiragana = async (event) => {
if (!event.target.dataset.original) {
event.target.dataset.original = event.target.innerHTML;
}
event.target.innerHTML = await convertToHiragana(event.target.innerHTML);
event.target.innerHTML += '<br/>'
event.target.innerHTML += await translate(event.target.dataset.original);
event.target.removeEventListener("click", addHiragana);
event.target.addEventListener("click", revertToOriginal);
};
Ads
不過目的唔係考試
主要只係方便聽Youtube ,睇Twitter 同漫畫
所以諗住買d online course 學文法
再背好生字讀法就應該夠做
我有App Store 睇過下d 學生字app 好多都畀錢同埋都係考試取向
有chrome extension 係會拆平假名但我覺得咁依賴應該唔會令自己記到點讀
所以諗住整個chrome extension 係會save 低你睇過嘅字
再寫個簡單Flashcard App 等得閒嘅時候開黎記番個extension save 低/export嘅字
本身冇寫過chrome extension
開咗個101 睇下入面d tutorial
https://developer.chrome.com/docs/extensions/mv3/getstarted/
如果本身寫過下webapp 應該容易上手
之後隨手玩下ChatGPT 試下開咗個頭先
結果而言其實自己寫+copilot 少少輔助會好過慢慢寫requirement 落ChatGPT 叫佢gen
原因有幾個:
1. 應該training data 比較舊,manifest_version 成日出2,就算指明係要version 3,玩多兩玩都會係2嘅syntax 咁出比你
2. 佢好似會直接當nodejs 咁寫,會require/import module,就算係叫佢用webpack pack 做一個js 黎用,佢用嘅package 都未必用到落chrome extension,例如path 咁
3. Debug 基本上係冇咩用,都係要自己發現問題,再講埋希望點解決先會出到d 有用少少嘅野
所以玩咗2日放棄咗完全靠ChatGPT
不過用嚟起個頭其實都ok 嘅
只係再深入嘅嘢就唔好徙自己時間
暫時成果係咁:
Click 落去一段文字會自動show 假名
會save 低d 字落chrome storage
之後如果有字係之前記低過嘅話,會變橙色,變相可以test 自己係咪識讀個字再click 落去睇
之後應該會真係睇返d online course學咗d 基本日文知識
同埋搞咗Flashcard 先
怕爆字數
係Reply 再慢慢講同update 多d 點搞成舊野
同埋ChatGPT 過程