21 changed files with 7744 additions and 2 deletions
@ -1,3 +1,73 @@
@@ -1,3 +1,73 @@
|
||||
# agent.corneruniverse.com |
||||
# agent |
||||
|
||||
AI code agent for building websites |
||||
This template should help get you started developing with Vue 3 in Vite. |
||||
|
||||
## Recommended IDE Setup |
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). |
||||
|
||||
## Recommended Browser Setup |
||||
|
||||
- Chromium-based browsers (Chrome, Edge, Brave, etc.): |
||||
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) |
||||
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) |
||||
- Firefox: |
||||
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) |
||||
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) |
||||
|
||||
## Type Support for `.vue` Imports in TS |
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. |
||||
|
||||
## Customize configuration |
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/). |
||||
|
||||
## Project Setup |
||||
|
||||
```sh |
||||
npm install |
||||
``` |
||||
|
||||
### Compile and Hot-Reload for Development |
||||
|
||||
```sh |
||||
npm run dev |
||||
``` |
||||
|
||||
### Type-Check, Compile and Minify for Production |
||||
|
||||
```sh |
||||
npm run build |
||||
``` |
||||
|
||||
### Run Unit Tests with [Vitest](https://vitest.dev/) |
||||
|
||||
```sh |
||||
npm run test:unit |
||||
``` |
||||
|
||||
### Run End-to-End Tests with [Playwright](https://playwright.dev) |
||||
|
||||
```sh |
||||
# Install browsers for the first run |
||||
npx playwright install |
||||
|
||||
# When testing on CI, must build the project first |
||||
npm run build |
||||
|
||||
# Runs the end-to-end tests |
||||
npm run test:e2e |
||||
# Runs the tests only on Chromium |
||||
npm run test:e2e -- --project=chromium |
||||
# Runs the tests of a specific file |
||||
npm run test:e2e -- tests/example.spec.ts |
||||
# Runs the tests in debug mode |
||||
npm run test:e2e -- --debug |
||||
``` |
||||
|
||||
### Lint with [ESLint](https://eslint.org/) |
||||
|
||||
```sh |
||||
npm run lint |
||||
``` |
||||
|
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
{ |
||||
"extends": "@tsconfig/node24/tsconfig.json", |
||||
"include": ["./**/*"] |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
import { test, expect } from '@playwright/test'; |
||||
|
||||
// See here how to get started:
|
||||
// https://playwright.dev/docs/intro
|
||||
test('visits the app root url', async ({ page }) => { |
||||
await page.goto('/'); |
||||
await expect(page.locator('h1')).toHaveText('You did it!'); |
||||
}) |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
import { globalIgnores } from 'eslint/config' |
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' |
||||
import pluginVue from 'eslint-plugin-vue' |
||||
import pluginPlaywright from 'eslint-plugin-playwright' |
||||
import pluginVitest from '@vitest/eslint-plugin' |
||||
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' |
||||
import pluginOxlint from 'eslint-plugin-oxlint' |
||||
|
||||
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
|
||||
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
|
||||
|
||||
export default defineConfigWithVueTs( |
||||
{ |
||||
name: 'app/files-to-lint', |
||||
files: ['**/*.{vue,ts,mts,tsx}'], |
||||
}, |
||||
|
||||
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), |
||||
|
||||
...pluginVue.configs['flat/essential'], |
||||
vueTsConfigs.recommended, |
||||
|
||||
{ |
||||
...pluginPlaywright.configs['flat/recommended'], |
||||
files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'], |
||||
}, |
||||
|
||||
{ |
||||
...pluginVitest.configs.recommended, |
||||
files: ['src/**/__tests__/*'], |
||||
}, |
||||
|
||||
skipFormatting, |
||||
|
||||
...pluginOxlint.configs['flat/recommended'], |
||||
) |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html> |
||||
<html lang=""> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<link rel="icon" href="/favicon.ico"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>Vite App</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/main.ts"></script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
{ |
||||
"name": "agent", |
||||
"version": "0.0.0", |
||||
"private": true, |
||||
"type": "module", |
||||
"engines": { |
||||
"node": "^20.19.0 || >=22.12.0" |
||||
}, |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "run-p type-check \"build-only {@}\" --", |
||||
"preview": "vite preview", |
||||
"test:unit": "vitest", |
||||
"test:e2e": "playwright test", |
||||
"build-only": "vite build", |
||||
"type-check": "vue-tsc --build", |
||||
"lint": "run-s lint:*", |
||||
"lint:oxlint": "oxlint . --fix", |
||||
"lint:eslint": "eslint . --fix --cache", |
||||
"format": "prettier --write --experimental-cli src/" |
||||
}, |
||||
"dependencies": { |
||||
"pinia": "^3.0.4", |
||||
"vue": "^3.5.26", |
||||
"vue-router": "^4.6.4" |
||||
}, |
||||
"devDependencies": { |
||||
"@playwright/test": "^1.57.0", |
||||
"@tsconfig/node24": "^24.0.3", |
||||
"@types/jsdom": "^27.0.0", |
||||
"@types/node": "^24.10.4", |
||||
"@vitejs/plugin-vue": "^6.0.3", |
||||
"@vitejs/plugin-vue-jsx": "^5.1.3", |
||||
"@vitest/eslint-plugin": "^1.6.5", |
||||
"@vue/eslint-config-prettier": "^10.2.0", |
||||
"@vue/eslint-config-typescript": "^14.6.0", |
||||
"@vue/test-utils": "^2.4.6", |
||||
"@vue/tsconfig": "^0.8.1", |
||||
"eslint": "^9.39.2", |
||||
"eslint-plugin-oxlint": "~1.38.0", |
||||
"eslint-plugin-playwright": "^2.4.0", |
||||
"eslint-plugin-vue": "~10.6.2", |
||||
"jiti": "^2.6.1", |
||||
"jsdom": "^27.4.0", |
||||
"npm-run-all2": "^8.0.4", |
||||
"oxlint": "~1.38.0", |
||||
"prettier": "3.7.4", |
||||
"typescript": "~5.9.3", |
||||
"vite": "beta", |
||||
"vite-plugin-vue-devtools": "^8.0.5", |
||||
"vitest": "^4.0.16", |
||||
"vue-tsc": "^3.2.2" |
||||
} |
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
import process from 'node:process' |
||||
import { defineConfig, devices } from '@playwright/test' |
||||
|
||||
/** |
||||
* Read environment variables from file. |
||||
* https://github.com/motdotla/dotenv
|
||||
*/ |
||||
// require('dotenv').config();
|
||||
|
||||
/** |
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/ |
||||
export default defineConfig({ |
||||
testDir: './e2e', |
||||
/* Maximum time one test can run for. */ |
||||
timeout: 30 * 1000, |
||||
expect: { |
||||
/** |
||||
* Maximum time expect() should wait for the condition to be met. |
||||
* For example in `await expect(locator).toHaveText();` |
||||
*/ |
||||
timeout: 5000, |
||||
}, |
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */ |
||||
forbidOnly: !!process.env.CI, |
||||
/* Retry on CI only */ |
||||
retries: process.env.CI ? 2 : 0, |
||||
/* Opt out of parallel tests on CI. */ |
||||
workers: process.env.CI ? 1 : undefined, |
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ |
||||
reporter: 'html', |
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ |
||||
use: { |
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ |
||||
actionTimeout: 0, |
||||
/* Base URL to use in actions like `await page.goto('/')`. */ |
||||
baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173', |
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ |
||||
trace: 'on-first-retry', |
||||
|
||||
/* Only on CI systems run the tests headless */ |
||||
headless: !!process.env.CI, |
||||
}, |
||||
|
||||
/* Configure projects for major browsers */ |
||||
projects: [ |
||||
{ |
||||
name: 'chromium', |
||||
use: { |
||||
...devices['Desktop Chrome'], |
||||
}, |
||||
}, |
||||
{ |
||||
name: 'firefox', |
||||
use: { |
||||
...devices['Desktop Firefox'], |
||||
}, |
||||
}, |
||||
{ |
||||
name: 'webkit', |
||||
use: { |
||||
...devices['Desktop Safari'], |
||||
}, |
||||
}, |
||||
|
||||
/* Test against mobile viewports. */ |
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */ |
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
], |
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */ |
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */ |
||||
webServer: { |
||||
/** |
||||
* Use the dev server by default for faster feedback loop. |
||||
* Use the preview server on CI for more realistic testing. |
||||
* Playwright will re-use the local server if there is already a dev-server running. |
||||
*/ |
||||
command: process.env.CI ? 'npm run preview' : 'npm run dev', |
||||
port: process.env.CI ? 4173 : 5173, |
||||
reuseExistingServer: !process.env.CI, |
||||
}, |
||||
}) |
||||
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,397 @@
@@ -0,0 +1,397 @@
|
||||
<template> |
||||
<div id="app"> |
||||
<header> |
||||
<h1>AI Code Agent</h1> |
||||
<div v-if="user" class="user-info"> |
||||
{{ user.username }} |
||||
<button @click="logout">Logout</button> |
||||
</div> |
||||
</header> |
||||
|
||||
<main> |
||||
<!-- Login --> |
||||
<div v-if="!user" class="login-section"> |
||||
<p>Login with your Gitea account to get started.</p> |
||||
<a :href="apiUrl + '/api/auth/login'" class="btn">Login with Gitea</a> |
||||
</div> |
||||
|
||||
<!-- Main Interface --> |
||||
<div v-else class="workspace"> |
||||
<!-- Repo Selection --> |
||||
<div class="section"> |
||||
<label>Repository</label> |
||||
<select v-model="selectedRepo" @change="clearState"> |
||||
<option value="">Select a repository...</option> |
||||
<option v-for="repo in repos" :key="repo.name" :value="repo.name"> |
||||
{{ repo.name }} |
||||
</option> |
||||
</select> |
||||
</div> |
||||
|
||||
<!-- Prompt Input --> |
||||
<div v-if="selectedRepo" class="section"> |
||||
<label>What would you like to change?</label> |
||||
<textarea |
||||
v-model="prompt" |
||||
placeholder="e.g., Add a contact form to the homepage with name, email, and message fields" |
||||
rows="4" |
||||
></textarea> |
||||
<button |
||||
@click="generate" |
||||
:disabled="generating || !prompt.trim()" |
||||
class="btn" |
||||
> |
||||
{{ generating ? "Generating..." : "Generate Changes" }} |
||||
</button> |
||||
</div> |
||||
|
||||
<!-- Error Display --> |
||||
<div v-if="error" class="error"> |
||||
{{ error }} |
||||
</div> |
||||
|
||||
<!-- Generated Changes --> |
||||
<div v-if="operations" class="section"> |
||||
<h2>Proposed Changes</h2> |
||||
<p class="summary">{{ operations.summary }}</p> |
||||
|
||||
<div |
||||
v-for="op in operations.operations" |
||||
:key="op.path" |
||||
class="file-change" |
||||
> |
||||
<div class="file-header"> |
||||
<span class="action" :class="op.action">{{ op.action }}</span> |
||||
<span class="path">{{ op.path }}</span> |
||||
</div> |
||||
<pre v-if="op.content" class="content">{{ op.content }}</pre> |
||||
</div> |
||||
|
||||
<div class="actions"> |
||||
<button @click="submit" :disabled="submitting" class="btn primary"> |
||||
{{ submitting ? "Creating PR..." : "Create Pull Request" }} |
||||
</button> |
||||
<button @click="clearState" class="btn secondary">Discard</button> |
||||
</div> |
||||
</div> |
||||
|
||||
<!-- PR Created --> |
||||
<div v-if="result" class="section success"> |
||||
<h2>Pull Request Created!</h2> |
||||
<p> |
||||
<a :href="result.pr_url" target="_blank" |
||||
>View PR #{{ result.pr_number }} in Gitea</a |
||||
> |
||||
</p> |
||||
<p v-if="result.preview_url"> |
||||
<a :href="result.preview_url" target="_blank" |
||||
>Preview: {{ result.preview_url }}</a |
||||
> |
||||
<br /><small>(Preview will be ready in ~30 seconds)</small> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
apiUrl: import.meta.env.VITE_API_URL || "", |
||||
user: null, |
||||
repos: [], |
||||
selectedRepo: "", |
||||
prompt: "", |
||||
generating: false, |
||||
operations: null, |
||||
submitting: false, |
||||
result: null, |
||||
error: null, |
||||
}; |
||||
}, |
||||
async mounted() { |
||||
await this.checkAuth(); |
||||
if (this.user) { |
||||
await this.loadRepos(); |
||||
} |
||||
}, |
||||
methods: { |
||||
async checkAuth() { |
||||
try { |
||||
const res = await fetch(`${this.apiUrl}/api/auth/user`, { |
||||
credentials: "include", |
||||
}); |
||||
if (res.ok) { |
||||
this.user = await res.json(); |
||||
} |
||||
} catch (e) { |
||||
console.error("Auth check failed:", e); |
||||
} |
||||
}, |
||||
async logout() { |
||||
await fetch(`${this.apiUrl}/api/auth/logout`, { |
||||
method: "POST", |
||||
credentials: "include", |
||||
}); |
||||
this.user = null; |
||||
this.repos = []; |
||||
this.clearState(); |
||||
}, |
||||
async loadRepos() { |
||||
try { |
||||
const res = await fetch(`${this.apiUrl}/api/repos`, { |
||||
credentials: "include", |
||||
}); |
||||
if (res.ok) { |
||||
this.repos = await res.json(); |
||||
} |
||||
} catch (e) { |
||||
console.error("Failed to load repos:", e); |
||||
} |
||||
}, |
||||
clearState() { |
||||
this.prompt = ""; |
||||
this.operations = null; |
||||
this.result = null; |
||||
this.error = null; |
||||
}, |
||||
async generate() { |
||||
this.generating = true; |
||||
this.error = null; |
||||
this.operations = null; |
||||
this.result = null; |
||||
|
||||
try { |
||||
const res = await fetch(`${this.apiUrl}/api/generate`, { |
||||
method: "POST", |
||||
headers: { "Content-Type": "application/json" }, |
||||
credentials: "include", |
||||
body: JSON.stringify({ |
||||
repo: this.selectedRepo, |
||||
prompt: this.prompt, |
||||
}), |
||||
}); |
||||
|
||||
const data = await res.json(); |
||||
if (!res.ok) throw new Error(data.error || "Generation failed"); |
||||
|
||||
this.operations = data; |
||||
} catch (e) { |
||||
this.error = e.message; |
||||
} finally { |
||||
this.generating = false; |
||||
} |
||||
}, |
||||
async submit() { |
||||
this.submitting = true; |
||||
this.error = null; |
||||
|
||||
try { |
||||
const res = await fetch(`${this.apiUrl}/api/submit`, { |
||||
method: "POST", |
||||
headers: { "Content-Type": "application/json" }, |
||||
credentials: "include", |
||||
body: JSON.stringify({ |
||||
repo: this.selectedRepo, |
||||
operations: this.operations, |
||||
summary: this.operations.summary, |
||||
prompt: this.prompt, |
||||
}), |
||||
}); |
||||
|
||||
const data = await res.json(); |
||||
if (!res.ok) throw new Error(data.error || "Submit failed"); |
||||
|
||||
this.result = data; |
||||
this.operations = null; |
||||
} catch (e) { |
||||
this.error = e.message; |
||||
} finally { |
||||
this.submitting = false; |
||||
} |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style> |
||||
* { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
font-family: |
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; |
||||
background: #f5f5f5; |
||||
} |
||||
|
||||
#app { |
||||
max-width: 800px; |
||||
margin: 0 auto; |
||||
padding: 20px; |
||||
} |
||||
|
||||
header { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
margin-bottom: 30px; |
||||
} |
||||
|
||||
h1 { |
||||
margin: 0; |
||||
font-size: 24px; |
||||
} |
||||
|
||||
.user-info { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 10px; |
||||
} |
||||
|
||||
.section { |
||||
background: white; |
||||
padding: 20px; |
||||
border-radius: 8px; |
||||
margin-bottom: 20px; |
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
||||
} |
||||
|
||||
label { |
||||
display: block; |
||||
font-weight: 600; |
||||
margin-bottom: 8px; |
||||
} |
||||
|
||||
select, |
||||
textarea { |
||||
width: 100%; |
||||
padding: 10px; |
||||
border: 1px solid #ddd; |
||||
border-radius: 4px; |
||||
font-size: 16px; |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
textarea { |
||||
resize: vertical; |
||||
} |
||||
|
||||
.btn { |
||||
display: inline-block; |
||||
padding: 10px 20px; |
||||
background: #4a9eff; |
||||
color: white; |
||||
border: none; |
||||
border-radius: 4px; |
||||
font-size: 16px; |
||||
cursor: pointer; |
||||
text-decoration: none; |
||||
} |
||||
|
||||
.btn:hover { |
||||
background: #3a8eef; |
||||
} |
||||
|
||||
.btn:disabled { |
||||
background: #ccc; |
||||
cursor: not-allowed; |
||||
} |
||||
|
||||
.btn.secondary { |
||||
background: #666; |
||||
} |
||||
|
||||
.btn.primary { |
||||
background: #22c55e; |
||||
} |
||||
|
||||
.error { |
||||
background: #fee; |
||||
color: #c00; |
||||
padding: 15px; |
||||
border-radius: 4px; |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
.success { |
||||
background: #efe; |
||||
border: 1px solid #afa; |
||||
} |
||||
|
||||
.summary { |
||||
font-style: italic; |
||||
color: #666; |
||||
} |
||||
|
||||
.file-change { |
||||
border: 1px solid #ddd; |
||||
border-radius: 4px; |
||||
margin: 15px 0; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.file-header { |
||||
background: #f5f5f5; |
||||
padding: 10px; |
||||
display: flex; |
||||
gap: 10px; |
||||
align-items: center; |
||||
} |
||||
|
||||
.action { |
||||
padding: 2px 8px; |
||||
border-radius: 3px; |
||||
font-size: 12px; |
||||
font-weight: 600; |
||||
text-transform: uppercase; |
||||
} |
||||
|
||||
.action.modify { |
||||
background: #fef3c7; |
||||
color: #92400e; |
||||
} |
||||
.action.create { |
||||
background: #d1fae5; |
||||
color: #065f46; |
||||
} |
||||
.action.delete { |
||||
background: #fee2e2; |
||||
color: #991b1b; |
||||
} |
||||
|
||||
.path { |
||||
font-family: monospace; |
||||
font-size: 14px; |
||||
} |
||||
|
||||
.content { |
||||
margin: 0; |
||||
padding: 15px; |
||||
overflow-x: auto; |
||||
background: #fafafa; |
||||
font-size: 13px; |
||||
max-height: 300px; |
||||
overflow-y: auto; |
||||
} |
||||
|
||||
.actions { |
||||
display: flex; |
||||
gap: 10px; |
||||
margin-top: 20px; |
||||
} |
||||
|
||||
.login-section { |
||||
text-align: center; |
||||
padding: 60px 20px; |
||||
background: white; |
||||
border-radius: 8px; |
||||
} |
||||
|
||||
a { |
||||
color: #4a9eff; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
import { describe, it, expect } from "vitest"; |
||||
|
||||
import { mount } from "@vue/test-utils"; |
||||
import App from "../App.vue"; |
||||
|
||||
describe("App", () => { |
||||
it("mounts renders properly", () => { |
||||
const wrapper = mount(App); |
||||
expect(wrapper.text()).toContain("You did it!"); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import { createApp } from "vue"; |
||||
import { createPinia } from "pinia"; |
||||
|
||||
import App from "./App.vue"; |
||||
import router from "./router"; |
||||
|
||||
const app = createApp(App); |
||||
|
||||
app.use(createPinia()); |
||||
app.use(router); |
||||
|
||||
app.mount("#app"); |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
import { createRouter, createWebHistory } from "vue-router"; |
||||
|
||||
const router = createRouter({ |
||||
history: createWebHistory(import.meta.env.BASE_URL), |
||||
routes: [], |
||||
}); |
||||
|
||||
export default router; |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import { ref, computed } from "vue"; |
||||
import { defineStore } from "pinia"; |
||||
|
||||
export const useCounterStore = defineStore("counter", () => { |
||||
const count = ref(0); |
||||
const doubleCount = computed(() => count.value * 2); |
||||
function increment() { |
||||
count.value++; |
||||
} |
||||
|
||||
return { count, doubleCount, increment }; |
||||
}); |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
{ |
||||
"extends": "@vue/tsconfig/tsconfig.dom.json", |
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"], |
||||
"exclude": ["src/**/__tests__/*"], |
||||
"compilerOptions": { |
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", |
||||
|
||||
"paths": { |
||||
"@/*": ["./src/*"] |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
{ |
||||
"files": [], |
||||
"references": [ |
||||
{ |
||||
"path": "./tsconfig.node.json" |
||||
}, |
||||
{ |
||||
"path": "./tsconfig.app.json" |
||||
}, |
||||
{ |
||||
"path": "./tsconfig.vitest.json" |
||||
} |
||||
] |
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
{ |
||||
"extends": "@tsconfig/node24/tsconfig.json", |
||||
"include": [ |
||||
"vite.config.*", |
||||
"vitest.config.*", |
||||
"cypress.config.*", |
||||
"nightwatch.conf.*", |
||||
"playwright.config.*", |
||||
"eslint.config.*" |
||||
], |
||||
"compilerOptions": { |
||||
"noEmit": true, |
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", |
||||
|
||||
"module": "ESNext", |
||||
"moduleResolution": "Bundler", |
||||
"types": ["node"] |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
{ |
||||
"extends": "./tsconfig.app.json", |
||||
"include": ["src/**/__tests__/*", "env.d.ts"], |
||||
"exclude": [], |
||||
"compilerOptions": { |
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", |
||||
|
||||
"lib": [], |
||||
"types": ["node", "jsdom"] |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
import { fileURLToPath, URL } from 'node:url' |
||||
|
||||
import { defineConfig } from 'vite' |
||||
import vue from '@vitejs/plugin-vue' |
||||
import vueJsx from '@vitejs/plugin-vue-jsx' |
||||
import vueDevTools from 'vite-plugin-vue-devtools' |
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({ |
||||
plugins: [ |
||||
vue(), |
||||
vueJsx(), |
||||
vueDevTools(), |
||||
], |
||||
resolve: { |
||||
alias: { |
||||
'@': fileURLToPath(new URL('./src', import.meta.url)) |
||||
}, |
||||
}, |
||||
}) |
||||
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
import { fileURLToPath } from 'node:url' |
||||
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' |
||||
import viteConfig from './vite.config' |
||||
|
||||
export default mergeConfig( |
||||
viteConfig, |
||||
defineConfig({ |
||||
test: { |
||||
environment: 'jsdom', |
||||
exclude: [...configDefaults.exclude, 'e2e/**'], |
||||
root: fileURLToPath(new URL('./', import.meta.url)), |
||||
}, |
||||
}), |
||||
) |
||||
Loading…
Reference in new issue