diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 4e764bc63..29a4c2d3d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -476,6 +476,9 @@ state: "状態"
sort: "ソート"
ascendingOrder: "昇順"
descendingOrder: "降順"
+scratchpad: "スクラッチパッド"
+scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
+output: "出力"
_theme:
explore: "テーマを探す"
diff --git a/package.json b/package.json
index 0214f8f3a..2c9e570b1 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"@koa/cors": "3.0.0",
"@koa/multer": "2.0.2",
"@koa/router": "8.0.8",
- "@syuilo/aiscript": "0.0.0",
+ "@syuilo/aiscript": "0.0.2",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.12.1",
"@types/cbor": "5.0.0",
@@ -250,6 +250,7 @@
"vue-marquee-text-component": "1.1.1",
"vue-meta": "2.3.3",
"vue-prism-component": "1.1.1",
+ "vue-prism-editor": "0.5.1",
"vue-router": "3.1.6",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.5.0",
diff --git a/src/client/app.vue b/src/client/app.vue
index 5523e1e75..f3f99fe28 100644
--- a/src/client/app.vue
+++ b/src/client/app.vue
@@ -156,7 +156,7 @@
+
+
diff --git a/src/client/router.ts b/src/client/router.ts
index 9644ede55..428be7ecc 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -48,6 +48,7 @@ export const router = new VueRouter({
{ path: '/my/antennas', component: page('my-antennas/index') },
{ path: '/my/apps', component: page('apps') },
{ path: '/preferences', component: page('preferences/index') },
+ { path: '/scratchpad', component: page('scratchpad') },
{ path: '/instance', component: page('instance/index') },
{ path: '/instance/emojis', component: page('instance/emojis') },
{ path: '/instance/users', component: page('instance/users') },
diff --git a/src/client/scripts/hotkey.ts b/src/client/scripts/hotkey.ts
index 7d1bb16e7..5f73aa58b 100644
--- a/src/client/scripts/hotkey.ts
+++ b/src/client/scripts/hotkey.ts
@@ -80,6 +80,7 @@ export default {
el._keyHandler = (e: KeyboardEvent) => {
const targetReservedKeys = document.activeElement ? ((document.activeElement as any)._misskey_reservedKeys || []) : [];
if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return;
+ if (document.activeElement && document.activeElement.attributes['contenteditable']) return;
for (const action of actions) {
const matched = match(e, action.patterns);