switch to typescript on wails.io
This commit is contained in:
81
app/frontend/.eslintrc-auto-import.json
Normal file
81
app/frontend/.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"ShallowRef": true,
|
||||
"Slot": true,
|
||||
"Slots": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"computed": true,
|
||||
"createApp": true,
|
||||
"customRef": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"defineStore": true,
|
||||
"effectScope": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"getCurrentWatcher": true,
|
||||
"h": true,
|
||||
"inject": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"isShallow": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"provide": true,
|
||||
"reactive": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"resolveComponent": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"storeToRefs": true,
|
||||
"toRaw": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"unref": true,
|
||||
"useAttrs": true,
|
||||
"useCssModule": true,
|
||||
"useCssVars": true,
|
||||
"useId": true,
|
||||
"useModel": true,
|
||||
"useSlots": true,
|
||||
"useTemplateRef": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
||||
3
app/frontend/eslint.config.js
Normal file
3
app/frontend/eslint.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import vuetify from 'eslint-config-vuetify'
|
||||
|
||||
export default vuetify()
|
||||
13
app/frontend/index.html
Normal file
13
app/frontend/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>IrChad</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./src/main.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
7169
app/frontend/package-lock.json
generated
Normal file
7169
app/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
app/frontend/package.json
Normal file
36
app/frontend/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "irchad",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "^5.2.9",
|
||||
"@mdi/font": "^7.4.47",
|
||||
"buffer": "^6.0.3",
|
||||
"irc-framework": "^4.14.0",
|
||||
"pinia": "^3.0.4",
|
||||
"vite-plugin-node-polyfills": "^0.25.0",
|
||||
"vue": "^3.2.37",
|
||||
"vuetify": "^3.11.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.18.10",
|
||||
"@tsconfig/node22": "^22.0.5",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-vuetify": "^4.3.5-beta.1",
|
||||
"sass-embedded": "^1.97.2",
|
||||
"typescript": "^5.9.3",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-vue-components": "^31.0.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-vuetify": "^2.1.2",
|
||||
"vue-tsc": "^3.2.2"
|
||||
}
|
||||
}
|
||||
1
app/frontend/package.json.md5
Executable file
1
app/frontend/package.json.md5
Executable file
@@ -0,0 +1 @@
|
||||
d86c613036f8e3460cc743ac40acec6e
|
||||
14
app/frontend/src/App.vue
Normal file
14
app/frontend/src/App.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<Login v-if="!ircStore.connected" />
|
||||
<Chat v-else />
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import Chat from "@/components/Chat.vue";
|
||||
import Login from "@/components/Login.vue";
|
||||
|
||||
const ircStore = useIRCStore();
|
||||
</script>
|
||||
24
app/frontend/src/components/BufferList.vue
Normal file
24
app/frontend/src/components/BufferList.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import { useBufferStore } from "@/stores/bufferStore";
|
||||
const { setActiveBuffer, buffers, activeBufferName } = useBufferStore();
|
||||
|
||||
const store = useBufferStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-list
|
||||
selectable
|
||||
:selected="[activeBufferName]"
|
||||
@click:select="(item) => setActiveBuffer(item.id)"
|
||||
>
|
||||
<v-list-item :value="bufName" v-for="(bufValue, bufName) in buffers">
|
||||
{{ bufName }}
|
||||
<template v-slot:append>
|
||||
<v-badge
|
||||
:content="bufValue.messages.length - bufValue.lastSeenIdx"
|
||||
inline
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
64
app/frontend/src/components/Chat.vue
Normal file
64
app/frontend/src/components/Chat.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import { useBufferStore } from "@/stores/bufferStore";
|
||||
import { useAccountStore } from "@/stores/accountStore";
|
||||
|
||||
const bufferStore = useBufferStore();
|
||||
const ircStore = useIRCStore();
|
||||
const accountStore = useAccountStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex flex-row" style="height: 100vh">
|
||||
<v-sheet border class="buffers">
|
||||
<UserCard />
|
||||
<v-divider />
|
||||
<BufferList />
|
||||
</v-sheet>
|
||||
<div class="messages d-flex flex-column">
|
||||
<v-toolbar density="compact">
|
||||
<v-toolbar-title>
|
||||
<p>{{ bufferStore.activeBufferName }}</p>
|
||||
{{ bufferStore.activeBuffer?.topic }}
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<MessageList
|
||||
:messages="bufferStore.activeBuffer?.messages"
|
||||
:me="accountStore.account.nick"
|
||||
/>
|
||||
<v-sheet>
|
||||
<!-- <v-text-field -->
|
||||
<!-- variant="outlined" -->
|
||||
<!-- :placeholder="`Message ${store.activeBufferName}`" -->
|
||||
<!-- v-model="inputBuffer" -->
|
||||
<!-- hide-details -->
|
||||
<!-- class="ma-2" -->
|
||||
<!-- @keydown.enter.exact.prevent="send" -->
|
||||
<!-- /> -->
|
||||
|
||||
<InputBuffer @send="ircStore.sendActiveBuffer" />
|
||||
</v-sheet>
|
||||
</div>
|
||||
<v-sheet class="user-list h-100" border>
|
||||
<UserList :users="bufferStore.activeBuffer?.users" />
|
||||
</v-sheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.buffers {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.messages {
|
||||
height: 100%;
|
||||
flex: 3;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
104
app/frontend/src/components/InputBuffer.vue
Normal file
104
app/frontend/src/components/InputBuffer.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import { useBufferStore } from "@/stores/bufferStore";
|
||||
const emit = defineEmits(["send"]);
|
||||
|
||||
const store = useIRCStore();
|
||||
const bufferStore = useBufferStore();
|
||||
const text = ref();
|
||||
const menu = ref({
|
||||
open: false,
|
||||
activator: null,
|
||||
});
|
||||
|
||||
const menuList = ref({
|
||||
density: "compact",
|
||||
slim: true,
|
||||
items: [],
|
||||
itemTitle: "title",
|
||||
itemValue: "value",
|
||||
selected: [],
|
||||
selectable: true,
|
||||
mandatory: true,
|
||||
returnObject: true,
|
||||
});
|
||||
|
||||
const cursorPos = ref(0);
|
||||
const completionPos = ref(0);
|
||||
|
||||
function clickItem() {}
|
||||
function send() {
|
||||
if (!text.value) return;
|
||||
emit("send", text.value);
|
||||
menu.value.open = false;
|
||||
text.value = "";
|
||||
}
|
||||
|
||||
function filterUsers(s) {
|
||||
if (store.activeBuffer) {
|
||||
return store.activeBuffer.users.filter((u) => u.nick.startsWith(s));
|
||||
}
|
||||
}
|
||||
|
||||
function trigger(ev) {
|
||||
const input = ev.target;
|
||||
const cursor = input.selectionStart;
|
||||
cursorPos.value = cursor;
|
||||
const text = input.value;
|
||||
|
||||
const textBefore = text.slice(0, cursor);
|
||||
const mentionMatch = textBefore.match(/@(\w*)$/);
|
||||
if (mentionMatch) {
|
||||
menu.value.open = true;
|
||||
menuList.value.items = filterUsers(mentionMatch[1]);
|
||||
menuList.value.itemTitle = "nick";
|
||||
menuList.value.itemValue = "nick";
|
||||
} else {
|
||||
menu.value.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
function tabComplete() {
|
||||
if (!menu.value.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nextSelection = 0;
|
||||
if (menuList.value.selected.length) {
|
||||
const currentIdx = menuList.value.items.indexOf(menuList.value.selected[0]);
|
||||
const nextIdx = currentIdx + 1;
|
||||
if (menuList.value.items[nextIdx]) nextSelection = nextIdx;
|
||||
}
|
||||
|
||||
menuList.value.selected = [menuList.value.items[nextSelection]];
|
||||
|
||||
// hello @
|
||||
|
||||
const beforeCursor = text.value.splice(0, cursor);
|
||||
const afterCursor = text.value.slice(cursor);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<v-menu
|
||||
v-model="menu.open"
|
||||
location="top"
|
||||
activator="parent"
|
||||
:open-on-click="false"
|
||||
:open-on-focus="false"
|
||||
>
|
||||
<v-list v-bind="menuList" @click:select="clickItem" />
|
||||
</v-menu>
|
||||
<v-text-field
|
||||
v-model="text"
|
||||
autofocus
|
||||
hide-details
|
||||
:placeholder="`Message ${bufferStore.activeBufferName}`"
|
||||
@input="trigger"
|
||||
@keydown.enter.prevent="send"
|
||||
@keydown.tab.prevent="tabComplete"
|
||||
variant="outlined"
|
||||
class="ma-1"
|
||||
role="irchad"
|
||||
></v-text-field>
|
||||
</template>
|
||||
56
app/frontend/src/components/Login.vue
Normal file
56
app/frontend/src/components/Login.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccountStore } from "@/stores/accountStore";
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import { storeToRefs } from "pinia";
|
||||
const accountStore = useAccountStore();
|
||||
const ircStore = useIRCStore();
|
||||
|
||||
const { account } = storeToRefs(accountStore);
|
||||
const withAccount = ref(false);
|
||||
const form = ref(false);
|
||||
|
||||
function login() {
|
||||
ircStore.connect();
|
||||
}
|
||||
|
||||
function required(v: any) {
|
||||
return !!v || "This field is required";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="w-25 mt-5 ma-auto">
|
||||
<v-card title="Login to IrChad">
|
||||
<v-form @submit.prevent="login" v-model="form">
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="account.nick"
|
||||
label="Nickname"
|
||||
:rules="[required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-if="withAccount"
|
||||
v-model="account.account"
|
||||
label="Username"
|
||||
role="username"
|
||||
:rules="[required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="account.password"
|
||||
v-if="withAccount"
|
||||
label="Password"
|
||||
type="password"
|
||||
:rules="[required]"
|
||||
/>
|
||||
<v-alert color="error" v-if="accountStore.authError.reason">
|
||||
{{ accountStore.authError.message }}
|
||||
</v-alert>
|
||||
<v-checkbox v-model="withAccount" label="Login with an account" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn type="submit" color="success" :disabled="!form">Connect</v-btn>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card>
|
||||
</main>
|
||||
</template>
|
||||
73
app/frontend/src/components/MessageList.vue
Normal file
73
app/frontend/src/components/MessageList.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, watch, useTemplateRef } from "vue";
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
const props = defineProps(["messages", "me"]);
|
||||
|
||||
const store = useIRCStore();
|
||||
const messagesReverse = computed(() => {
|
||||
if (props.messages) {
|
||||
return [...props.messages];
|
||||
}
|
||||
});
|
||||
|
||||
const timeFormatter = new Intl.DateTimeFormat("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
});
|
||||
|
||||
function formatTime(ts: string) {
|
||||
const date = new Date(ts);
|
||||
return timeFormatter.format(date);
|
||||
}
|
||||
|
||||
const chatHistory = useTemplateRef("chat-scrollback");
|
||||
watch(
|
||||
() => props.messages,
|
||||
() =>
|
||||
nextTick(() => {
|
||||
chatHistory.value!.scrollTop = chatHistory.value!.scrollHeight;
|
||||
}),
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-sheet ref="chat-history" class="message-list d-flex">
|
||||
<div ref="chat-scrollback">
|
||||
<v-virtual-scroll height="100%" :items="messagesReverse">
|
||||
<template #default="{ item: msg }">
|
||||
<v-list-item
|
||||
density="compact"
|
||||
:prepend-avatar="store.getMetadata(msg.nick, 'avatar')"
|
||||
>
|
||||
<v-list-item-title>
|
||||
<span
|
||||
class="message-nick font-weight-bold"
|
||||
:class="{ 'text-primary': me === msg.nick }"
|
||||
>
|
||||
{{ msg.nick }}
|
||||
</span>
|
||||
<span class="message-time" v-if="!!msg.time">{{
|
||||
formatTime(msg.time)
|
||||
}}</span>
|
||||
</v-list-item-title>
|
||||
{{ msg.message }}
|
||||
</v-list-item></template
|
||||
>
|
||||
</v-virtual-scroll>
|
||||
</div>
|
||||
</v-sheet>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.message-list {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
.message-time {
|
||||
font-size: 0.65em;
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
52
app/frontend/src/components/UserCard.vue
Normal file
52
app/frontend/src/components/UserCard.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAccountStore } from "@/stores/accountStore";
|
||||
|
||||
const ircStore = useIRCStore();
|
||||
const accountStore = useAccountStore();
|
||||
const { selfAvatar } = storeToRefs(ircStore);
|
||||
const avatarDialog = ref(false);
|
||||
const newNick = ref();
|
||||
const newBio = ref();
|
||||
|
||||
function changeAvatar() {
|
||||
newNick.value = accountStore.account.nick;
|
||||
avatarDialog.value = true;
|
||||
}
|
||||
|
||||
function submitAvatar() {
|
||||
ircStore.setAvatar(selfAvatar.value);
|
||||
avatarDialog.value = false;
|
||||
if (newNick.value && accountStore.account.nick !== newNick.value) {
|
||||
ircStore.setNick(newNick.value);
|
||||
}
|
||||
|
||||
if (newBio.value) {
|
||||
ircStore.setBio(newBio.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog v-model="avatarDialog" max-width="800px">
|
||||
<v-card title="Edit Profile">
|
||||
<v-card-text>
|
||||
<v-text-field v-model="selfAvatar" label="Avatar URL" />
|
||||
<v-text-field v-model="newNick" label="Nick" />
|
||||
<v-text-field v-model="newBio" label="Bio" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn text="OK" @click="submitAvatar" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<v-avatar @click="changeAvatar" v-if="selfAvatar" :image="selfAvatar" />
|
||||
{{ accountStore.account.nick }}
|
||||
</v-card-title>
|
||||
<v-card-text> </v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
37
app/frontend/src/components/UserList.vue
Normal file
37
app/frontend/src/components/UserList.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { useBufferStore } from "@/stores/bufferStore";
|
||||
import { useIRCStore } from "@/stores/irc";
|
||||
import { computed } from "vue";
|
||||
const props = defineProps(["users"]);
|
||||
const store = useIRCStore();
|
||||
const bufferStore = useBufferStore();
|
||||
|
||||
const sortedUsers = computed(() => {
|
||||
if (!bufferStore.activeBuffer || !bufferStore.activeBuffer.users) return [];
|
||||
const u = [...bufferStore.activeBuffer.users];
|
||||
u.sort((a, b) => a.nick.localeCompare(b.nick));
|
||||
return u;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-list density="compact">
|
||||
<v-list-item
|
||||
v-for="user in sortedUsers"
|
||||
:prepend-avatar="store.getMetadata(user.nick, 'avatar')"
|
||||
:title="user.nick"
|
||||
>
|
||||
<v-menu activator="parent">
|
||||
<v-card :title="user.nick">
|
||||
<v-card-text>
|
||||
<p v-text="store.metadata[user.nick]?.bio"></p>
|
||||
</v-card-text>
|
||||
<v-list density="compact">
|
||||
<v-list-item title="Ident"> {{ user.ident }}</v-list-item>
|
||||
<v-list-item title="Hostname"> {{ user.hostname }}</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
51
app/frontend/src/lib/buffer.ts
Normal file
51
app/frontend/src/lib/buffer.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
import IrcChannel from "irc-framework/src/channel";
|
||||
import IrcUser from "irc-framework/src/user";
|
||||
|
||||
export interface BufferOptions {
|
||||
channel: typeof IrcChannel;
|
||||
name: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export class Buffer {
|
||||
name: string;
|
||||
options: BufferOptions;
|
||||
channel: IrcChannel;
|
||||
metadata: Record<string, any>;
|
||||
lastSeenIdx: number;
|
||||
messages: any[];
|
||||
typing: string[];
|
||||
users: IrcUser[];
|
||||
topic: string | null;
|
||||
|
||||
constructor(options: BufferOptions) {
|
||||
this.options = options || null;
|
||||
|
||||
this.name = options.name;
|
||||
|
||||
this.channel = options.channel || null;
|
||||
|
||||
this.metadata = options.metadata || {};
|
||||
this.messages = [];
|
||||
this.lastSeenIdx = 0;
|
||||
|
||||
this.typing = [];
|
||||
this.topic = options.topic || null;
|
||||
|
||||
if (this.channel) this.syncUsers();
|
||||
}
|
||||
|
||||
syncUsers() {
|
||||
this.users = [...this.channel.users];
|
||||
}
|
||||
|
||||
kind() {
|
||||
return this.name.startsWith("#") ? "channel" : "pm";
|
||||
}
|
||||
|
||||
resetLastSeen() {
|
||||
this.lastSeenIdx = 0;
|
||||
}
|
||||
}
|
||||
16
app/frontend/src/main.ts
Normal file
16
app/frontend/src/main.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { registerPlugins } from "@/plugins";
|
||||
|
||||
// Components
|
||||
import App from "./App.vue";
|
||||
|
||||
// Composables
|
||||
import { createApp } from "vue";
|
||||
|
||||
// Styles
|
||||
// import "unfonts.css";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
registerPlugins(app);
|
||||
|
||||
app.mount("#app");
|
||||
8
app/frontend/src/plugins/index.ts
Normal file
8
app/frontend/src/plugins/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import vuetify from "./vuetify";
|
||||
import pinia from "@/stores";
|
||||
|
||||
import type { App } from "vue";
|
||||
|
||||
export function registerPlugins(app: App) {
|
||||
app.use(vuetify).use(pinia);
|
||||
}
|
||||
10
app/frontend/src/plugins/vuetify.ts
Normal file
10
app/frontend/src/plugins/vuetify.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import "@mdi/font/css/materialdesignicons.css";
|
||||
import "vuetify/styles";
|
||||
|
||||
import { createVuetify } from "vuetify";
|
||||
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
defaultTheme: "system",
|
||||
},
|
||||
});
|
||||
25
app/frontend/src/stores/accountStore.ts
Normal file
25
app/frontend/src/stores/accountStore.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
|
||||
export const useAccountStore = defineStore("accountStore", () => {
|
||||
const authenticated = ref(false);
|
||||
const account = ref({
|
||||
nick: "",
|
||||
account: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const authError = ref({
|
||||
reason: "",
|
||||
message: "",
|
||||
});
|
||||
|
||||
function setAuthenticated(v: boolean) {
|
||||
authenticated.value = v;
|
||||
}
|
||||
function setNick(v: string) {
|
||||
account.value.nick = v;
|
||||
}
|
||||
|
||||
return { account, authError, authenticated, setAuthenticated, setNick };
|
||||
});
|
||||
47
app/frontend/src/stores/bufferStore.ts
Normal file
47
app/frontend/src/stores/bufferStore.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Buffer, type BufferOptions } from "@/lib/buffer.ts";
|
||||
import { defineStore } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
export const useBufferStore = defineStore("bufferStore", () => {
|
||||
const buffers = ref({} as Record<string, Buffer>);
|
||||
|
||||
const activeBufferName = ref(null as string | null);
|
||||
|
||||
function setActiveBuffer(bufferName: string) {
|
||||
const buffer = getBuffer(bufferName);
|
||||
if (!buffer) return;
|
||||
activeBufferName.value = bufferName;
|
||||
buffer.resetLastSeen();
|
||||
}
|
||||
|
||||
function addBuffer(bufferName: string, options: BufferOptions) {
|
||||
buffers.value[bufferName] = new Buffer(options);
|
||||
return buffers.value[bufferName];
|
||||
}
|
||||
|
||||
function getBuffer(bufferName: string) {
|
||||
return buffers.value[bufferName];
|
||||
}
|
||||
|
||||
function delBuffer(bufferName: string) {
|
||||
if (buffers.value[bufferName]) {
|
||||
delete buffers.value[bufferName];
|
||||
}
|
||||
}
|
||||
|
||||
const activeBuffer = computed(() => {
|
||||
if (activeBufferName.value) {
|
||||
return buffers.value[activeBufferName.value];
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
buffers,
|
||||
activeBufferName,
|
||||
activeBuffer,
|
||||
addBuffer,
|
||||
getBuffer,
|
||||
delBuffer,
|
||||
setActiveBuffer,
|
||||
};
|
||||
});
|
||||
3
app/frontend/src/stores/index.ts
Normal file
3
app/frontend/src/stores/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createPinia } from "pinia";
|
||||
|
||||
export default createPinia();
|
||||
298
app/frontend/src/stores/irc.ts
Normal file
298
app/frontend/src/stores/irc.ts
Normal file
@@ -0,0 +1,298 @@
|
||||
import { defineStore, storeToRefs } from "pinia";
|
||||
import { Client } from "irc-framework";
|
||||
import { useBufferStore } from "./bufferStore";
|
||||
import { ref } from "vue";
|
||||
import { useAccountStore } from "./accountStore";
|
||||
|
||||
export const useIRCStore = defineStore("ircStore", () => {
|
||||
const bufferStore = useBufferStore();
|
||||
const accountStore = useAccountStore();
|
||||
const connected = ref(false);
|
||||
const { authError } = storeToRefs(accountStore);
|
||||
|
||||
const selfAvatar = ref("https://placekittens.com/128/128");
|
||||
const bio = ref();
|
||||
|
||||
loadPrefs();
|
||||
|
||||
function setAvatar(v: string) {
|
||||
selfAvatar.value = v;
|
||||
client.raw(`METADATA * SET avatar ${selfAvatar.value}`);
|
||||
storePrefs();
|
||||
}
|
||||
|
||||
function loadPrefs() {
|
||||
// const v = localStorage.getItem("prefs");
|
||||
// if (v === null) return;
|
||||
//
|
||||
// const prefs = JSON.parse(v);
|
||||
// if (!prefs) return;
|
||||
//
|
||||
// if (prefs.avatar) {
|
||||
// selfAvatar.value = prefs.avatar;
|
||||
// }
|
||||
//
|
||||
// if (prefs.nick) {
|
||||
// clientInfo.value.nick = prefs.nick;
|
||||
// }
|
||||
//
|
||||
// if (prefs.bio) {
|
||||
// bio.value = prefs.bio;
|
||||
// }
|
||||
}
|
||||
|
||||
function storePrefs() {
|
||||
// localStorage.setItem(
|
||||
// "prefs",
|
||||
// JSON.stringify({
|
||||
// nick: clientInfo.value.nick,
|
||||
// avatar: selfAvatar.value,
|
||||
// bio: bio.value,
|
||||
// }),
|
||||
// );
|
||||
}
|
||||
|
||||
function setNick(v: string) {
|
||||
client.changeNick(v);
|
||||
}
|
||||
|
||||
const metadata = ref({} as Record<string, any>);
|
||||
|
||||
function getMetadata(subject: string, key: string) {
|
||||
if (metadata.value[subject]) return metadata.value[subject][key];
|
||||
}
|
||||
|
||||
const client = markRaw(new Client());
|
||||
|
||||
function connect() {
|
||||
client.requestCap("draft/metadata-2");
|
||||
client.requestCap("echo-message");
|
||||
client.requestCap("chathistory");
|
||||
const tls = location.protocol === "https:";
|
||||
const connectParams = {
|
||||
host: location.hostname,
|
||||
port: location.port,
|
||||
tls,
|
||||
version: "irchad on irc-framework",
|
||||
path: "/ws",
|
||||
account: accountStore.account.account
|
||||
? {
|
||||
account: accountStore.account.account,
|
||||
password: accountStore.account.password,
|
||||
}
|
||||
: undefined,
|
||||
sasl_disconect_on_fail: true,
|
||||
nick: accountStore.account.nick,
|
||||
};
|
||||
|
||||
console.log(connectParams);
|
||||
client.connect(connectParams);
|
||||
}
|
||||
|
||||
function sendActiveBuffer(message: string) {
|
||||
if (!bufferStore.activeBuffer) {
|
||||
return;
|
||||
}
|
||||
bufferStore.activeBuffer.channel.say(message);
|
||||
}
|
||||
|
||||
function isMe(target: string) {
|
||||
console.log(client.user.nick);
|
||||
return target === client.user.nick;
|
||||
}
|
||||
|
||||
function setBio(v: string) {
|
||||
bio.value = v;
|
||||
client.raw(`METADATA * SET bio ${bio.value}`);
|
||||
storePrefs();
|
||||
}
|
||||
|
||||
client.on("socket close", () => {
|
||||
connected.value = false;
|
||||
});
|
||||
|
||||
client.on("loggedin", () => {
|
||||
accountStore.setAuthenticated(true);
|
||||
authError.value = {
|
||||
reason: "",
|
||||
message: "",
|
||||
};
|
||||
});
|
||||
|
||||
client.on(
|
||||
"sasl failed",
|
||||
({ reason, message }: { reason: string; message: string }) => {
|
||||
authError.value = {
|
||||
reason,
|
||||
message,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
client.on("registered", function () {
|
||||
connected.value = true;
|
||||
client.list();
|
||||
client.raw("METADATA * SUB avatar");
|
||||
client.raw("METADATA * SUB bio");
|
||||
client.raw(`METADATA * SET avatar ${selfAvatar.value}`);
|
||||
client.raw(`METADATA * SET bio ${bio.value}`);
|
||||
});
|
||||
|
||||
client.on(
|
||||
"nick",
|
||||
function ({ nick, new_nick }: { nick: string; new_nick: string }) {
|
||||
if (nick === client.user.nick) {
|
||||
accountStore.setNick(new_nick);
|
||||
}
|
||||
for (let buffName in bufferStore.buffers.value) {
|
||||
const buff = bufferStore.getBuffer(buffName);
|
||||
if (!buff) {
|
||||
console.log(`${buffName} not found`);
|
||||
continue;
|
||||
}
|
||||
const idx = buff.users.findIndex((u) => u.nick === nick);
|
||||
if (idx === -1) {
|
||||
console.log(`${nick} not found in ${buffName}`);
|
||||
continue;
|
||||
}
|
||||
buff.users[idx].nick = new_nick;
|
||||
}
|
||||
metadata.value[new_nick] = { ...metadata.value[nick] };
|
||||
delete metadata.value[nick];
|
||||
},
|
||||
);
|
||||
|
||||
client.on(
|
||||
"unknown command",
|
||||
function (ircCommand: { command: string; params: string[] }) {
|
||||
if (ircCommand.command === "METADATA") {
|
||||
const from = ircCommand.params[0];
|
||||
const target = ircCommand.params[2];
|
||||
const key = ircCommand.params[1];
|
||||
const value = ircCommand.params[3];
|
||||
|
||||
let subject = target;
|
||||
if (target === "*") {
|
||||
subject = from;
|
||||
}
|
||||
if (!subject || !key || !value) return;
|
||||
|
||||
if (!metadata.value[subject]) {
|
||||
metadata.value[subject] = {};
|
||||
}
|
||||
|
||||
metadata.value[subject][key] = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
client.on("channel list", (channels: { channel: string }[]) =>
|
||||
channels.map((ch) => client.join(ch.channel)),
|
||||
);
|
||||
|
||||
client.on(
|
||||
"tagmsg",
|
||||
({
|
||||
nick,
|
||||
tags,
|
||||
target,
|
||||
}: {
|
||||
nick: string;
|
||||
tags: string[];
|
||||
target: string;
|
||||
}) => {
|
||||
console.log(nick, tags, target);
|
||||
},
|
||||
);
|
||||
|
||||
client.on("message", function (message: { nick: string; target: string }) {
|
||||
let buffer;
|
||||
if (message.nick === "HistServ") return;
|
||||
if (isMe(message.target)) {
|
||||
buffer = bufferStore.getBuffer(message.nick);
|
||||
} else {
|
||||
buffer = bufferStore.getBuffer(message.target);
|
||||
}
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.messages.push(message);
|
||||
|
||||
if (
|
||||
bufferStore.activeBuffer &&
|
||||
bufferStore.activeBuffer.name === buffer.name
|
||||
) {
|
||||
buffer.resetLastSeen();
|
||||
}
|
||||
});
|
||||
|
||||
client.on("join", ({ nick, channel }: { nick: string; channel: string }) => {
|
||||
if (isMe(nick)) {
|
||||
bufferStore.addBuffer(channel, {
|
||||
name: channel,
|
||||
channel: client.channel(channel),
|
||||
});
|
||||
if (!bufferStore.activeBuffer) {
|
||||
bufferStore.setActiveBuffer(channel);
|
||||
}
|
||||
client.raw("CHATHISTORY LATEST " + channel + " * 200");
|
||||
return;
|
||||
}
|
||||
|
||||
const buffer = bufferStore.getBuffer(channel);
|
||||
if (!buffer) return;
|
||||
buffer.syncUsers();
|
||||
buffer.users.push({
|
||||
nick: nick,
|
||||
});
|
||||
});
|
||||
|
||||
client.on("quit", function ({ nick }: { nick: string }) {
|
||||
for (let buff of Object.values(bufferStore.buffers)) {
|
||||
const idx = buff.users.findIndex((u) => u.nick === nick);
|
||||
if (idx === -1) continue;
|
||||
buff.users.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
|
||||
client.on(
|
||||
"topic",
|
||||
({ topic, channel }: { topic: string; channel: string }) => {
|
||||
const buffer = bufferStore.getBuffer(channel);
|
||||
if (!buffer) return;
|
||||
buffer.topic = topic;
|
||||
},
|
||||
);
|
||||
client.on("part", ({ nick, channel }: { nick: string; channel: string }) => {
|
||||
if (isMe(nick)) {
|
||||
bufferStore.delBuffer(channel);
|
||||
}
|
||||
const buffer = bufferStore.getBuffer(channel);
|
||||
if (!buffer) return;
|
||||
const idx = buffer.users.findIndex((u) => u.nick === nick);
|
||||
if (idx === -1) return;
|
||||
|
||||
buffer.users.splice(idx, 1);
|
||||
});
|
||||
|
||||
client.on("userlist", (ev: { channel: string; users: any[] }) => {
|
||||
const buffer = bufferStore.getBuffer(ev.channel);
|
||||
if (!buffer) return;
|
||||
buffer.users = ev.users;
|
||||
});
|
||||
|
||||
return {
|
||||
connect,
|
||||
client,
|
||||
sendActiveBuffer,
|
||||
getMetadata,
|
||||
metadata,
|
||||
selfAvatar,
|
||||
setAvatar,
|
||||
setNick,
|
||||
setBio,
|
||||
bio,
|
||||
connected,
|
||||
};
|
||||
});
|
||||
26
app/frontend/src/style.css
Normal file
26
app/frontend/src/style.css
Normal file
@@ -0,0 +1,26 @@
|
||||
html {
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
}
|
||||
0
app/frontend/src/styles/settings.scss
Normal file
0
app/frontend/src/styles/settings.scss
Normal file
35
app/frontend/src/types/irc-framework.d.ts
vendored
Normal file
35
app/frontend/src/types/irc-framework.d.ts
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
declare module "irc-framework" {
|
||||
export interface IrcUser {
|
||||
nick: string;
|
||||
ident?: string;
|
||||
hostname?: string;
|
||||
modes?: string[];
|
||||
[key: string]: any;
|
||||
}
|
||||
//
|
||||
// export class Client {
|
||||
// join(channel: string, key?: string);
|
||||
// constructor();
|
||||
// connect(options: any): void;
|
||||
// on(event: string, callback: (event: any) => void): void;
|
||||
// channel(name: string): IrcChannel;
|
||||
// changeNick(newNick: string);
|
||||
// say(target: string, message: string);
|
||||
// raw(v: string);
|
||||
// requestCap(cap: string);
|
||||
// list();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// declare module "irc-framework/src/channel" {
|
||||
// export class IrcChannel {
|
||||
// constructor(irc_client: Client, channel_name: string, key?: string);
|
||||
// name: string;
|
||||
// users: IrcUser[];
|
||||
// say(message: string): void;
|
||||
// notice(message: string): void;
|
||||
// part(message?: string): void;
|
||||
// join(key?: string): void;
|
||||
// mode(mode: string, param?: string): void;
|
||||
// }
|
||||
}
|
||||
7
app/frontend/src/vite-env.d.ts
vendored
Normal file
7
app/frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type {DefineComponent} from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
14
app/frontend/tsconfig.app.json
Normal file
14
app/frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
11
app/frontend/tsconfig.json
Normal file
11
app/frontend/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
20
app/frontend/tsconfig.node.json
Normal file
20
app/frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"],
|
||||
"allowJs": true
|
||||
}
|
||||
}
|
||||
62
app/frontend/vite.config.mjs
Normal file
62
app/frontend/vite.config.mjs
Normal file
@@ -0,0 +1,62 @@
|
||||
import Vue from "@vitejs/plugin-vue";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
import Vuetify, { transformAssetUrls } from "vite-plugin-vuetify";
|
||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
Vue({
|
||||
template: { transformAssetUrls },
|
||||
}),
|
||||
AutoImport({
|
||||
imports: [
|
||||
"vue",
|
||||
{
|
||||
pinia: ["defineStore", "storeToRefs"],
|
||||
},
|
||||
],
|
||||
dts: "src/auto-imports.d.ts",
|
||||
eslintrc: {
|
||||
enabled: true,
|
||||
},
|
||||
vueTemplate: true,
|
||||
}),
|
||||
Components({
|
||||
dts: "src/components.d.ts",
|
||||
}),
|
||||
Vuetify({
|
||||
autoImport: true,
|
||||
styles: {
|
||||
configFile: "src/styles/settings.scss",
|
||||
},
|
||||
}),
|
||||
nodePolyfills({
|
||||
globals: {
|
||||
Buffer: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
optimizeDeps: {
|
||||
exclude: ["vuetify"],
|
||||
},
|
||||
define: { "process.env": {} },
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("src", import.meta.url)),
|
||||
},
|
||||
extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"],
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
"/ws": {
|
||||
target: "ws://localhost:8097",
|
||||
ws: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
4
app/frontend/wailsjs/go/main/App.d.ts
vendored
Executable file
4
app/frontend/wailsjs/go/main/App.d.ts
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function Greet(arg1:string):Promise<string>;
|
||||
7
app/frontend/wailsjs/go/main/App.js
Executable file
7
app/frontend/wailsjs/go/main/App.js
Executable file
@@ -0,0 +1,7 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function Greet(arg1) {
|
||||
return window['go']['main']['App']['Greet'](arg1);
|
||||
}
|
||||
24
app/frontend/wailsjs/runtime/package.json
Normal file
24
app/frontend/wailsjs/runtime/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "2.0.0",
|
||||
"description": "Wails Javascript runtime library",
|
||||
"main": "runtime.js",
|
||||
"types": "runtime.d.ts",
|
||||
"scripts": {
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wailsapp/wails.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Wails",
|
||||
"Javascript",
|
||||
"Go"
|
||||
],
|
||||
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wailsapp/wails/issues"
|
||||
},
|
||||
"homepage": "https://github.com/wailsapp/wails#readme"
|
||||
}
|
||||
249
app/frontend/wailsjs/runtime/runtime.d.ts
vendored
Normal file
249
app/frontend/wailsjs/runtime/runtime.d.ts
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The electron alternative for Go
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface Size {
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
export interface Screen {
|
||||
isCurrent: boolean;
|
||||
isPrimary: boolean;
|
||||
width : number
|
||||
height : number
|
||||
}
|
||||
|
||||
// Environment information such as platform, buildtype, ...
|
||||
export interface EnvironmentInfo {
|
||||
buildType: string;
|
||||
platform: string;
|
||||
arch: string;
|
||||
}
|
||||
|
||||
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
|
||||
// emits the given event. Optional data may be passed with the event.
|
||||
// This will trigger any event listeners.
|
||||
export function EventsEmit(eventName: string, ...data: any): void;
|
||||
|
||||
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
|
||||
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
|
||||
|
||||
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
|
||||
// sets up a listener for the given event name, but will only trigger a given number times.
|
||||
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
|
||||
|
||||
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
|
||||
// sets up a listener for the given event name, but will only trigger once.
|
||||
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
|
||||
|
||||
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
|
||||
// unregisters the listener for the given event name.
|
||||
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
|
||||
|
||||
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
|
||||
// unregisters all listeners.
|
||||
export function EventsOffAll(): void;
|
||||
|
||||
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
|
||||
// logs the given message as a raw message
|
||||
export function LogPrint(message: string): void;
|
||||
|
||||
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
|
||||
// logs the given message at the `trace` log level.
|
||||
export function LogTrace(message: string): void;
|
||||
|
||||
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
|
||||
// logs the given message at the `debug` log level.
|
||||
export function LogDebug(message: string): void;
|
||||
|
||||
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
|
||||
// logs the given message at the `error` log level.
|
||||
export function LogError(message: string): void;
|
||||
|
||||
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
|
||||
// logs the given message at the `fatal` log level.
|
||||
// The application will quit after calling this method.
|
||||
export function LogFatal(message: string): void;
|
||||
|
||||
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
|
||||
// logs the given message at the `info` log level.
|
||||
export function LogInfo(message: string): void;
|
||||
|
||||
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
|
||||
// logs the given message at the `warning` log level.
|
||||
export function LogWarning(message: string): void;
|
||||
|
||||
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
|
||||
// Forces a reload by the main application as well as connected browsers.
|
||||
export function WindowReload(): void;
|
||||
|
||||
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
|
||||
// Reloads the application frontend.
|
||||
export function WindowReloadApp(): void;
|
||||
|
||||
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
|
||||
// Sets the window AlwaysOnTop or not on top.
|
||||
export function WindowSetAlwaysOnTop(b: boolean): void;
|
||||
|
||||
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
|
||||
// *Windows only*
|
||||
// Sets window theme to system default (dark/light).
|
||||
export function WindowSetSystemDefaultTheme(): void;
|
||||
|
||||
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
|
||||
// *Windows only*
|
||||
// Sets window to light theme.
|
||||
export function WindowSetLightTheme(): void;
|
||||
|
||||
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
|
||||
// *Windows only*
|
||||
// Sets window to dark theme.
|
||||
export function WindowSetDarkTheme(): void;
|
||||
|
||||
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
|
||||
// Centers the window on the monitor the window is currently on.
|
||||
export function WindowCenter(): void;
|
||||
|
||||
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
|
||||
// Sets the text in the window title bar.
|
||||
export function WindowSetTitle(title: string): void;
|
||||
|
||||
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
|
||||
// Makes the window full screen.
|
||||
export function WindowFullscreen(): void;
|
||||
|
||||
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
|
||||
// Restores the previous window dimensions and position prior to full screen.
|
||||
export function WindowUnfullscreen(): void;
|
||||
|
||||
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
|
||||
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
|
||||
export function WindowIsFullscreen(): Promise<boolean>;
|
||||
|
||||
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
|
||||
// Sets the width and height of the window.
|
||||
export function WindowSetSize(width: number, height: number): void;
|
||||
|
||||
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
|
||||
// Gets the width and height of the window.
|
||||
export function WindowGetSize(): Promise<Size>;
|
||||
|
||||
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
|
||||
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
|
||||
// Setting a size of 0,0 will disable this constraint.
|
||||
export function WindowSetMaxSize(width: number, height: number): void;
|
||||
|
||||
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
|
||||
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
|
||||
// Setting a size of 0,0 will disable this constraint.
|
||||
export function WindowSetMinSize(width: number, height: number): void;
|
||||
|
||||
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
|
||||
// Sets the window position relative to the monitor the window is currently on.
|
||||
export function WindowSetPosition(x: number, y: number): void;
|
||||
|
||||
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
|
||||
// Gets the window position relative to the monitor the window is currently on.
|
||||
export function WindowGetPosition(): Promise<Position>;
|
||||
|
||||
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
|
||||
// Hides the window.
|
||||
export function WindowHide(): void;
|
||||
|
||||
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
|
||||
// Shows the window, if it is currently hidden.
|
||||
export function WindowShow(): void;
|
||||
|
||||
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
|
||||
// Maximises the window to fill the screen.
|
||||
export function WindowMaximise(): void;
|
||||
|
||||
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
|
||||
// Toggles between Maximised and UnMaximised.
|
||||
export function WindowToggleMaximise(): void;
|
||||
|
||||
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
|
||||
// Restores the window to the dimensions and position prior to maximising.
|
||||
export function WindowUnmaximise(): void;
|
||||
|
||||
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
|
||||
// Returns the state of the window, i.e. whether the window is maximised or not.
|
||||
export function WindowIsMaximised(): Promise<boolean>;
|
||||
|
||||
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
|
||||
// Minimises the window.
|
||||
export function WindowMinimise(): void;
|
||||
|
||||
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
|
||||
// Restores the window to the dimensions and position prior to minimising.
|
||||
export function WindowUnminimise(): void;
|
||||
|
||||
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
|
||||
// Returns the state of the window, i.e. whether the window is minimised or not.
|
||||
export function WindowIsMinimised(): Promise<boolean>;
|
||||
|
||||
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
|
||||
// Returns the state of the window, i.e. whether the window is normal or not.
|
||||
export function WindowIsNormal(): Promise<boolean>;
|
||||
|
||||
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
|
||||
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
|
||||
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
|
||||
|
||||
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
|
||||
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
||||
export function ScreenGetAll(): Promise<Screen[]>;
|
||||
|
||||
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
|
||||
// Opens the given URL in the system browser.
|
||||
export function BrowserOpenURL(url: string): void;
|
||||
|
||||
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
|
||||
// Returns information about the environment
|
||||
export function Environment(): Promise<EnvironmentInfo>;
|
||||
|
||||
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
|
||||
// Quits the application.
|
||||
export function Quit(): void;
|
||||
|
||||
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
|
||||
// Hides the application.
|
||||
export function Hide(): void;
|
||||
|
||||
// [Show](https://wails.io/docs/reference/runtime/intro#show)
|
||||
// Shows the application.
|
||||
export function Show(): void;
|
||||
|
||||
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
|
||||
// Returns the current text stored on clipboard
|
||||
export function ClipboardGetText(): Promise<string>;
|
||||
|
||||
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
|
||||
// Sets a text on the clipboard
|
||||
export function ClipboardSetText(text: string): Promise<boolean>;
|
||||
|
||||
// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop)
|
||||
// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
|
||||
export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void
|
||||
|
||||
// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
|
||||
// OnFileDropOff removes the drag and drop listeners and handlers.
|
||||
export function OnFileDropOff() :void
|
||||
|
||||
// Check if the file path resolver is available
|
||||
export function CanResolveFilePaths(): boolean;
|
||||
|
||||
// Resolves file paths for an array of files
|
||||
export function ResolveFilePaths(files: File[]): void
|
||||
242
app/frontend/wailsjs/runtime/runtime.js
Normal file
242
app/frontend/wailsjs/runtime/runtime.js
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The electron alternative for Go
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
export function LogPrint(message) {
|
||||
window.runtime.LogPrint(message);
|
||||
}
|
||||
|
||||
export function LogTrace(message) {
|
||||
window.runtime.LogTrace(message);
|
||||
}
|
||||
|
||||
export function LogDebug(message) {
|
||||
window.runtime.LogDebug(message);
|
||||
}
|
||||
|
||||
export function LogInfo(message) {
|
||||
window.runtime.LogInfo(message);
|
||||
}
|
||||
|
||||
export function LogWarning(message) {
|
||||
window.runtime.LogWarning(message);
|
||||
}
|
||||
|
||||
export function LogError(message) {
|
||||
window.runtime.LogError(message);
|
||||
}
|
||||
|
||||
export function LogFatal(message) {
|
||||
window.runtime.LogFatal(message);
|
||||
}
|
||||
|
||||
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
|
||||
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
|
||||
}
|
||||
|
||||
export function EventsOn(eventName, callback) {
|
||||
return EventsOnMultiple(eventName, callback, -1);
|
||||
}
|
||||
|
||||
export function EventsOff(eventName, ...additionalEventNames) {
|
||||
return window.runtime.EventsOff(eventName, ...additionalEventNames);
|
||||
}
|
||||
|
||||
export function EventsOffAll() {
|
||||
return window.runtime.EventsOffAll();
|
||||
}
|
||||
|
||||
export function EventsOnce(eventName, callback) {
|
||||
return EventsOnMultiple(eventName, callback, 1);
|
||||
}
|
||||
|
||||
export function EventsEmit(eventName) {
|
||||
let args = [eventName].slice.call(arguments);
|
||||
return window.runtime.EventsEmit.apply(null, args);
|
||||
}
|
||||
|
||||
export function WindowReload() {
|
||||
window.runtime.WindowReload();
|
||||
}
|
||||
|
||||
export function WindowReloadApp() {
|
||||
window.runtime.WindowReloadApp();
|
||||
}
|
||||
|
||||
export function WindowSetAlwaysOnTop(b) {
|
||||
window.runtime.WindowSetAlwaysOnTop(b);
|
||||
}
|
||||
|
||||
export function WindowSetSystemDefaultTheme() {
|
||||
window.runtime.WindowSetSystemDefaultTheme();
|
||||
}
|
||||
|
||||
export function WindowSetLightTheme() {
|
||||
window.runtime.WindowSetLightTheme();
|
||||
}
|
||||
|
||||
export function WindowSetDarkTheme() {
|
||||
window.runtime.WindowSetDarkTheme();
|
||||
}
|
||||
|
||||
export function WindowCenter() {
|
||||
window.runtime.WindowCenter();
|
||||
}
|
||||
|
||||
export function WindowSetTitle(title) {
|
||||
window.runtime.WindowSetTitle(title);
|
||||
}
|
||||
|
||||
export function WindowFullscreen() {
|
||||
window.runtime.WindowFullscreen();
|
||||
}
|
||||
|
||||
export function WindowUnfullscreen() {
|
||||
window.runtime.WindowUnfullscreen();
|
||||
}
|
||||
|
||||
export function WindowIsFullscreen() {
|
||||
return window.runtime.WindowIsFullscreen();
|
||||
}
|
||||
|
||||
export function WindowGetSize() {
|
||||
return window.runtime.WindowGetSize();
|
||||
}
|
||||
|
||||
export function WindowSetSize(width, height) {
|
||||
window.runtime.WindowSetSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetMaxSize(width, height) {
|
||||
window.runtime.WindowSetMaxSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetMinSize(width, height) {
|
||||
window.runtime.WindowSetMinSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetPosition(x, y) {
|
||||
window.runtime.WindowSetPosition(x, y);
|
||||
}
|
||||
|
||||
export function WindowGetPosition() {
|
||||
return window.runtime.WindowGetPosition();
|
||||
}
|
||||
|
||||
export function WindowHide() {
|
||||
window.runtime.WindowHide();
|
||||
}
|
||||
|
||||
export function WindowShow() {
|
||||
window.runtime.WindowShow();
|
||||
}
|
||||
|
||||
export function WindowMaximise() {
|
||||
window.runtime.WindowMaximise();
|
||||
}
|
||||
|
||||
export function WindowToggleMaximise() {
|
||||
window.runtime.WindowToggleMaximise();
|
||||
}
|
||||
|
||||
export function WindowUnmaximise() {
|
||||
window.runtime.WindowUnmaximise();
|
||||
}
|
||||
|
||||
export function WindowIsMaximised() {
|
||||
return window.runtime.WindowIsMaximised();
|
||||
}
|
||||
|
||||
export function WindowMinimise() {
|
||||
window.runtime.WindowMinimise();
|
||||
}
|
||||
|
||||
export function WindowUnminimise() {
|
||||
window.runtime.WindowUnminimise();
|
||||
}
|
||||
|
||||
export function WindowSetBackgroundColour(R, G, B, A) {
|
||||
window.runtime.WindowSetBackgroundColour(R, G, B, A);
|
||||
}
|
||||
|
||||
export function ScreenGetAll() {
|
||||
return window.runtime.ScreenGetAll();
|
||||
}
|
||||
|
||||
export function WindowIsMinimised() {
|
||||
return window.runtime.WindowIsMinimised();
|
||||
}
|
||||
|
||||
export function WindowIsNormal() {
|
||||
return window.runtime.WindowIsNormal();
|
||||
}
|
||||
|
||||
export function BrowserOpenURL(url) {
|
||||
window.runtime.BrowserOpenURL(url);
|
||||
}
|
||||
|
||||
export function Environment() {
|
||||
return window.runtime.Environment();
|
||||
}
|
||||
|
||||
export function Quit() {
|
||||
window.runtime.Quit();
|
||||
}
|
||||
|
||||
export function Hide() {
|
||||
window.runtime.Hide();
|
||||
}
|
||||
|
||||
export function Show() {
|
||||
window.runtime.Show();
|
||||
}
|
||||
|
||||
export function ClipboardGetText() {
|
||||
return window.runtime.ClipboardGetText();
|
||||
}
|
||||
|
||||
export function ClipboardSetText(text) {
|
||||
return window.runtime.ClipboardSetText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
|
||||
*
|
||||
* @export
|
||||
* @callback OnFileDropCallback
|
||||
* @param {number} x - x coordinate of the drop
|
||||
* @param {number} y - y coordinate of the drop
|
||||
* @param {string[]} paths - A list of file paths.
|
||||
*/
|
||||
|
||||
/**
|
||||
* OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
|
||||
*
|
||||
* @export
|
||||
* @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
|
||||
* @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
|
||||
*/
|
||||
export function OnFileDrop(callback, useDropTarget) {
|
||||
return window.runtime.OnFileDrop(callback, useDropTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* OnFileDropOff removes the drag and drop listeners and handlers.
|
||||
*/
|
||||
export function OnFileDropOff() {
|
||||
return window.runtime.OnFileDropOff();
|
||||
}
|
||||
|
||||
export function CanResolveFilePaths() {
|
||||
return window.runtime.CanResolveFilePaths();
|
||||
}
|
||||
|
||||
export function ResolveFilePaths(files) {
|
||||
return window.runtime.ResolveFilePaths(files);
|
||||
}
|
||||
Reference in New Issue
Block a user