mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-26 21:15:15 +00:00
commit
36be5f9714
|
@ -17,6 +17,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@popperjs/core": "^2.11.6",
|
"@popperjs/core": "^2.11.6",
|
||||||
|
"@vueuse/core": "^9.1.1",
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"bootstrap": "^5.2.3",
|
"bootstrap": "^5.2.3",
|
||||||
"bootstrap-vue-3": "^0.4.11",
|
"bootstrap-vue-3": "^0.4.11",
|
||||||
|
|
|
@ -12,7 +12,12 @@
|
||||||
content-cols="6"
|
content-cols="6"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<b-form-select id="input-bins" v-model="bins" size="sm" :options="binOptions"></b-form-select>
|
<b-form-select
|
||||||
|
id="input-bins"
|
||||||
|
v-model="settingsStore.profitDistributionBins"
|
||||||
|
size="sm"
|
||||||
|
:options="binOptions"
|
||||||
|
></b-form-select>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -67,11 +72,10 @@ export default defineComponent({
|
||||||
// console.log(profits);
|
// console.log(profits);
|
||||||
// const data = [[]];
|
// const data = [[]];
|
||||||
const binOptions = [10, 15, 20, 25, 50];
|
const binOptions = [10, 15, 20, 25, 50];
|
||||||
const bins = ref<number>(20);
|
|
||||||
const data = computed(() => {
|
const data = computed(() => {
|
||||||
const profits = props.trades.map((trade) => trade.profit_ratio);
|
const profits = props.trades.map((trade) => trade.profit_ratio);
|
||||||
|
|
||||||
return binData(profits, bins.value);
|
return binData(profits, settingsStore.profitDistributionBins);
|
||||||
});
|
});
|
||||||
|
|
||||||
const chartOptions = computed((): EChartsOption => {
|
const chartOptions = computed((): EChartsOption => {
|
||||||
|
@ -137,7 +141,7 @@ export default defineComponent({
|
||||||
return chartOptionsLoc;
|
return chartOptionsLoc;
|
||||||
});
|
});
|
||||||
console.log(chartOptions);
|
console.log(chartOptions);
|
||||||
return { settingsStore, chartOptions, bins, binOptions };
|
return { settingsStore, chartOptions, binOptions };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
35
src/shared/notifications.ts
Normal file
35
src/shared/notifications.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { showAlert } from '@/stores/alerts';
|
||||||
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
import { FTWsMessage, FtWsMessageTypes } from '@/types/wsMessageTypes';
|
||||||
|
|
||||||
|
export function showNotification(msg: FTWsMessage, botname: string) {
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
if (settingsStore.notifications && settingsStore.notifications[msg.type]) {
|
||||||
|
switch (msg.type) {
|
||||||
|
case FtWsMessageTypes.entryFill:
|
||||||
|
console.log('entryFill', msg);
|
||||||
|
showAlert(`${botname}: Entry fill for ${msg.pair} at ${msg.open_rate}`, 'success');
|
||||||
|
break;
|
||||||
|
case FtWsMessageTypes.exitFill:
|
||||||
|
console.log('exitFill', msg);
|
||||||
|
showAlert(`${botname}: Exit fill for ${msg.pair} at ${msg.open_rate}`, 'success');
|
||||||
|
break;
|
||||||
|
case FtWsMessageTypes.exitCancel:
|
||||||
|
console.log('exitCancel', msg);
|
||||||
|
showAlert(
|
||||||
|
`${botname}: Exit order cancelled for ${msg.pair} due to ${msg.reason}`,
|
||||||
|
'warning',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case FtWsMessageTypes.entryCancel:
|
||||||
|
console.log('entryCancel', msg);
|
||||||
|
showAlert(
|
||||||
|
`${botname}: Entry order cancelled for ${msg.pair} due to ${msg.reason}`,
|
||||||
|
'warning',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`${botname}: Message ${msg.type} not shown.`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -189,6 +189,17 @@ export class UserService {
|
||||||
return `${baseURL}${APIBASE}`;
|
return `${baseURL}${APIBASE}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getBaseWsUrl(): string {
|
||||||
|
const baseUrl = this.getBaseUrl();
|
||||||
|
if (baseUrl.startsWith('http://')) {
|
||||||
|
return baseUrl.replace('http://', 'ws://');
|
||||||
|
}
|
||||||
|
if (baseUrl.startsWith('https://')) {
|
||||||
|
return baseUrl.replace('https://', 'wss://');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call on startup to migrate old login info to new login
|
* Call on startup to migrate old login info to new login
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const useAlertsStore = defineStore('alerts', {
|
||||||
this.activeMessages.push(message);
|
this.activeMessages.push(message);
|
||||||
},
|
},
|
||||||
removeAlert(alert: AlertType) {
|
removeAlert(alert: AlertType) {
|
||||||
console.log('dismissed');
|
console.log('dismissed', alert);
|
||||||
this.activeMessages = this.activeMessages.filter((v) => v !== alert);
|
this.activeMessages = this.activeMessages.filter((v) => v !== alert);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,6 +38,9 @@ import {
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { showAlert } from './alerts';
|
import { showAlert } from './alerts';
|
||||||
|
import { useWebSocket } from '@vueuse/core';
|
||||||
|
import { FTWsMessage, FtWsMessageTypes } from '@/types/wsMessageTypes';
|
||||||
|
import { showNotification } from '@/shared/notifications';
|
||||||
|
|
||||||
export function createBotSubStore(botId: string, botName: string) {
|
export function createBotSubStore(botId: string, botName: string) {
|
||||||
const userService = useUserService(botId);
|
const userService = useUserService(botId);
|
||||||
|
@ -46,6 +49,7 @@ export function createBotSubStore(botId: string, botName: string) {
|
||||||
const useBotStore = defineStore(botId, {
|
const useBotStore = defineStore(botId, {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
|
websocketStarted: false,
|
||||||
isSelected: true,
|
isSelected: true,
|
||||||
ping: '',
|
ping: '',
|
||||||
botStatusAvailable: false,
|
botStatusAvailable: false,
|
||||||
|
@ -501,6 +505,7 @@ export function createBotSubStore(botId: string, botName: string) {
|
||||||
const { data } = await api.get('/show_config');
|
const { data } = await api.get('/show_config');
|
||||||
this.botState = data;
|
this.botState = data;
|
||||||
this.botStatusAvailable = true;
|
this.botStatusAvailable = true;
|
||||||
|
this.startWebSocket();
|
||||||
return Promise.resolve(data);
|
return Promise.resolve(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -806,7 +811,93 @@ export function createBotSubStore(botId: string, botName: string) {
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
_handleWebsocketMessage(ws, event: MessageEvent<any>) {
|
||||||
|
const msg: FTWsMessage = JSON.parse(event.data);
|
||||||
|
switch (msg.type) {
|
||||||
|
case FtWsMessageTypes.whitelist:
|
||||||
|
this.whitelist = msg.data;
|
||||||
|
break;
|
||||||
|
case FtWsMessageTypes.entryFill:
|
||||||
|
case FtWsMessageTypes.exitFill:
|
||||||
|
case FtWsMessageTypes.exitCancel:
|
||||||
|
case FtWsMessageTypes.entryCancel:
|
||||||
|
showNotification(msg, botName);
|
||||||
|
break;
|
||||||
|
case FtWsMessageTypes.newCandle:
|
||||||
|
console.log('exitFill', msg);
|
||||||
|
const [pair, timeframe] = msg.data;
|
||||||
|
// TODO: check for active bot ...
|
||||||
|
if (pair === this.selectedPair) {
|
||||||
|
// Reload pair candles
|
||||||
|
this.getPairCandles({ pair, timeframe, limit: 500 });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Unhandled events ...
|
||||||
|
console.log(`Received event ${(msg as any).type}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startWebSocket() {
|
||||||
|
if (
|
||||||
|
this.websocketStarted === true ||
|
||||||
|
this.botStatusAvailable === false ||
|
||||||
|
this.botApiVersion < 2.2
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { status, data, send, open, close, ws } = useWebSocket(
|
||||||
|
// 'ws://localhost:8080/api/v1/message/ws?token=testtoken',
|
||||||
|
`${userService.getBaseWsUrl()}/message/ws?token=${userService.getAccessToken()}`,
|
||||||
|
{
|
||||||
|
autoReconnect: {
|
||||||
|
delay: 10000,
|
||||||
|
// retries: 10
|
||||||
|
},
|
||||||
|
// heartbeat: {
|
||||||
|
// message: JSON.stringify({ type: 'ping' }),
|
||||||
|
// interval: 10000,
|
||||||
|
// },
|
||||||
|
onError: (ws, event) => {
|
||||||
|
console.log('onError', event, ws);
|
||||||
|
this.websocketStarted = false;
|
||||||
|
close();
|
||||||
|
},
|
||||||
|
onMessage: this._handleWebsocketMessage,
|
||||||
|
onConnected: () => {
|
||||||
|
console.log('subscribing');
|
||||||
|
this.websocketStarted = true;
|
||||||
|
const subscriptions = [
|
||||||
|
FtWsMessageTypes.whitelist,
|
||||||
|
FtWsMessageTypes.entryFill,
|
||||||
|
FtWsMessageTypes.exitFill,
|
||||||
|
FtWsMessageTypes.entryCancel,
|
||||||
|
FtWsMessageTypes.exitCancel,
|
||||||
|
/*'new_candle' /*'analyzed_df'*/
|
||||||
|
];
|
||||||
|
if (this.botApiVersion >= 2.21) {
|
||||||
|
subscriptions.push(FtWsMessageTypes.newCandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'subscribe',
|
||||||
|
data: subscriptions,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: FtWsMessageTypes.whitelist,
|
||||||
|
data: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return useBotStore();
|
return useBotStore();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { defineStore } from 'pinia';
|
||||||
import { getCurrentTheme, getTheme } from '@/shared/themes';
|
import { getCurrentTheme, getTheme } from '@/shared/themes';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { UiVersion } from '@/types';
|
import { UiVersion } from '@/types';
|
||||||
|
import { FtWsMessageTypes } from '@/types/wsMessageTypes';
|
||||||
|
|
||||||
const STORE_UI_SETTINGS = 'ftUISettings';
|
const STORE_UI_SETTINGS = 'ftUISettings';
|
||||||
|
|
||||||
|
@ -12,11 +13,12 @@ export enum OpenTradeVizOptions {
|
||||||
noOpenTrades = 'noOpenTrades',
|
noOpenTrades = 'noOpenTrades',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsType {
|
const notificationDefaults = {
|
||||||
openTradesInTitle?: string;
|
[FtWsMessageTypes.entryFill]: true,
|
||||||
timezone?: string;
|
[FtWsMessageTypes.exitFill]: true,
|
||||||
backgroundSync?: boolean;
|
[FtWsMessageTypes.entryCancel]: true,
|
||||||
}
|
[FtWsMessageTypes.exitCancel]: true,
|
||||||
|
};
|
||||||
|
|
||||||
export const useSettingsStore = defineStore('uiSettings', {
|
export const useSettingsStore = defineStore('uiSettings', {
|
||||||
// other options...
|
// other options...
|
||||||
|
@ -28,6 +30,8 @@ export const useSettingsStore = defineStore('uiSettings', {
|
||||||
currentTheme: getCurrentTheme(),
|
currentTheme: getCurrentTheme(),
|
||||||
uiVersion: 'dev',
|
uiVersion: 'dev',
|
||||||
useHeikinAshiCandles: false,
|
useHeikinAshiCandles: false,
|
||||||
|
notifications: notificationDefaults,
|
||||||
|
profitDistributionBins: 20,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
65
src/types/wsMessageTypes.ts
Normal file
65
src/types/wsMessageTypes.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
export enum FtWsMessageTypes {
|
||||||
|
whitelist = 'whitelist',
|
||||||
|
entryFill = 'entry_fill',
|
||||||
|
entryCancel = 'entry_cancel',
|
||||||
|
|
||||||
|
exitFill = 'exit_fill',
|
||||||
|
exitCancel = 'exit_cancel',
|
||||||
|
newCandle = 'new_candle',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FtWhitelistMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.whitelist;
|
||||||
|
data: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FtEntryFillMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.entryFill;
|
||||||
|
pair: string;
|
||||||
|
open_rate: number;
|
||||||
|
amount: number;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FtExitFillMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.exitFill;
|
||||||
|
pair: string;
|
||||||
|
open_rate: number;
|
||||||
|
amount: number;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FTEntryCancelMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.entryCancel;
|
||||||
|
pair: string;
|
||||||
|
reason: string;
|
||||||
|
direction: string;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FTExitCancelMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.exitCancel;
|
||||||
|
pair: string;
|
||||||
|
reason: string;
|
||||||
|
direction: string;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FtNewCandleMessage extends FtBaseWsMessage {
|
||||||
|
type: FtWsMessageTypes.newCandle;
|
||||||
|
/** Pair, timeframe, candletype*/
|
||||||
|
data: [string, string, string];
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FTWsMessage =
|
||||||
|
| FtWhitelistMessage
|
||||||
|
| FtEntryFillMessage
|
||||||
|
| FTEntryCancelMessage
|
||||||
|
| FtExitFillMessage
|
||||||
|
| FTExitCancelMessage
|
||||||
|
| FtNewCandleMessage;
|
|
@ -37,45 +37,47 @@
|
||||||
>Use Heikin Ashi candles.</b-form-checkbox
|
>Use Heikin Ashi candles.</b-form-checkbox
|
||||||
>
|
>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
<b-form-group description="Notifications">
|
||||||
|
<b-form-checkbox v-model="settingsStore.notifications[FtWsMessageTypes.entryFill]"
|
||||||
|
>Entry notifications</b-form-checkbox
|
||||||
|
>
|
||||||
|
<b-form-checkbox v-model="settingsStore.notifications[FtWsMessageTypes.exitFill]"
|
||||||
|
>Exit notifications</b-form-checkbox
|
||||||
|
>
|
||||||
|
<b-form-checkbox v-model="settingsStore.notifications[FtWsMessageTypes.entryCancel]"
|
||||||
|
>Entry Cancel notifications</b-form-checkbox
|
||||||
|
>
|
||||||
|
<b-form-checkbox v-model="settingsStore.notifications[FtWsMessageTypes.exitCancel]"
|
||||||
|
>Exit Cancel notifications</b-form-checkbox
|
||||||
|
>
|
||||||
|
</b-form-group>
|
||||||
</div>
|
</div>
|
||||||
</b-card>
|
</b-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
||||||
import { useLayoutStore } from '@/stores/layout';
|
import { useLayoutStore } from '@/stores/layout';
|
||||||
import { showAlert } from '@/stores/alerts';
|
import { showAlert } from '@/stores/alerts';
|
||||||
|
import { FtWsMessageTypes } from '@/types/wsMessageTypes';
|
||||||
|
|
||||||
export default defineComponent({
|
const settingsStore = useSettingsStore();
|
||||||
name: 'Settings',
|
const layoutStore = useLayoutStore();
|
||||||
setup() {
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const layoutStore = useLayoutStore();
|
|
||||||
|
|
||||||
const timezoneOptions = ['UTC', Intl.DateTimeFormat().resolvedOptions().timeZone];
|
const timezoneOptions = ['UTC', Intl.DateTimeFormat().resolvedOptions().timeZone];
|
||||||
const openTradesOptions = [
|
const openTradesOptions = [
|
||||||
{ value: OpenTradeVizOptions.showPill, text: 'Show pill in icon' },
|
{ value: OpenTradeVizOptions.showPill, text: 'Show pill in icon' },
|
||||||
{ value: OpenTradeVizOptions.asTitle, text: 'Show in title' },
|
{ value: OpenTradeVizOptions.asTitle, text: 'Show in title' },
|
||||||
{ value: OpenTradeVizOptions.noOpenTrades, text: "Don't show open trades in header" },
|
{ value: OpenTradeVizOptions.noOpenTrades, text: "Don't show open trades in header" },
|
||||||
];
|
];
|
||||||
|
|
||||||
//
|
//
|
||||||
const resetDynamicLayout = () => {
|
const resetDynamicLayout = () => {
|
||||||
layoutStore.resetTradingLayout();
|
layoutStore.resetTradingLayout();
|
||||||
layoutStore.resetDashboardLayout();
|
layoutStore.resetDashboardLayout();
|
||||||
showAlert('Layouts have been reset.');
|
showAlert('Layouts have been reset.');
|
||||||
};
|
};
|
||||||
return {
|
|
||||||
resetDynamicLayout,
|
|
||||||
settingsStore,
|
|
||||||
layoutStore,
|
|
||||||
timezoneOptions,
|
|
||||||
openTradesOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
27
yarn.lock
27
yarn.lock
|
@ -1228,6 +1228,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||||
|
|
||||||
|
"@types/web-bluetooth@^0.0.15":
|
||||||
|
version "0.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72"
|
||||||
|
integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==
|
||||||
|
|
||||||
"@types/yargs-parser@*":
|
"@types/yargs-parser@*":
|
||||||
version "20.2.1"
|
version "20.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
|
||||||
|
@ -1665,6 +1670,28 @@
|
||||||
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.2.6.tgz#23d85b81d05be36f12aa802459a7876457dec795"
|
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.2.6.tgz#23d85b81d05be36f12aa802459a7876457dec795"
|
||||||
integrity sha512-64zHtJZdG7V/U2L0j/z3Pt5bSygccI3xs+Kl7LB73AZK4MQ8WONJhqDQPK8leUFFA9CmmoJygeky7zcl2hX10A==
|
integrity sha512-64zHtJZdG7V/U2L0j/z3Pt5bSygccI3xs+Kl7LB73AZK4MQ8WONJhqDQPK8leUFFA9CmmoJygeky7zcl2hX10A==
|
||||||
|
|
||||||
|
"@vueuse/core@^9.1.1":
|
||||||
|
version "9.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.1.1.tgz#a5c09c33ccee58cfd53bc3ec2d5a0d304155529e"
|
||||||
|
integrity sha512-QfuaNWRDMQcCUwXylCyYhPC3ScS9Tiiz4J0chdwr3vOemBwRToSywq8MP+ZegKYFnbETzRY8G/5zC+ca30wrRQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/web-bluetooth" "^0.0.15"
|
||||||
|
"@vueuse/metadata" "9.1.1"
|
||||||
|
"@vueuse/shared" "9.1.1"
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
|
"@vueuse/metadata@9.1.1":
|
||||||
|
version "9.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.1.1.tgz#b3fe4b97e62096f7566cd8eb107c503998b2c9a6"
|
||||||
|
integrity sha512-XZ2KtSW+85LLHB/IdGILPAtbIVHasPsAW7aqz3BRMzJdAQWRiM/FGa1OKBwLbXtUw/AmjKYFlZJo7eOFIBXRog==
|
||||||
|
|
||||||
|
"@vueuse/shared@9.1.1":
|
||||||
|
version "9.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.1.1.tgz#811f47629e281a19013ae6dcdf11ed3e1e91e023"
|
||||||
|
integrity sha512-c+IfcOYmHiHqoEa3ED1Tbpue5GHmoUmTp8PtO4YbczthtY155Rt6DmWhjxMLXBF1Bcidagxljmp/7xtAzEHXLw==
|
||||||
|
dependencies:
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
abab@^2.0.3, abab@^2.0.5:
|
abab@^2.0.3, abab@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user