<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift_JIS">
<title>音声入力</title>
<script>
function startSpeechRecognition() {
var recognition = new webkitSpeechRecognition(); // Chrome用のプレフィックス
var textBox = document.getElementById("myTextBox");
recognition.lang = 'ja-JP'; // 言語を日本語に設定
recognition.onresult = function(event) {
var transcript = event.results[0][0].transcript;
textBox.value += transcript; // 続けて入力するために += 演算子を使用
}
recognition.start();
}
function copyTextToClipboard() {
var textBox = document.getElementById("myTextBox");
textBox.select();
document.execCommand("copy");
// alert("テキストがクリップボードにコピーされました");
}
function clearTextBox() {
var textBox = document.getElementById("myTextBox");
textBox.value = "";
}
// ==================== 追加機能: 助詞・接続詞ボタン ====================
// items を持つグループはそのまま表示、subs を持つグループは1行にまとめて表示する。
var PARTICLE_GROUPS = [
// 助詞はすべて1つのグループにまとめる。分類はコメント順に残し、重複は1つに統合。
{ name: "助詞", items: [
// 格助詞
"が", "の", "を", "に", "へ", "と", "より", "から", "にて", "して",
// 接続助詞
"ば", "とも", "ど", "ども", "て", "で", "つつ", "ながら",
"ものの", "ものを", "ものから",
// 副助詞
"だに", "すら", "さへ", "し", "い", "のみ", "ばかり", "など", "まで",
// 係助詞
"は", "も", "ぞ", "なむ", "や", "か", "こそ",
// 終助詞
"な", "そ", "ばや", "しか", "てしか", "てもがな", "かな", "かし",
// 間投助詞
"よ"
]},
// よく使う定型文。必要な語はこの行に足してください。
{ name: "定型文", items: [
"お願い", "お願いします", "よろしく", "よろしくお願いします",
"ありがとう", "ありがとうございます", "続けてください。", "してください。",
"改行", "削除", "ご確認ください", "拝見しました"
]},
{ name: "接続詞", subs: [
{ name: "添加", items: [
"そして", "また", "さらに", "しかも", "ほかに", "もしくは"
]},
{ name: "逆接", items: [
"しかし", "でも", "けれど", "けれども", "ところが", "それでも", "ただし"
]},
{ name: "原因・理由", items: [
"だから", "ので", "から", "ため", "ゆえに"
]},
{ name: "条件", items: [
"もし", "なら"
]},
{ name: "目的", items: [
"ように", "ために"
]},
{ name: "順序・結論", items: [
"すると", "そこで", "したがって", "よって", "つまり"
]},
{ name: "例示", items: [
"たとえば", "例えば"
]}
]},
{ name: "句読点", items: [
"。", "、"
]},
{ name: "数字(半角)", items: [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
]},
{ name: "数字(漢数字)", items: [
"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"
]}
];
// カーソル位置に語句を挿入して、フォーカスをテキストエリアに戻す
function insertParticle(particle) {
var textBox = document.getElementById("myTextBox");
var start = textBox.selectionStart;
var end = textBox.selectionEnd;
var value = textBox.value;
textBox.value = value.substring(0, start) + particle + value.substring(end);
var pos = start + particle.length;
textBox.focus();
textBox.setSelectionRange(pos, pos);
}
// 直前の1文字を削除する
function deletePrevChar() {
var textBox = document.getElementById("myTextBox");
var start = textBox.selectionStart;
var end = textBox.selectionEnd;
var value = textBox.value;
if (start !== end) {
textBox.value = value.substring(0, start) + value.substring(end);
} else if (start > 0) {
textBox.value = value.substring(0, start - 1) + value.substring(start);
start = start - 1;
}
textBox.focus();
textBox.setSelectionRange(start, start);
}
// 改行をカーソル位置に挿入する
function insertNewLine() {
insertParticle("\n");
}
// 入力欄の文字列をカーソル位置に挿入する
function insertByInput() {
var field = document.getElementById("insertTarget");
var text = field.value;
if (!text) { field.focus(); return; }
insertParticle(text);
setFootMessage("「" + text + "」を入力しました");
}
// 指定した文字列をテキストから取り除く
// カーソルより手前にある出現を最優先し、無ければ後ろから最初の出現を消す。
function deleteByTarget() {
var field = document.getElementById("deleteTarget");
var target = field.value;
if (!target) { field.focus(); return; }
var textBox = document.getElementById("myTextBox");
var value = textBox.value;
var cursor = textBox.selectionStart;
var idx = -1;
var from = 0;
while (true) {
var found = value.indexOf(target, from);
if (found < 0 || found + target.length > cursor) break;
idx = found;
from = found + 1;
}
if (idx < 0) {
var ahead = value.indexOf(target, cursor);
if (ahead >= 0) idx = ahead;
}
if (idx < 0) {
setFootMessage("「" + target + "」が見つかりません");
field.focus();
field.select();
return;
}
textBox.value = value.substring(0, idx) + value.substring(idx + target.length);
textBox.focus();
textBox.setSelectionRange(idx, idx);
setFootMessage("「" + target + "」を削除しました");
}
// フッターの一時メッセージを表示する
function setFootMessage(msg) {
var el = document.getElementById("footMsg");
if (!el) return;
el.textContent = msg;
if (msg) {
clearTimeout(setFootMessage.timer);
setFootMessage.timer = setTimeout(function() { el.textContent = ""; }, 2500);
}
}
// ボタンを右側パネルに生成する
function buildParticleButtons() {
var container = document.getElementById("particleGroups");
if (!container) return;
container.innerHTML = "";
for (var g = 0; g < PARTICLE_GROUPS.length; g++) {
container.appendChild(createGroup(PARTICLE_GROUPS[g]));
}
}
// グループ1つを生成する
function createGroup(group) {
var details = document.createElement("details");
details.open = true;
var summary = document.createElement("summary");
summary.className = "groupTitle";
summary.appendChild(document.createTextNode(group.name + " (" + countItems(group) + ")"));
details.appendChild(summary);
if (group.subs) {
for (var s = 0; s < group.subs.length; s++) {
details.appendChild(createSubRow(group.subs[s]));
}
} else {
details.appendChild(createGrid(group.items));
}
return details;
}
// グループ内の語数を数える
function countItems(group) {
if (!group.subs) return group.items.length;
var n = 0;
for (var s = 0; s < group.subs.length; s++) {
n += group.subs[s].items.length;
}
return n;
}
// 通常の語ボタンを行内に並べる
function createGrid(items) {
var row = document.createElement("div");
row.className = "row";
for (var i = 0; i < items.length; i++) {
row.appendChild(createButton(items[i]));
}
return row;
}
// 見出しとボタンを同じ行に並べる
function createSubRow(group) {
var row = document.createElement("div");
row.className = "row subRow";
var label = document.createElement("span");
label.className = "subLabel";
label.appendChild(document.createTextNode(group.name));
row.appendChild(label);
for (var i = 0; i < group.items.length; i++) {
row.appendChild(createButton(group.items[i]));
}
return row;
}
// ボタン1個を生成する
function createButton(text) {
var btn = document.createElement("button");
btn.className = "pbtn";
btn.type = "button";
btn.appendChild(document.createTextNode(text));
btn.onclick = (function(word) {
return function() { insertParticle(word); };
})(text);
return btn;
}
window.addEventListener("load", buildParticleButtons);
</script>
<style>
/* 画面からはみ出さずに1画面に収める */
html {
height: 100%;
overflow: hidden;
}
body {
height: 100%;
margin: 0;
padding: 6px;
box-sizing: border-box;
font-family: "Meiryo", "MS PGothic", sans-serif;
display: flex;
flex-direction: column;
overflow: hidden;
}
.button {
display: inline-block;
margin-bottom: 10px;
margin-right: 10px;
}
.toolbar {
flex: 0 0 auto;
margin-bottom: 2px;
}
.main {
flex: 1 1 auto;
min-height: 0;
display: flex;
gap: 8px;
}
#myTextBox {
flex: 1 1 220px;
min-width: 200px;
height: 100%;
font-size: 16px;
box-sizing: border-box;
resize: none;
}
/* 助詞パネルは右側に置き、語ボタンを横に並べる */
#particlePanel {
flex: 1 1 640px;
min-width: 560px;
box-sizing: border-box;
border: 1px solid #999;
padding: 4px;
background: #fafafa;
display: flex;
flex-direction: column;
}
/* 画面が足りない場合のみスクロールする */
#particleGroups {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
.groupTitle {
font-weight: bold;
font-size: 13px;
padding: 1px 4px;
background: #e8eef5;
border: 1px solid #c4d2e0;
cursor: pointer;
user-select: none;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 3px;
padding: 1px 0;
}
.subLabel {
font-size: 12px;
font-weight: bold;
color: #444;
align-self: center;
min-width: 62px;
}
.pbtn {
font-family: inherit;
font-size: 14px;
line-height: 1.2;
padding: 2px 6px;
background: #fff;
border: 1px solid #888;
border-radius: 3px;
cursor: pointer;
min-width: 30px;
}
.pbtn:hover {
background: #fff3b0;
border-color: #b58900;
}
.pbtn:active {
background: #f6c445;
}
.panelFoot {
flex: 0 0 auto;
border-top: 1px solid #ddd;
padding-top: 2px;
}
.panelFoot .button {
margin-bottom: 2px;
margin-right: 0;
}
.footRow {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
}
.footLabel {
font-size: 12px;
color: #444;
margin-left: 6px;
}
.footInput {
font-family: inherit;
font-size: 13px;
padding: 2px 4px;
width: 130px;
box-sizing: border-box;
}
.footMsg {
font-size: 11px;
color: #a33;
margin-left: 6px;
}
</style>
</head>
<body>
<div class="toolbar">
<button class="button" onclick="clearTextBox()">クリア</button>
<button class="button" onclick="startSpeechRecognition()">音声入力を開始</button>
<button class="button" onclick="copyTextToClipboard()">テキストをコピー</button>
</div>
<div class="main">
<textarea id="myTextBox"></textarea>
<div id="particlePanel">
<div id="particleGroups"></div>
<div class="panelFoot">
<div class="footRow">
<button class="button" onclick="insertNewLine()">改行</button>
<button class="button" onclick="deletePrevChar()">1文字削除</button>
<label class="footLabel" for="insertTarget">入力する文字列</label>
<input type="text" id="insertTarget" class="footInput" placeholder="入れる語句を入力" onkeydown="if (event.key === 'Enter') insertByInput();">
<button class="button" onclick="insertByInput()">挿入</button>
<label class="footLabel" for="deleteTarget">削除する文字列</label>
<input type="text" id="deleteTarget" class="footInput" placeholder="消す語句を入力" onkeydown="if (event.key === 'Enter') deleteByTarget();">
<button class="button" onclick="deleteByTarget()">削除</button>
<span id="footMsg" class="footMsg"></span>
</div>
</div>
</div>
</div>
</body>
</html>
ディスカッション
コメント一覧
まだ、コメントがありません