diff --git a/package-lock.json b/package-lock.json
index 56799bdd..fd78fee5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,20 +8,20 @@
"name": "@aresrpg/aresrpg-dapp",
"version": "6.1.1",
"dependencies": {
- "@aresrpg/aresrpg-engine": "2.6.3",
- "@aresrpg/aresrpg-protocol": "5.1.6",
- "@aresrpg/aresrpg-sdk": "5.0.7",
+ "@aresrpg/aresrpg-engine": "2.6.4",
+ "@aresrpg/aresrpg-protocol": "5.1.7",
+ "@aresrpg/aresrpg-sdk": "5.0.11",
"@aresrpg/aresrpg-world": "1.7.0",
"@bufbuild/buf": "1.47.2",
"@bufbuild/protobuf": "2.2.3",
"@bufbuild/protoc-gen-es": "2.2.3",
"@imengyu/vue3-context-menu": "1.4.4",
"@intlify/unplugin-vue-i18n": "6.0.1",
- "@mysten/enoki": "0.4.17",
- "@mysten/kiosk": "0.9.32",
- "@mysten/sui": "1.16.2",
- "@mysten/wallet-standard": "0.13.18",
- "@mysten/zksend": "0.12.8",
+ "@mysten/enoki": "0.4.18",
+ "@mysten/kiosk": "0.9.33",
+ "@mysten/sui": "1.17.0",
+ "@mysten/wallet-standard": "0.13.19",
+ "@mysten/zksend": "0.12.9",
"@vercel/analytics": "1.4.1",
"@vueuse/core": "12.0.0",
"bignumber.js": "9.1.2",
@@ -74,7 +74,7 @@
"@iconify-json/twemoji": "^1.2.1",
"@modyfi/vite-plugin-yaml": "^1.1.0",
"@types/dat.gui": "^0.7.13",
- "@types/node": "^22.10.1",
+ "@types/node": "^22.10.2",
"@types/three": "^0.170.0",
"@typescript-eslint/eslint-plugin": "^8.18.0",
"@typescript-eslint/parser": "^8.18.0",
@@ -174,9 +174,9 @@
}
},
"node_modules/@aresrpg/aresrpg-engine": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-engine/-/aresrpg-engine-2.6.3.tgz",
- "integrity": "sha512-a7X5YvfapG1CU6xC2iEMqAKVmYH+bgtYT2kbX7Hufyy+uQxq5zMGuRlDMUgJGY1UDLdHubGj/pC90wFwtN2y/w==",
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-engine/-/aresrpg-engine-2.6.4.tgz",
+ "integrity": "sha512-uyH6yKKxeRt0wEIJuFZ3/wfRcGudKrHYDvxUaGMXSp3oGhUgkw99CubFI9UJChn/ISuXpD94Vl5HxYj0UlJUxQ==",
"engines": {
"node": ">=20.0.0"
},
@@ -185,9 +185,9 @@
}
},
"node_modules/@aresrpg/aresrpg-protocol": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-protocol/-/aresrpg-protocol-5.1.6.tgz",
- "integrity": "sha512-Hbm4bs1//LZe2leuLPJNKwWBfelO8IPJ35B/ZYm8gSwLrJ6+R8n0yRjiRwPoxRKYf47mqVgyKwlsw1OlcCz5Mg==",
+ "version": "5.1.7",
+ "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-protocol/-/aresrpg-protocol-5.1.7.tgz",
+ "integrity": "sha512-iMrbposfpuF9Vp6nhqFH9Z6P39DjrKZDgI0Itxnv+Rn4LAiPs7f1CPpik4aBq+co/W6T5f6IBi4zGkaVpbp0cw==",
"license": "ISC",
"dependencies": {
"@bufbuild/buf": "^1.45.0",
@@ -200,14 +200,14 @@
}
},
"node_modules/@aresrpg/aresrpg-sdk": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-sdk/-/aresrpg-sdk-5.0.7.tgz",
- "integrity": "sha512-V+jg6eIOzMwq/0b6MX7+UCtFoTkNNA/bKELslTni2NopZvJf2LjjI8j30zC8orEIbUQForeg32Nv3/qlQfZGcQ==",
+ "version": "5.0.11",
+ "resolved": "https://registry.npmjs.org/@aresrpg/aresrpg-sdk/-/aresrpg-sdk-5.0.11.tgz",
+ "integrity": "sha512-o/Z5HAjKLzk6seJV/H86SkGL7X9S/nQgv4cgOH1YdeRDk+wCla6P5tFoyLNAwG2Jz43eSFiiXbCg51t24kyygQ==",
"license": "MIT",
"dependencies": {
"@hydre/pino-human": "1.3.0",
- "@mysten/kiosk": "^0.9.32",
- "@mysten/sui": "^1.16.2",
+ "@mysten/kiosk": "^0.9.33",
+ "@mysten/sui": "^1.17.0",
"bignumber.js": "^9.1.2",
"iterator-helper": "^1.3.4",
"lru-cache": "^11.0.2",
@@ -3315,12 +3315,12 @@
}
},
"node_modules/@mysten/enoki": {
- "version": "0.4.17",
- "resolved": "https://registry.npmjs.org/@mysten/enoki/-/enoki-0.4.17.tgz",
- "integrity": "sha512-1ZXY7NDr/szp5k3zJHaGYBLdcAw+dICUMevmbuZfWTSJwzwCbr8i7gsydMAmhJ672Rh5IWQ/7R77EkRQqfDG7g==",
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/@mysten/enoki/-/enoki-0.4.18.tgz",
+ "integrity": "sha512-THt5+8ZhCMMo1BntsT2EclOWs8OY4J+ZKU/zRs2jP6xk41QKpuDgURBg5Ye/bye3haOfln9CNuz0lDLO/8cY5Q==",
"license": "Apache-2.0",
"dependencies": {
- "@mysten/sui": "1.16.2",
+ "@mysten/sui": "1.17.0",
"@nanostores/react": "^0.7.2",
"jose": "^5.6.3",
"nanostores": "^0.10.3"
@@ -3339,21 +3339,21 @@
}
},
"node_modules/@mysten/kiosk": {
- "version": "0.9.32",
- "resolved": "https://registry.npmjs.org/@mysten/kiosk/-/kiosk-0.9.32.tgz",
- "integrity": "sha512-YRpZXubnnEaJTyRyYX1b3e2E2Tgv22H97i+gNo85OiebH7zAJ7V+vHpgHjJ8ZFcM9OiTY32fIRvrIBmDvx71Fg==",
+ "version": "0.9.33",
+ "resolved": "https://registry.npmjs.org/@mysten/kiosk/-/kiosk-0.9.33.tgz",
+ "integrity": "sha512-HifwzZ0JvhNFa0NsZMRe5zH73VW5llsGHZXJwEOv6s0kGgboaiX1/hAn2BLn0ZA34En2sro7S8rXK40MVkuUiA==",
"license": "Apache-2.0",
"dependencies": {
- "@mysten/sui": "1.16.2"
+ "@mysten/sui": "1.17.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@mysten/sui": {
- "version": "1.16.2",
- "resolved": "https://registry.npmjs.org/@mysten/sui/-/sui-1.16.2.tgz",
- "integrity": "sha512-1Nfm7iTs3IVsiCXFPrnci9Y7fP9ldtwNOTe7JRkaHTg58VRhSe/nhHOvt6UsYiswVPUBqlsSF73KER5MpirCvw==",
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/@mysten/sui/-/sui-1.17.0.tgz",
+ "integrity": "sha512-vL6QrH3l10dTatimPmz/feqMbYfEjvh8MPf3Xwn5tjuwDwBCS0ha1kdN+4vUpu6t0aCFviK+Df/vanORS8cbGQ==",
"license": "Apache-2.0",
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
@@ -3362,6 +3362,7 @@
"@noble/hashes": "^1.4.0",
"@scure/bip32": "^1.4.0",
"@scure/bip39": "^1.3.0",
+ "@simplewebauthn/typescript-types": "^7.4.0",
"@suchipi/femver": "^1.0.0",
"bech32": "^2.0.0",
"gql.tada": "^1.8.2",
@@ -3376,23 +3377,23 @@
}
},
"node_modules/@mysten/wallet-standard": {
- "version": "0.13.18",
- "resolved": "https://registry.npmjs.org/@mysten/wallet-standard/-/wallet-standard-0.13.18.tgz",
- "integrity": "sha512-f1NU51AgiRunTa7LJhJsQabbIzUT5vWpqxN847rU7HDwFhExBsXAxCkxQrzgXZc9fX/ZaoOVexpolGI1TC7Xjw==",
+ "version": "0.13.19",
+ "resolved": "https://registry.npmjs.org/@mysten/wallet-standard/-/wallet-standard-0.13.19.tgz",
+ "integrity": "sha512-+Rvdi/fDWKSYVuRyO9IvdDZ30S43NfU5jBt8C61QIDrtVBlIMNR3rLy9gTK3uCcCwdNQvwJcRAhWYFgZ1qQoKQ==",
"license": "Apache-2.0",
"dependencies": {
- "@mysten/sui": "1.16.2",
+ "@mysten/sui": "1.17.0",
"@wallet-standard/core": "1.0.3"
}
},
"node_modules/@mysten/zksend": {
- "version": "0.12.8",
- "resolved": "https://registry.npmjs.org/@mysten/zksend/-/zksend-0.12.8.tgz",
- "integrity": "sha512-qdSPamcrxhZ36KCXRwnPwAOMaOeHMD4QaMRh7XU1q4LBFmgV+K/3BLRGQQ4OtIvU2Ou7ErF/WlSjf+/c82szSw==",
+ "version": "0.12.9",
+ "resolved": "https://registry.npmjs.org/@mysten/zksend/-/zksend-0.12.9.tgz",
+ "integrity": "sha512-xr8PL8MnAAlSi9M0L9lFAbKPS3jViGeD3qMUaTYR4GSQ8BSp79q2XezKAGSEmAOryhx+5pxOAq+1ySy+baQwLw==",
"license": "Apache-2.0",
"dependencies": {
- "@mysten/sui": "1.16.2",
- "@mysten/wallet-standard": "0.13.18",
+ "@mysten/sui": "1.17.0",
+ "@mysten/wallet-standard": "0.13.19",
"mitt": "^3.0.1",
"nanostores": "^0.10.3",
"valibot": "^0.36.0"
@@ -3904,6 +3905,13 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/@simplewebauthn/typescript-types": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@simplewebauthn/typescript-types/-/typescript-types-7.4.0.tgz",
+ "integrity": "sha512-8/ZjHeUPe210Bt5oyaOIGx4h8lHdsQs19BiOT44gi/jBEgK7uBGA0Fy7NRsyh777al3m6WM0mBf0UR7xd4R7WQ==",
+ "deprecated": "This package has been renamed to @simplewebauthn/types. Please install @simplewebauthn/types instead to ensure you receive future updates.",
+ "license": "MIT"
+ },
"node_modules/@suchipi/femver": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz",
@@ -3976,9 +3984,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.10.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz",
- "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==",
+ "version": "22.10.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz",
+ "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index fbf1e245..ca3efde1 100644
--- a/package.json
+++ b/package.json
@@ -14,20 +14,20 @@
"publish:mainnet": "git tag mainnet-$(node -p -e \"require('./package.json').version\") && git push origin mainnet-$(node -p -e \"require('./package.json').version\")"
},
"dependencies": {
- "@aresrpg/aresrpg-engine": "2.6.3",
- "@aresrpg/aresrpg-protocol": "5.1.6",
- "@aresrpg/aresrpg-sdk": "5.0.7",
+ "@aresrpg/aresrpg-engine": "2.6.4",
+ "@aresrpg/aresrpg-protocol": "5.1.7",
+ "@aresrpg/aresrpg-sdk": "5.0.11",
"@aresrpg/aresrpg-world": "1.7.0",
"@bufbuild/buf": "1.47.2",
"@bufbuild/protobuf": "2.2.3",
"@bufbuild/protoc-gen-es": "2.2.3",
"@imengyu/vue3-context-menu": "1.4.4",
"@intlify/unplugin-vue-i18n": "6.0.1",
- "@mysten/enoki": "0.4.17",
- "@mysten/kiosk": "0.9.32",
- "@mysten/sui": "1.16.2",
- "@mysten/wallet-standard": "0.13.18",
- "@mysten/zksend": "0.12.8",
+ "@mysten/enoki": "0.4.18",
+ "@mysten/kiosk": "0.9.33",
+ "@mysten/sui": "1.17.0",
+ "@mysten/wallet-standard": "0.13.19",
+ "@mysten/zksend": "0.12.9",
"@vercel/analytics": "1.4.1",
"@vueuse/core": "12.0.0",
"bignumber.js": "9.1.2",
@@ -80,7 +80,7 @@
"@iconify-json/twemoji": "^1.2.1",
"@modyfi/vite-plugin-yaml": "^1.1.0",
"@types/dat.gui": "^0.7.13",
- "@types/node": "^22.10.1",
+ "@types/node": "^22.10.2",
"@types/three": "^0.170.0",
"@typescript-eslint/eslint-plugin": "^8.18.0",
"@typescript-eslint/parser": "^8.18.0",
diff --git a/src/app.vue b/src/app.vue
index 1b3a5928..d631286a 100644
--- a/src/app.vue
+++ b/src/app.vue
@@ -8,6 +8,8 @@ import deep_equal from 'fast-deep-equal';
import { decrease_loading, increase_loading } from './core/utils/loading.js';
import { get_spells } from './core/game/spells_per_class.js';
+import toast from './toast.js';
+import { translate } from './i18n.js';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// @ts-ignore
@@ -93,6 +95,7 @@ const admin = reactive({
const owned_tokens = ref([]);
const finished_crafts = ref([]);
const currently_listed_items_names = ref({});
+const recipes = ref([]);
provide('sidebar_reduced', sidebar_reduced);
provide('game_visible', game_visible);
@@ -123,7 +126,7 @@ provide('finished_crafts', finished_crafts);
provide('message_history', message_history);
provide('currently_listed_items_names', currently_listed_items_names);
-provide('recipes', ref([]));
+provide('recipes', recipes);
function update_all(
state,
@@ -173,6 +176,8 @@ function update_all(
if (sui.finished_crafts.length !== finished_crafts.value.length)
finished_crafts.value = sui.finished_crafts;
+ if (sui.recipes.length !== recipes.value.length) recipes.value = sui.recipes;
+
const all_ids = characters.map(c => c.id);
// @ts-ignore
diff --git a/src/assets/translations/en.yaml b/src/assets/translations/en.yaml
index e5406aae..26afcbe5 100644
--- a/src/assets/translations/en.yaml
+++ b/src/assets/translations/en.yaml
@@ -164,7 +164,6 @@ APP_MINT_CARD_VAPOREONTEXT: |
This Vaporeon is a pet which you can equip on AresRPG, he will follow you in your exploration and fights.
Feed him with some $HSUI daily, and he will increase your stats
-APP_RECIPE_REQUIREMENTS: Requirements
APP_RECIPE_TAILOR: Tailor
APP_RECIPE_CRAFT: Craft
APP_RECIPE_REVEAL: Reveal
@@ -178,6 +177,7 @@ APP_RECIPE_REVEALING: Revealing item
APP_RECIPE_REVEALED: Item revealed
APP_RECIPE_FAILED_TO_REVEAL: Failed to reveal item
APP_RECIPE_CLOSE: Close
+APP_RECIPE_LEARNED: New recipe learned! ({0})
APP_SERVER_ONLINE_PLAYERS: Online players
APP_SERVER_TOTAL_PLAYERS: Registered players
diff --git a/src/assets/translations/fr.yaml b/src/assets/translations/fr.yaml
index 6c173e23..a587a7e6 100644
--- a/src/assets/translations/fr.yaml
+++ b/src/assets/translations/fr.yaml
@@ -165,7 +165,6 @@ APP_MINT_CARD_VAPOREONTEXT: |
Ce Vaporeon est un familier que vous pouvez équiper sur AresRPG, il vous suivra dans vos explorations et combats.
Nourrissez-le quotidiennement avec du $HSUI, et il augmentera vos statistiques
-APP_RECIPE_REQUIREMENTS: Requis
APP_RECIPE_TAILOR: Tailleur
APP_RECIPE_CRAFT: Fabriquer
APP_RECIPE_REVEAL: Révéler
@@ -179,6 +178,7 @@ APP_RECIPE_REVEALING: Révélation de l'item
APP_RECIPE_REVEALED: Item révélé
APP_RECIPE_FAILED_TO_REVEAL: Échec de la révélation de l'item
APP_RECIPE_CLOSE: Fermer
+APP_RECIPE_LEARNED: Nouvelle recette apprise! ({0})
APP_SERVER_ONLINE_PLAYERS: Joueurs en ligne
APP_SERVER_TOTAL_PLAYERS: Joueurs enregistrés
diff --git a/src/assets/translations/jp.yaml b/src/assets/translations/jp.yaml
index b0b03ff4..b1a88823 100644
--- a/src/assets/translations/jp.yaml
+++ b/src/assets/translations/jp.yaml
@@ -163,7 +163,6 @@ APP_MINT_CARD_VAPOREONTEXT: |
このシャワーズは、AresRPGで装備できるペットで、探索や戦闘であなたに同行します。
毎日$HSUIで餌を与えると、ステータスが上がります。
-APP_RECIPE_REQUIREMENTS: 必要条件
APP_RECIPE_TAILOR: 裁縫師
APP_RECIPE_CRAFT: クラフト
APP_RECIPE_REVEAL: 明らかにする
@@ -177,6 +176,7 @@ APP_RECIPE_REVEALING: アイテムを明らかにしています
APP_RECIPE_REVEALED: アイテムが明らかになりました
APP_RECIPE_FAILED_TO_REVEAL: アイテムの明らかに失敗しました
APP_RECIPE_CLOSE: 閉じる
+APP_RECIPE_LEARNED: 新しいレシピを学びました! ({0})
APP_SERVER_ONLINE_PLAYERS: オンラインプレイヤー
APP_SERVER_TOTAL_PLAYERS: 登録プレイヤー
diff --git a/src/components/cards/item-inventory.vue b/src/components/cards/item-inventory.vue
index f50ad4fe..24fde414 100644
--- a/src/components/cards/item-inventory.vue
+++ b/src/components/cards/item-inventory.vue
@@ -91,8 +91,8 @@ async function use_item() {
});
tx.update('success', t('APP_ITEM_USED'), { digest });
} catch (error) {
- console.error(error);
- tx.update('error', t('APP_ITEM_FAILED_TO_USE'));
+ if (error) tx.update('error', t('APP_ITEM_FAILED_TO_USE'));
+ else tx.remove();
}
}
diff --git a/src/components/cards/market-listings.vue b/src/components/cards/market-listings.vue
index 87f7b5de..dddc1007 100644
--- a/src/components/cards/market-listings.vue
+++ b/src/components/cards/market-listings.vue
@@ -181,18 +181,28 @@ function on_listings_response(payload) {
select_item(items[0]);
}
+function on_item_listed({ item, price }) {
+ listings.value = [
+ ...listings.value,
+ {
+ ...item,
+ list_price: price,
+ },
+ ];
+}
+
onMounted(async () => {
context.events.on('packet/marketItemListings', on_listings_response);
SUI_EMITTER.on('ItemPurchasedEvent', on_item_purchased);
SUI_EMITTER.on('ItemDelistedEvent', on_item_delisted);
- SUI_EMITTER.on('ItemListedEvent', fetch_listings);
+ SUI_EMITTER.on('ItemListedEvent', on_item_listed);
});
onUnmounted(() => {
context.events.off('packet/marketItemListings', on_listings_response);
SUI_EMITTER.off('ItemPurchasedEvent', on_item_purchased);
SUI_EMITTER.off('ItemDelistedEvent', on_item_delisted);
- SUI_EMITTER.off('ItemListedEvent', fetch_listings);
+ SUI_EMITTER.off('ItemListedEvent', on_item_listed);
});
diff --git a/src/components/cards/recipe.vue b/src/components/cards/recipe.vue
index 7cf669ec..f2febd78 100644
--- a/src/components/cards/recipe.vue
+++ b/src/components/cards/recipe.vue
@@ -1,17 +1,16 @@
.recipe.material-2
.top
- img(:src="item_icon(recipe.template.item_type)")
+ img(:src="item_icon(recipe.item_type)")
.name
- span {{ recipe.template.name }}
- .job #[b {{ t('APP_RECIPE_REQUIREMENTS') }}]: {{ t('APP_RECIPE_TAILOR') }} {{ recipe.level }}
- .lvl Lvl. {{ recipe.template.level }}
+ span {{ recipe.name }}
+ .lvl Lvl. {{ recipe.level }}
.bottom
.ingredients
- vs-tooltip(v-for="ingredient in recipe.ingredients" :key="ingredient.item_type")
+ vs-tooltip(v-for="ingredient in recipe.ingredients" :key="ingredient.name")
template(#content) {{ ingredient.name }}
.ingredient
- img(:src="item_icon(ingredient.item_type)" :alt="ingredient.item_type" :class="{ token: ingredient.item_type.length > 20 }")
+ img(:src="item_icon(ingredient.item || ingredient.token)" :alt="ingredient.name" :class="{ token: !!ingredient.token }")
span x{{ pretty_amount(ingredient) }}
.btns
vs-button(v-if="admin.admin_caps.length" type="gradient" size="small" color="#F4511E" @click="delete_recipe") Delete
@@ -23,7 +22,7 @@
vs-dialog(v-model="craft_dialog")
template(#header) {{ t('APP_RECIPE_CRAFT_TITLE') }}
i18n-t(keypath="APP_RECIPE_CRAFT_DESC")
- b.itemname {{ recipe.template.name }} (Lvl. {{ recipe.template.level }})
+ b.itemname {{ recipe.name }} (Lvl. {{ recipe.level }})
template(#footer)
.dialog-footer
vs-button(type="transparent" color="#E74C3C" @click="craft_dialog = false") {{ t('APP_RECIPE_CANCEL') }}
diff --git a/src/components/game-ui/character-create.vue b/src/components/game-ui/character-create.vue
index 9f756f86..ceef4866 100644
--- a/src/components/game-ui/character-create.vue
+++ b/src/components/game-ui/character-create.vue
@@ -175,7 +175,7 @@ async function create_character() {
new_character_name.value = '';
} catch (error) {
if (error) tx.update('error', t('APP_CHARACTER_CREATE_ERROR'));
- console.error(error);
+ else tx.remove();
}
create_button_disabled.value = false;
}
diff --git a/src/components/game-ui/game-chat.vue b/src/components/game-ui/game-chat.vue
index 04053955..2d85c8b3 100644
--- a/src/components/game-ui/game-chat.vue
+++ b/src/components/game-ui/game-chat.vue
@@ -17,7 +17,11 @@
.text {{ message }}
.input
vs-button.canal(type="relief" color="#ECF0F1" size="small") general
- input(@keydown.stop="" v-model="current_message" @keyup.enter="send_message")
+ input(
+ @keydown.stop="handle_keydown"
+ v-model="current_message"
+ @keyup.enter="send_message"
+ )