First commit

This commit is contained in:
2026-03-18 22:42:33 +09:00
commit 50657066a6
64 changed files with 5290 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}
+2
View File
@@ -0,0 +1,2 @@
# Nocos Frontend
Vue 3.5 + Vue Router 5.0 + Vite 7.2
+77
View File
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LynqChat</title>
<link rel="icon" href="/lynqchat.svg" type="image/svg+xml">
</head>
<body>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
width: 100vw;
height: 100vh;
font-size: 16px;
background-color: #ffffff;
}
@media (prefers-color-scheme: dark) {
html, body {
background-color: #1b1b1b;
}
}
.loading {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
width: fit-content;
height: fit-content;
position: fixed;
inset: 50%;
transform: translate(-50%, -50%);
}
.logo {
width: 6rem;
height: 6rem;
border-radius: 1rem;
}
.progress {
border: 0.4rem solid #425C97;
border-radius: 100%;
border-top: 0.4rem solid #ffffff;
width: 4rem;
height: 4rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<div class="loading">
<img
class="logo"
src="/lynqchat.svg"
/>
<div class="progress" />
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+25
View File
@@ -0,0 +1,25 @@
{
"name": "frontend",
"private": true,
"type": "module",
"license": "AGPL-3.0-only",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"dexie": "^4.3.0",
"vue": "^3.5.24",
"vue-router": "^5.0.2"
},
"devDependencies": {
"@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1",
"typescript": "~5.9.3",
"vite": "^8.0.0",
"vue-tsc": "^3.1.4"
},
"packageManager": "pnpm@10.29.1"
}
+11
View File
@@ -0,0 +1,11 @@
<svg width="4096" height="4096" viewBox="0 0 4096 4096" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="4096" height="4096" fill="#1E1E1E"/>
<rect width="4096" height="4096" fill="#425C97"/>
<rect x="546" y="1086" width="155.544" height="1011.04" rx="77.7722" fill="white"/>
<rect x="1168.18" y="1941.49" width="155.544" height="622.177" rx="77.7722" transform="rotate(90 1168.18 1941.49)" fill="white"/>
<path d="M1674.03 1913.56C1691.75 1951.65 1679.85 1996.96 1645.71 2021.43C1601.21 2053.32 1538.68 2036.9 1515.59 1987.26L1325.79 1579.23C1298.84 1521.31 1341.13 1455 1405.01 1455C1438.99 1455 1469.9 1474.71 1484.23 1505.52L1674.03 1913.56Z" fill="white"/>
<path d="M1520.23 2377.11C1505.89 2407.93 1474.99 2427.64 1441 2427.64C1377.13 2427.64 1334.84 2361.33 1361.78 2303.41L1732.93 1505.52C1747.26 1474.71 1778.16 1455 1812.15 1455C1876.03 1455 1918.32 1521.31 1891.37 1579.23L1520.23 2377.11Z" fill="white"/>
<path d="M2849 1439C2959.46 1439 3049 1528.54 3049 1639V2021.23C3049 2064.18 3014.18 2099 2971.23 2099C2928.28 2099 2893.46 2064.18 2893.46 2021.23V1639C2893.46 1614.83 2874.17 1595.16 2850.15 1594.55L2849 1594.54H2249L2247.85 1594.55C2223.83 1595.16 2204.54 1614.83 2204.54 1639V2021.23C2204.54 2064.18 2169.72 2099 2126.77 2099C2083.82 2099 2049 2064.18 2049 2021.23V1639C2049 1528.54 2138.54 1439 2249 1439H2849Z" fill="white"/>
<path d="M2749 1766.77H3389C3434.41 1766.77 3471.23 1803.59 3471.23 1849V2149C3471.23 2216.51 3416.51 2271.23 3349 2271.23H2749C2681.49 2271.23 2626.77 2216.51 2626.77 2149V1889C2626.77 1821.49 2681.49 1766.77 2749 1766.77Z" stroke="white" stroke-width="155.54"/>
<path d="M3393.46 1689C3479.62 1689 3549.46 1758.84 3549.46 1845V2931C3549.46 2974.08 3514.54 3009 3471.46 3009C3428.38 3009 3393.46 2974.08 3393.46 2931V1689Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+27
View File
@@ -0,0 +1,27 @@
<template>
<Progress
v-show="routerStatus.isLoad"
class="router-progress"
:size="6"
/>
<main>
<RouterView
:key="$route.fullPath"
/>
</main>
</template>
<style scoped>
.router-progress {
position: fixed;
inset: 0.5rem 0.5rem 0 auto;
z-index: 9999;
}
</style>
<script lang="ts" setup>
import { RouterView } from "vue-router";
import routerStatus from "@/lib/router";
import Progress from "@/components/Progress.vue";
</script>
+32
View File
@@ -0,0 +1,32 @@
<template>
<div class="progress" />
</template>
<style scoped>
.progress {
border: v-bind(borderSize) solid #425C97;
border-radius: 100%;
border-top: v-bind(borderSize) solid #ffffff;
width: v-bind(size);
height: v-bind(size);
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<script lang="ts" setup>
const props = defineProps<{
size: number;
}>();
const size = `${props.size * 0.25}rem`;
const borderSize = `${props.size / 3}px`;
</script>
+24
View File
@@ -0,0 +1,24 @@
:root {
--bg-color: #ffffff;
--text-color: #000000;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1b1b1b;
--text-color: #ffffff;
}
}
* {
margin: 0;
padding: 0;
}
html, body {
width: 100vw;
height: 100vh;
font-size: 16px;
background-color: var(--bg-color);
color: var(--text-color);
}
+56
View File
@@ -0,0 +1,56 @@
import Dexie, { type EntityTable } from "dexie";
export interface Settings {
id: number;
name: string;
value: string;
}
export interface Server {
id: number;
name: string;
value: string;
}
export default class extends Dexie {
server!: EntityTable<Server, "id">;
settings!: EntityTable<Settings, "id">;
constructor() {
super("lynq-chat");
this.version(1).stores({
server: "++id,&name",
settings: "++id,&name",
});
/*(async () => {
await this.open();
await this.transaction("rw", this.settings, async () => {
const defaults = {
"key": "value",
};
for (const [name, value] of Object.entries(defaults)) {
const exists = await this.settings
.where("name")
.equals(name)
.first();
if (!exists) {
await this.settings.add({ name, value });
}
}
});
})();*/
};
}
export async function getByIndex<T>(
table: EntityTable<T, any>,
index: string,
indexValue: any
): Promise<T | undefined> {
return await table.where(index).equals(indexValue).first();
}
+9
View File
@@ -0,0 +1,9 @@
import { reactive } from "vue";
const routerStatus = reactive<{
isLoad: boolean;
}>({
isLoad: false,
});
export default routerStatus;
+29
View File
@@ -0,0 +1,29 @@
import { createApp } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import routerStatus from "@/lib/router";
import "@/global.css";
import Layout from "@/Layout.vue";
const app = createApp(Layout);
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/",
component: () => import("@/routes/index.vue"),
},
],
});
// @ts-ignore 余分な引数の警告
router.beforeEach((to, from, next) => {
routerStatus.isLoad = true;
next();
});
router.afterEach(() => {
routerStatus.isLoad = false;
});
app.use(router);
app.mount("body");
+6
View File
@@ -0,0 +1,6 @@
<template>
<h1>Hello World</h1>
</template>
<script lang="ts" setup>
</script>
+4
View File
@@ -0,0 +1,4 @@
export {}
declare global {
}
+30
View File
@@ -0,0 +1,30 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"types": ["vite/client"],
"typeRoots": ["./node_modules/"],
"target": "ES2023",
"lib": ["ES2023", "DOM"],
"module": "ESNext",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
/* Alias */
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
},
},
"include": [
"./src/**/*.ts",
"./src/**/*.tsx",
"./src/**/*.vue",
],
}
+13
View File
@@ -0,0 +1,13 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
},
},
}
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
+29
View File
@@ -0,0 +1,29 @@
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "")
const apiPort = Number(env.VITE_API_PORT) || 3300
return {
plugins: [vue()],
server: {
hmr: {
clientPort: 5173,
protocol: "ws",
},
proxy: {
"/api": {
target: `http://localhost:${apiPort}`,
changeOrigin: true,
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
}
})