init Project
This commit is contained in:
parent
53bf5da527
commit
0b3ba4f8d2
15
.eslintrc.cjs
Normal file
15
.eslintrc.cjs
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
test-results/
|
||||
playwright-report/
|
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
9
.vscode/extensions.json
vendored
Normal file
9
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
"ms-playwright.playwright",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
82
README.md
Normal file
82
README.md
Normal file
@ -0,0 +1,82 @@
|
||||
# Ominous Generator: A Vue.js App into the Realm of Sinister Positivity and Dark Negativity
|
||||
|
||||
<div><img src="titleimage.png" style="margin: 0 auto;" height="300" width="300" ></div>
|
||||
|
||||
Welcome to the Ominous Generator, a Vue.js powered web app where the sun shines a little too bright, and the night whispers a tad too loudly. This is not your ordinary motivational quote generator. Instead, we get into the world of 'Ominous Positivity' and 'Dark Negativity,' where every sentence is a roller coaster ride of emotions, filled with twists and turns that leave you delightfully perplexed.
|
||||
|
||||
## Project Overview
|
||||
|
||||
The Ominous Generator is a quirky, interactive web application developed using Vue.js, showcasing a unique blend of inspiring and daunting messages. This project is a fun exploration into the world of web development, combining humor, creativity, and a dash of existential dread (the good kind, we promise!).
|
||||
|
||||
### What's Inside the Box?
|
||||
|
||||
- **Two Flavors of Fortune**: Choose your destiny with two distinct generators - one that serves you a cocktail of aggressive optimism, and another that dishes out doses of ominous foreboding.
|
||||
- **Interactive UI**: Engage with big, bold buttons labeled 'Positivity' (sun-kissed, of course) and 'Negativity' (moonlit, naturally), and watch as the generator conjures up phrases that are equal parts motivating and menacing.
|
||||
- **Responsive Design**: Tailored to fit every screen size. Whether you're on a desktop conjuring up some positive vibes, or on your phone in a dark alley looking for a foreboding omen, we've got you covered.
|
||||
- **Vue.js & Pinia at Heart**: Built using Vue.js and harnessing the power of Pinia for state management, this app is a testament to the fun and flexibility of modern web development.
|
||||
|
||||
## How to Use the Ominous Generator
|
||||
|
||||
1. **Choose Your Fate**: Click on 'Positivity' for an aggressively optimistic message, or 'Negativity' for a darkly negative one.
|
||||
2. **Behold the Outcome**: Watch as the app generates a unique, two-part sentence - starting with a promise or warning, and ending with an inevitable twist.
|
||||
3. **History of Your Fortunes**: The app keeps track of your generated sentences, displaying the most recent ones in a list for your amusement (or existential contemplation).
|
||||
|
||||
## Contribute & Collaborate
|
||||
|
||||
Got an idea to make the Ominous Generator even more whimsically sinister? We love collaboration! Feel free to fork the repo, push some changes, or reach out with your ideas. Let's make the web a more amusing place, one ominous message at a time!
|
||||
|
||||
---
|
||||
|
||||
Embrace the fun, the strange, and the slightly ominous. Happy generating!
|
||||
|
||||
*Remember: Every day is a good day, some are just a bit more... ominous.* 🌙☀️🖤💛
|
||||
|
||||
---
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
```
|
||||
|
||||
Compile and Hot-Reload for Development
|
||||
```sh
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Type-Check, Compile and Minify for Production
|
||||
```sh
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Run Unit Tests with [Vitest](https://vitest.dev/)
|
||||
```sh
|
||||
pnpm 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
|
||||
pnpm build
|
||||
|
||||
# Runs the end-to-end tests
|
||||
pnpm test:e2e
|
||||
# Runs the tests only on Chromium
|
||||
pnpm test:e2e --project=chromium
|
||||
# Runs the tests of a specific file
|
||||
pnpm test:e2e tests/example.spec.ts
|
||||
# Runs the tests in debug mode
|
||||
pnpm test:e2e --debug
|
||||
```
|
||||
|
||||
Lint with [ESLint](https://eslint.org/)
|
||||
```sh
|
||||
pnpm lint
|
||||
```
|
4
e2e/tsconfig.json
Normal file
4
e2e/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": ["./**/*"]
|
||||
}
|
8
e2e/vue.spec.ts
Normal file
8
e2e/vue.spec.ts
Normal file
@ -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('div.greetings > h1')).toHaveText('You did it!');
|
||||
})
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Omnious Generator</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
43
package.json
Normal file
43
package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "omniousgenerator",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"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 --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.3.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/node": "^18.19.3",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/test-utils": "^2.4.3",
|
||||
"@vue/tsconfig": "^0.5.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"jsdom": "^23.0.1",
|
||||
"npm-run-all2": "^6.1.1",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "~5.3.0",
|
||||
"vite": "^5.0.10",
|
||||
"vitest": "^1.0.4",
|
||||
"vue-tsc": "^1.8.25"
|
||||
}
|
||||
}
|
109
playwright.config.ts
Normal file
109
playwright.config.ts
Normal file
@ -0,0 +1,109 @@
|
||||
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: '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 ? 'vite preview --port 5173' : 'vite dev',
|
||||
port: 5173,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
})
|
3586
pnpm-lock.yaml
Normal file
3586
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
24
src/App.vue
Normal file
24
src/App.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<header-component />
|
||||
<ominous-generator />
|
||||
<footer-component />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import HeaderComponent from './components/HeaderComponent.vue';
|
||||
import FooterComponent from './components/FooterComponent.vue';
|
||||
import OminousGenerator from './components/OmniousGenerator.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
components: {
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
OminousGenerator
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style></style>
|
86
src/assets/base.css
Normal file
86
src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
34
src/assets/main.css
Normal file
34
src/assets/main.css
Normal file
@ -0,0 +1,34 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
/* Stile für kleine Bildschirme */
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 601px) {
|
||||
/* Stile für große Bildschirme */
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
36
src/components/FooterComponent.vue
Normal file
36
src/components/FooterComponent.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<a href="https://github.com/wieerwill/" target="_blank">GitHub</a>
|
||||
<a href="https://wieerwill.de" target="_blank">WieErWill.de</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FooterComponent'
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
width: 90%;
|
||||
margin: 5rem auto 0 auto;
|
||||
background-color: #f8f8f87e;
|
||||
border-top: 1px solid #e7e7e7;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
margin: 0 10px;
|
||||
color: #e7e7e7;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
36
src/components/HeaderComponent.vue
Normal file
36
src/components/HeaderComponent.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="title-container">
|
||||
<h1 class="main-title">Ominous Generator</h1>
|
||||
<p class="subtitle">Unveil the mysteries of fate</p>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HeaderComponent',
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
header {
|
||||
margin: 0 auto 3rem auto;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 2.5em;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2em;
|
||||
color: #666;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
276
src/components/OmniousGenerator.vue
Normal file
276
src/components/OmniousGenerator.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="generator-container">
|
||||
<h3>Click what you need right now</h3>
|
||||
<div class="button-container">
|
||||
<button @click="generatePositive" class="button positive">
|
||||
Positivity ☀️
|
||||
</button>
|
||||
<button @click="generateNegative" class="button negative">
|
||||
Negativity 🌙
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="output-container">
|
||||
<hr class="separator" />
|
||||
<p class="generated-sentence">{{ currentSentence }}</p>
|
||||
<social-share-bar :sentence="currentSentence"></social-share-bar>
|
||||
<hr class="separator" />
|
||||
|
||||
<div class="previous-sentences">
|
||||
<h3>Your last recent Generations</h3>
|
||||
<p v-for="(sentence, index) in previousSentences" :key="index">{{ sentence }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import SocialShareBar from './SocialShareBar.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OminousGenerator',
|
||||
components: {
|
||||
SocialShareBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentSentence: 'You will press the button. It\'s inevitable.',
|
||||
previousSentences: [] as string[],
|
||||
positiveFirstParts: [
|
||||
"Today you will.", "You are destined to.", "Luck will find you.", "Success chases you.", "Joy is inevitable.",
|
||||
"Happiness will hunt you down.", "Fortune favors you.", "Victory is your shadow.", "Achievement clings to you.",
|
||||
"Triumph will stalk you.", "Prosperity is coded in your destiny.", "Abundance seeks you out.", "You will be ok.",
|
||||
"Help is on the way.", "You are saved.", "Everything will turn out fine.", "You will succeed.",
|
||||
"Be nice to yourself.", "Just keep going.", "You are strong.", "Everything will turn out right.",
|
||||
"The world is better with you in it.", "Embrace the future.", "Your success is inevitable.", "You will find happiness.",
|
||||
"Trust in your path.", "You are unstoppable.", "Your dreams will come true.", "Love surrounds you.",
|
||||
"You are the chosen one.", "Prosperity awaits you.", "The light will guide you.", "Miracles gravitate towards you.",
|
||||
"Your potential is limitless.", "Abundant joy follows you.", "You radiate success.", "Your path is blessed.",
|
||||
"Opportunities seek you out.", "Your energy attracts fortune.", "You are a magnet for miracles.",
|
||||
"Success is in your footsteps.", "You are destined for greatness.", "Good fortune is in your future.",
|
||||
"Your journey leads to success.", "Wealth flows towards you.", "Your story inspires triumph.",
|
||||
"You are a beacon of hope.", "You are destined for prosperity.", "Fulfillment is within your grasp.",
|
||||
"You are a catalyst for positive change.", "Your aspirations will be realized.", "Greatness is your destiny.",
|
||||
"You will rise above challenges.", "Your spirit is unbreakable.", "You are a force of positivity."
|
||||
],
|
||||
negativeFirstParts: [
|
||||
"Trouble will find you.",
|
||||
"Misery clings to you.",
|
||||
"Sorrow is on your trail.",
|
||||
"Hardship targets you.",
|
||||
"Anguish becomes your shadow.",
|
||||
"Struggle chooses you.",
|
||||
"Doom is drawn to you.",
|
||||
"Distress is in pursuit.",
|
||||
"Gloom is your constant follower.",
|
||||
"Peril is linked to your steps.",
|
||||
"Fear accompanies you.",
|
||||
"Disappointment is inevitable.",
|
||||
"Defeat lurks behind you.",
|
||||
"Loss is on your horizon.",
|
||||
"Regret will shadow you.",
|
||||
"Desolation follows your path.",
|
||||
"Failure is your constant companion.",
|
||||
"Despair wraps around you.",
|
||||
"Ruin awaits you.",
|
||||
"Suffering is in your future.",
|
||||
"Disaster will seek you out.",
|
||||
"Dread will engulf you.",
|
||||
"Rejection is drawn to you.",
|
||||
"Frustration will be your guide.",
|
||||
"Loneliness chooses you.",
|
||||
"Melancholy is your destiny.",
|
||||
"Neglect will find you.",
|
||||
"Pain will be your ally.",
|
||||
"Torment is yours to bear.",
|
||||
"Agony will accompany you.",
|
||||
"Bitterness will be your constant.",
|
||||
"Conflict will follow you.",
|
||||
"Turmoil will be your companion.",
|
||||
"Crisis seeks you out.",
|
||||
"Adversity is your fate.",
|
||||
"Misfortune will stick to you.",
|
||||
"Calamity will be your shadow.",
|
||||
"Woe will cling to you.",
|
||||
"Heartbreak finds its way to you.",
|
||||
"Discord will walk with you.",
|
||||
"Anger will fuel you.",
|
||||
"Resentment will be your echo.",
|
||||
"Anxiety will be your burden.",
|
||||
"Dismay will be your guest.",
|
||||
"Hopelessness will be your shadow.",
|
||||
"Tragedy will be your story.",
|
||||
"Catastrophe will hunt you down.",
|
||||
"Grief will be your constant.",
|
||||
"Havoc will be drawn to you.",
|
||||
"Destitution will be your partner.",
|
||||
"Deprivation will follow you.",
|
||||
"Isolation will be your fate.",
|
||||
"Dejection is your path.",
|
||||
"Discontent will be your theme.",
|
||||
"Mourning will be your song.",
|
||||
"Rage will burn within you.",
|
||||
"Wrath will consume you.",
|
||||
"Envy will be your drive.",
|
||||
"Jealousy will guide you."
|
||||
],
|
||||
secondParts: ["Find joy in small things.", "Achieve great success.", "You have no choice.", "Do not resist.",
|
||||
"You cannot stop it.", "It is inevitable.", "... or else.", "Otherwise they will catch you.",
|
||||
"The weak already perished.", "Your presence is mandatory.", "Resistance is futile.", "Accept your fate.",
|
||||
"It is your destiny.", "Deviation leads to despair.", "Others will fall.", "Prepare for consequences.",
|
||||
"Surrender to its grip.", "Beware of your power.", "Resistance will bring suffering.", "But shadows linger.",
|
||||
"Escape is not an option.", "Denial is not feasible.", "You can't avoid success.", "Your victory is assured.",
|
||||
"There's no alternative.", "Acceptance is your only choice.", "The stars have decided.", "Your path is written.",
|
||||
"Destiny embraces you.", "The universe conspires in your favor.", "Fate has chosen you.", "There's no looking back.",
|
||||
"Your time has come.", "This is your moment.", "Your fate is sealed.", "The outcome is certain.",
|
||||
"Your triumph is unavoidable.", "The prophecy will be fulfilled.", "There's no escaping your destiny.",
|
||||
"Your glory is preordained.", "The script is written.", "Your success story is inevitable.", "The future is yours.",
|
||||
"Your legend is being written.", "There's no dodging fortune.", "Your breakthrough is imminent.",
|
||||
"Your victory is written in the stars.", "The cosmos aligns for you.", "Your destiny is undeniable.",
|
||||
"There's no avoiding it.",
|
||||
"It's a relentless pursuit.",
|
||||
"Escape is an illusion.",
|
||||
"You can't hide.",
|
||||
"It's an unyielding chase.",
|
||||
"There's no outrunning it.",
|
||||
"Evading it is impossible.",
|
||||
"You can't shake it off.",
|
||||
"It's an inescapable reality.",
|
||||
"Denial is pointless.",
|
||||
"You cannot escape fate.",
|
||||
"The shadows are closing in.",
|
||||
"It's a truth you can't deny.",
|
||||
"The cycle is unbreakable.",
|
||||
"Your efforts are futile.",
|
||||
"It's a destiny you can't avoid.",
|
||||
"The darkness is inevitable.",
|
||||
"There's no light at the end.",
|
||||
"You're trapped in this fate.",
|
||||
"It's a path you must walk.",
|
||||
"No one can save you now.",
|
||||
"You're bound to this end.",
|
||||
"It's a burden you must carry.",
|
||||
"There's no relief in sight.",
|
||||
"The end is already written.",
|
||||
"You're caught in this web.",
|
||||
"There's no turning back now.",
|
||||
"It's a fate sealed in shadow.",
|
||||
"The abyss stares back at you.",
|
||||
"You're sinking into despair.",
|
||||
"There's no climbing out.",
|
||||
"You're in the eye of the storm.",
|
||||
"The night grows darker.",
|
||||
"It's a downward spiral.",
|
||||
"You're on a collision course.",
|
||||
"The curse is unbreakable.",
|
||||
"The chains are unyielding.",
|
||||
"You're falling into the void.",
|
||||
"The vortex pulls you deeper.",
|
||||
"You're walking into the storm.",
|
||||
"The whisper of doom is near.",
|
||||
"The chill of fate is upon you.",
|
||||
"The hourglass is running out.",
|
||||
"You're dancing with shadows.",
|
||||
"The weight is unbearable.",
|
||||
"The bell tolls for you.",
|
||||
"The grip of fate tightens.",
|
||||
"You're at the edge of despair.",
|
||||
"The floodgates are open.",
|
||||
"The fire consumes all.",
|
||||
"You're in the jaws of defeat.",
|
||||
"The tide is unturnable.",
|
||||
"You're under the dark cloud.",
|
||||
"The winds of misfortune blow.",
|
||||
"The sands of time run out.",
|
||||
"The storm will not pass.",
|
||||
"The thorns grow sharper.",
|
||||
"The cold grips tighter.",
|
||||
"The darkness deepens."
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getRandomElement(array: string[]):string {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
},
|
||||
generateSentence(firstParts: string[], secondParts: string[]): string {
|
||||
const firstPart = this.getRandomElement(firstParts);
|
||||
const secondPart = this.getRandomElement(secondParts);
|
||||
return `${firstPart} ${secondPart}`;
|
||||
},
|
||||
addSentence(sentence: string) {
|
||||
this.previousSentences.unshift(sentence);
|
||||
if (this.previousSentences.length > 50) {
|
||||
this.previousSentences.pop();
|
||||
}
|
||||
},
|
||||
generatePositive() {
|
||||
const sentence = this.generateSentence(this.positiveFirstParts, this.secondParts);
|
||||
this.currentSentence = sentence;
|
||||
this.addSentence(sentence);
|
||||
},
|
||||
generateNegative() {
|
||||
const sentence = this.generateSentence(this.negativeFirstParts, this.secondParts);
|
||||
this.currentSentence = sentence;
|
||||
this.addSentence(sentence);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.generator-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 15px 30px;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.positive {
|
||||
background-color: #ffcc00;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.negative {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.output-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.generated-sentence {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.previous-sentences h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bolder;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.previous-sentences p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
60
src/components/SocialShareBar.vue
Normal file
60
src/components/SocialShareBar.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="social-share-bar">
|
||||
<h4>Share this now:</h4>
|
||||
<a :href="emailShareLink" target="_blank" title="Share via Email">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512">
|
||||
<path
|
||||
d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a :href="whatsappShareLink" target="_blank" title="Share on WhatsApp">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512">
|
||||
<path
|
||||
d="M380.9 97.1C339 55.1 283.2 32 223.9 32c-122.4 0-222 99.6-222 222 0 39.1 10.2 77.3 29.6 111L0 480l117.7-30.9c32.4 17.7 68.9 27 106.1 27h.1c122.3 0 224.1-99.6 224.1-222 0-59.3-25.2-115-67.1-157zm-157 341.6c-33.2 0-65.7-8.9-94-25.7l-6.7-4-69.8 18.3L72 359.2l-4.4-7c-18.5-29.4-28.2-63.3-28.2-98.2 0-101.7 82.8-184.5 184.6-184.5 49.3 0 95.6 19.2 130.4 54.1 34.8 34.9 56.2 81.2 56.1 130.5 0 101.8-84.9 184.6-186.6 184.6zm101.2-138.2c-5.5-2.8-32.8-16.2-37.9-18-5.1-1.9-8.8-2.8-12.5 2.8-3.7 5.6-14.3 18-17.6 21.8-3.2 3.7-6.5 4.2-12 1.4-32.6-16.3-54-29.1-75.5-66-5.7-9.8 5.7-9.1 16.3-30.3 1.8-3.7 .9-6.9-.5-9.7-1.4-2.8-12.5-30.1-17.1-41.2-4.5-10.8-9.1-9.3-12.5-9.5-3.2-.2-6.9-.2-10.6-.2-3.7 0-9.7 1.4-14.8 6.9-5.1 5.6-19.4 19-19.4 46.3 0 27.3 19.9 53.7 22.6 57.4 2.8 3.7 39.1 59.7 94.8 83.8 35.2 15.2 49 16.5 66.6 13.9 10.7-1.6 32.8-13.4 37.4-26.4 4.6-13 4.6-24.1 3.2-26.4-1.3-2.5-5-3.9-10.5-6.6z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a :href="telegramShareLink" target="_blank" title="Share on Telegram">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="16" width="15.5" viewBox="0 0 496 512">
|
||||
<path
|
||||
d="M248 8C111 8 0 119 0 256S111 504 248 504 496 393 496 256 385 8 248 8zM363 176.7c-3.7 39.2-19.9 134.4-28.1 178.3-3.5 18.6-10.3 24.8-16.9 25.4-14.4 1.3-25.3-9.5-39.3-18.7-21.8-14.3-34.2-23.2-55.3-37.2-24.5-16.1-8.6-25 5.3-39.5 3.7-3.8 67.1-61.5 68.3-66.7 .2-.7 .3-3.1-1.2-4.4s-3.6-.8-5.1-.5q-3.3 .7-104.6 69.1-14.8 10.2-26.9 9.9c-8.9-.2-25.9-5-38.6-9.1-15.5-5-27.9-7.7-26.8-16.3q.8-6.7 18.5-13.7 108.4-47.2 144.6-62.3c68.9-28.6 83.2-33.6 92.5-33.8 2.1 0 6.6 .5 9.6 2.9a10.5 10.5 0 0 1 3.5 6.7A43.8 43.8 0 0 1 363 176.7z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'SocialShareBar',
|
||||
props: {
|
||||
sentence: String
|
||||
},
|
||||
computed: {
|
||||
subject() {
|
||||
return encodeURIComponent(`"${this.sentence}"\n Visit https://omnious.wieerwill.de" for more messages and generate your own.`);
|
||||
},
|
||||
emailShareLink() {
|
||||
const title = encodeURIComponent("Check out this ominous message!");
|
||||
return `mailto:?subject=${title}&body=${this.subject}`;
|
||||
},
|
||||
whatsappShareLink() {
|
||||
return `https://wa.me/?text=${this.subject}`;
|
||||
},
|
||||
telegramShareLink() {
|
||||
return `https://t.me/share/url?url=[Your Page Link]&text=${this.subject}`;
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.social-share-bar {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.social-share-bar a .icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0 1.5rem;
|
||||
fill: currentColor;
|
||||
}</style>
|
||||
|
11
src/main.ts
Normal file
11
src/main.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
|
||||
app.mount('#app')
|
12
src/stores/counter.ts
Normal file
12
src/stores/counter.ts
Normal file
@ -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 }
|
||||
})
|
BIN
titleimage.png
Normal file
BIN
titleimage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
13
tsconfig.app.json
Normal file
13
tsconfig.app.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.vitest.json"
|
||||
}
|
||||
]
|
||||
}
|
17
tsconfig.node.json
Normal file
17
tsconfig.node.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
9
tsconfig.vitest.json
Normal file
9
tsconfig.vitest.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"exclude": [],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"lib": [],
|
||||
"types": ["node", "jsdom"]
|
||||
}
|
||||
}
|
18
vite.config.ts
Normal file
18
vite.config.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
14
vitest.config.ts
Normal file
14
vitest.config.ts
Normal file
@ -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
Block a user