@tsparticles/template-login 4.1.3 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +3 -0
  3. package/package.json +4 -3
  4. package/template/angular/package.json +34 -0
  5. package/template/angular/src/app/app.component.css +151 -0
  6. package/template/angular/src/app/app.component.html +32 -0
  7. package/template/angular/src/app/app.component.ts +105 -0
  8. package/template/angular/src/app/app.module.ts +13 -0
  9. package/template/angular-confetti/package.json +36 -0
  10. package/template/angular-confetti/src/app/app.component.css +151 -0
  11. package/template/angular-confetti/src/app/app.component.html +32 -0
  12. package/template/angular-confetti/src/app/app.component.ts +105 -0
  13. package/template/angular-confetti/src/app/app.module.ts +13 -0
  14. package/template/angular-fireworks/package.json +36 -0
  15. package/template/angular-fireworks/src/app/app.component.css +151 -0
  16. package/template/angular-fireworks/src/app/app.component.html +32 -0
  17. package/template/angular-fireworks/src/app/app.component.ts +105 -0
  18. package/template/angular-fireworks/src/app/app.module.ts +13 -0
  19. package/template/astro/package.json +22 -0
  20. package/template/astro/src/pages/index.astro +173 -0
  21. package/template/ember/app/templates/application.hbs +61 -0
  22. package/template/ember/package.json +42 -0
  23. package/template/inferno/package.json +24 -0
  24. package/template/inferno/src/App.css +151 -0
  25. package/template/inferno/src/App.tsx +158 -0
  26. package/template/jquery/package.json +22 -0
  27. package/template/jquery/src/main.ts +110 -0
  28. package/template/jquery/src/style.css +151 -0
  29. package/template/lit/package.json +22 -0
  30. package/template/lit/src/my-app.ts +143 -0
  31. package/template/nextjs/package.json +25 -0
  32. package/template/nextjs/src/app/page.tsx +168 -0
  33. package/template/nextjs/src/app/providers.tsx +13 -0
  34. package/template/nuxt2/package.json +17 -0
  35. package/template/nuxt2/pages/index.vue +303 -0
  36. package/template/nuxt3/app.vue +288 -0
  37. package/template/nuxt3/package.json +21 -0
  38. package/template/nuxt4/app.vue +288 -0
  39. package/template/nuxt4/package.json +21 -0
  40. package/template/preact/package.json +23 -0
  41. package/template/preact/src/App.css +151 -0
  42. package/template/preact/src/App.tsx +140 -0
  43. package/template/qwik/package.json +22 -0
  44. package/template/qwik/src/App.tsx +139 -0
  45. package/template/react/package.json +19 -0
  46. package/template/react/src/App.css +151 -0
  47. package/template/react/src/App.tsx +140 -0
  48. package/template/riot/package.json +24 -0
  49. package/template/riot/src/app.riot +156 -0
  50. package/template/solid/package.json +18 -0
  51. package/template/solid/src/App.tsx +169 -0
  52. package/template/solid/src/index.css +151 -0
  53. package/template/solid/src/main.tsx +9 -0
  54. package/template/stencil/package.json +20 -0
  55. package/template/stencil/src/components/app-home/app-home.tsx +164 -0
  56. package/template/svelte/package.json +20 -0
  57. package/template/svelte/src/App.svelte +325 -0
  58. package/template/svelte/src/main.ts +12 -0
  59. package/template/vanilla/LICENSE +21 -0
  60. package/template/vanilla/package.json +1 -1
  61. package/template/vanilla/src/main.ts +1 -1
  62. package/template/vue2/package.json +24 -0
  63. package/template/vue2/src/App.vue +287 -0
  64. package/template/vue3/package.json +19 -0
  65. package/template/vue3/src/App.vue +286 -0
  66. package/template/vue3/src/main.ts +14 -0
  67. package/template/webcomponents/package.json +21 -0
  68. package/template/webcomponents/src/main.ts +125 -0
  69. package/template/webcomponents/src/style.css +151 -0
@@ -0,0 +1,61 @@
1
+ <div id="particles-container" {{did-insert this.initParticles}} {{particles options=this.options}}>
2
+ </div>
3
+
4
+ <div class="auth-container">
5
+ <div class="auth-card">
6
+ <div class="auth-header">
7
+ <h1>{{if this.isLogin "Login" "Register"}}</h1>
8
+ <button class="theme-btn" type="button" {{on "click" this.toggleTheme}} aria-label="Toggle theme">{{if (eq this.theme "dark") "🌙" "☀️"}}</button>
9
+ </div>
10
+ <form {{on "submit" this.handleSubmit}} novalidate>
11
+ <div class="form-group">
12
+ <label for="email">Email</label>
13
+ <Input id="email" @type="email" @value={{this.email}} placeholder="you@example.com" required />
14
+ <span class="error">{{this.emailErr}}</span>
15
+ </div>
16
+ <div class="form-group">
17
+ <label for="password">Password</label>
18
+ <Input id="password" @type="password" @value={{this.password}} placeholder="Enter password" required />
19
+ <span class="error">{{this.passErr}}</span>
20
+ </div>
21
+ {{#unless this.isLogin}}
22
+ <div class="form-group">
23
+ <label for="confirmPassword">Confirm Password</label>
24
+ <Input id="confirmPassword" @type="password" @value={{this.confirm}} placeholder="Confirm password" />
25
+ <span class="error">{{this.confirmErr}}</span>
26
+ </div>
27
+ {{/unless}}
28
+ <button type="submit">{{if this.isLogin "Sign In" "Create Account"}}</button>
29
+ </form>
30
+ <p class="toggle-text">
31
+ <span>{{if this.isLogin "Don't have an account?" "Already have an account?"}}</span>
32
+ <a href="#" {{on "click" this.toggleMode}}>{{if this.isLogin "Register" "Login"}}</a>
33
+ </p>
34
+ </div>
35
+ </div>
36
+
37
+ {{outlet}}
38
+
39
+ <style>
40
+ :root { --bg: #0f0f1a; --card-bg: rgba(255, 255, 255, 0.05); --text: #fff; --border: rgba(255, 255, 255, 0.1); --input-bg: rgba(255, 255, 255, 0.08); --accent: #6c5ce7; --accent-hover: #a29bfe; --error: #e74c3c; }
41
+ [data-theme="light"] { --bg: #f0f0f5; --card-bg: rgba(255, 255, 255, 0.8); --text: #1a1a2e; --border: rgba(0, 0, 0, 0.1); --input-bg: rgba(0, 0, 0, 0.05); }
42
+ * { box-sizing: border-box; }
43
+ body { margin: 0; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; background: var(--bg); color: var(--text); transition: background 0.3s, color 0.3s; }
44
+ .auth-container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; width: 100%; max-width: 400px; padding: 1rem; }
45
+ .auth-card { background: var(--card-bg); backdrop-filter: blur(10px); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); }
46
+ .auth-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; }
47
+ .auth-header h1 { margin: 0; font-size: 1.8em; }
48
+ .theme-btn { background: none; border: 1px solid var(--border); border-radius: 8px; padding: 0.4em 0.6em; font-size: 1.2em; cursor: pointer; transition: background 0.2s; }
49
+ .theme-btn:hover { background: var(--input-bg); }
50
+ .form-group { margin-bottom: 1rem; }
51
+ label { display: block; margin-bottom: 0.3em; font-size: 0.9em; opacity: 0.8; }
52
+ input { width: 100%; padding: 0.7em 1em; font-size: 1em; border: 1px solid var(--border); border-radius: 8px; background: var(--input-bg); color: var(--text); outline: none; transition: border-color 0.2s; }
53
+ input:focus { border-color: var(--accent); }
54
+ input::placeholder { color: var(--text); opacity: 0.4; }
55
+ .error { display: block; margin-top: 0.3em; font-size: 0.8em; color: var(--error); min-height: 1em; }
56
+ button[type="submit"] { width: 100%; padding: 0.8em; font-size: 1em; font-weight: 600; border: none; border-radius: 8px; background: var(--accent); color: #fff; cursor: pointer; transition: background 0.2s; margin-top: 0.5rem; }
57
+ button[type="submit"]:hover { background: var(--accent-hover); }
58
+ .toggle-text { text-align: center; margin-top: 1.5rem; font-size: 0.9em; opacity: 0.7; }
59
+ .toggle-text a { color: var(--accent); text-decoration: none; margin-left: 0.3em; }
60
+ .toggle-text a:hover { text-decoration: underline; }
61
+ </style>
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "scripts": {
6
+ "dev": "ember serve",
7
+ "build": "ember build",
8
+ "preview": "ember serve"
9
+ },
10
+ "devDependencies": {
11
+ "@ember/optional-features": "^3.0.0",
12
+ "@glimmer/component": "^2.1.1",
13
+ "@glimmer/tracking": "^1.1.2",
14
+ "broccoli-asset-rev": "^3.0.0",
15
+ "ember-auto-import": "^2.13.1",
16
+ "ember-cli": "~6.12.0",
17
+ "ember-cli-babel": "^8.3.1",
18
+ "ember-cli-htmlbars": "^7.0.1",
19
+ "ember-cli-inject-live-reload": "^2.1.0",
20
+ "ember-cli-sri": "^2.1.1",
21
+ "ember-cli-terser": "^4.0.2",
22
+ "ember-load-initializers": "^3.0.1",
23
+ "ember-modifier": "^4.3.0",
24
+ "ember-resolver": "^13.2.0",
25
+ "ember-source": "~6.12.0",
26
+ "loader.js": "^4.7.0",
27
+ "typescript": "^6.0.3",
28
+ "webpack": "^5.106.1"
29
+ },
30
+ "dependencies": {
31
+ "@tsparticles/ember": "^4.1.3",
32
+ "@tsparticles/slim": "^4.1.3",
33
+ "@tsparticles/configs": "^4.1.3",
34
+ "@tsparticles/engine": "^4.1.3"
35
+ },
36
+ "ember": {
37
+ "edition": "octane"
38
+ },
39
+ "ember-addon": {
40
+ "configPath": "tests/dummy/config"
41
+ }
42
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "inferno": "^9.1.0",
13
+ "@tsparticles/inferno": "^4.1.3",
14
+ "@tsparticles/slim": "^4.1.3",
15
+ "@tsparticles/configs": "^4.1.3",
16
+ "@tsparticles/engine": "^4.1.3"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^6.0.3",
20
+ "vite": "^8.0.14",
21
+ "babel-plugin-inferno": "^6.8.5",
22
+ "vite-plugin-inferno": "^2.0.1"
23
+ }
24
+ }
@@ -0,0 +1,151 @@
1
+ :root {
2
+ --bg: #0f0f1a;
3
+ --card-bg: rgba(255, 255, 255, 0.05);
4
+ --text: #fff;
5
+ --border: rgba(255, 255, 255, 0.1);
6
+ --input-bg: rgba(255, 255, 255, 0.08);
7
+ --accent: #6c5ce7;
8
+ --accent-hover: #a29bfe;
9
+ --error: #e74c3c;
10
+ }
11
+
12
+ [data-theme="light"] {
13
+ --bg: #f0f0f5;
14
+ --card-bg: rgba(255, 255, 255, 0.8);
15
+ --text: #1a1a2e;
16
+ --border: rgba(0, 0, 0, 0.1);
17
+ --input-bg: rgba(0, 0, 0, 0.05);
18
+ }
19
+
20
+ * {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ body {
25
+ margin: 0;
26
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
27
+ background: var(--bg);
28
+ color: var(--text);
29
+ transition: background 0.3s, color 0.3s;
30
+ }
31
+
32
+ .auth-container {
33
+ position: absolute;
34
+ top: 50%;
35
+ left: 50%;
36
+ transform: translate(-50%, -50%);
37
+ z-index: 10;
38
+ width: 100%;
39
+ max-width: 400px;
40
+ padding: 1rem;
41
+ }
42
+
43
+ .auth-card {
44
+ background: var(--card-bg);
45
+ backdrop-filter: blur(10px);
46
+ border: 1px solid var(--border);
47
+ border-radius: 16px;
48
+ padding: 2rem;
49
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
50
+ }
51
+
52
+ .auth-header {
53
+ display: flex;
54
+ justify-content: space-between;
55
+ align-items: center;
56
+ margin-bottom: 1.5rem;
57
+ }
58
+
59
+ .auth-header h1 {
60
+ margin: 0;
61
+ font-size: 1.8em;
62
+ }
63
+
64
+ .theme-btn {
65
+ background: none;
66
+ border: 1px solid var(--border);
67
+ border-radius: 8px;
68
+ padding: 0.4em 0.6em;
69
+ font-size: 1.2em;
70
+ cursor: pointer;
71
+ transition: background 0.2s;
72
+ }
73
+
74
+ .theme-btn:hover {
75
+ background: var(--input-bg);
76
+ }
77
+
78
+ .form-group {
79
+ margin-bottom: 1rem;
80
+ }
81
+
82
+ label {
83
+ display: block;
84
+ margin-bottom: 0.3em;
85
+ font-size: 0.9em;
86
+ opacity: 0.8;
87
+ }
88
+
89
+ input {
90
+ width: 100%;
91
+ padding: 0.7em 1em;
92
+ font-size: 1em;
93
+ border: 1px solid var(--border);
94
+ border-radius: 8px;
95
+ background: var(--input-bg);
96
+ color: var(--text);
97
+ outline: none;
98
+ transition: border-color 0.2s;
99
+ }
100
+
101
+ input:focus {
102
+ border-color: var(--accent);
103
+ }
104
+
105
+ input::placeholder {
106
+ color: var(--text);
107
+ opacity: 0.4;
108
+ }
109
+
110
+ .error {
111
+ display: block;
112
+ margin-top: 0.3em;
113
+ font-size: 0.8em;
114
+ color: var(--error);
115
+ min-height: 1em;
116
+ }
117
+
118
+ button[type="submit"] {
119
+ width: 100%;
120
+ padding: 0.8em;
121
+ font-size: 1em;
122
+ font-weight: 600;
123
+ border: none;
124
+ border-radius: 8px;
125
+ background: var(--accent);
126
+ color: #fff;
127
+ cursor: pointer;
128
+ transition: background 0.2s;
129
+ margin-top: 0.5rem;
130
+ }
131
+
132
+ button[type="submit"]:hover {
133
+ background: var(--accent-hover);
134
+ }
135
+
136
+ .toggle-text {
137
+ text-align: center;
138
+ margin-top: 1.5rem;
139
+ font-size: 0.9em;
140
+ opacity: 0.7;
141
+ }
142
+
143
+ .toggle-text a {
144
+ color: var(--accent);
145
+ text-decoration: none;
146
+ margin-left: 0.3em;
147
+ }
148
+
149
+ .toggle-text a:hover {
150
+ text-decoration: underline;
151
+ }
@@ -0,0 +1,158 @@
1
+ import { Component, type ComponentType } from "inferno";
2
+ import Particles, { ParticlesProvider } from "@tsparticles/inferno";
3
+ import { loadSlim } from "@tsparticles/slim";
4
+ import type { Engine, ISourceOptions } from "@tsparticles/engine";
5
+ import "./App.css";
6
+
7
+ const options: ISourceOptions = {
8
+ background: { color: { value: "transparent" } },
9
+ fpsLimit: 60,
10
+ particles: {
11
+ number: { value: 60, density: { enable: true } },
12
+ color: { value: ["#6c5ce7", "#a29bfe", "#fd79a8", "#00cec9"] },
13
+ shape: { type: "circle" },
14
+ opacity: { value: 0.5, random: true },
15
+ size: { value: { min: 1, max: 3 }, random: true },
16
+ move: { enable: true, speed: 1, direction: "none", random: true, straight: false, outModes: { default: "out" } },
17
+ },
18
+ interactivity: {
19
+ events: { onHover: { enable: true, mode: "bubble" } },
20
+ modes: { bubble: { distance: 200, size: 6, duration: 2, opacity: 0.8 } },
21
+ },
22
+ detectRetina: true,
23
+ };
24
+
25
+ function validateEmail(val: string): boolean {
26
+ return /^[^\s@]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/.test(val);
27
+ }
28
+
29
+ interface AppState {
30
+ isLogin: boolean;
31
+ theme: string;
32
+ email: string;
33
+ password: string;
34
+ confirm: string;
35
+ emailErr: string;
36
+ passErr: string;
37
+ confirmErr: string;
38
+ }
39
+
40
+ const App: ComponentType<Record<string, unknown>> = class App extends Component<Record<string, unknown>, AppState> {
41
+ constructor(props: Record<string, unknown>) {
42
+ super(props);
43
+ this.state = {
44
+ isLogin: true,
45
+ theme: localStorage.getItem("theme") || "dark",
46
+ email: "",
47
+ password: "",
48
+ confirm: "",
49
+ emailErr: "",
50
+ passErr: "",
51
+ confirmErr: "",
52
+ };
53
+ }
54
+
55
+ componentDidMount(): void {
56
+ document.documentElement.setAttribute("data-theme", this.state.theme);
57
+ }
58
+
59
+ componentDidUpdate(): void {
60
+ document.documentElement.setAttribute("data-theme", this.state.theme);
61
+ }
62
+
63
+ private setTheme(t: string): void {
64
+ this.setState({ theme: t });
65
+ document.documentElement.setAttribute("data-theme", t);
66
+ localStorage.setItem("theme", t);
67
+ }
68
+
69
+ private toggleTheme = (): void => {
70
+ this.setTheme(this.state.theme === "dark" ? "light" : "dark");
71
+ };
72
+
73
+ private handleSubmit = (e: Event): void => {
74
+ e.preventDefault();
75
+ let valid = true;
76
+
77
+ if (!this.state.email || !validateEmail(this.state.email)) {
78
+ this.setState({ emailErr: "Please enter a valid email address" });
79
+ valid = false;
80
+ } else {
81
+ this.setState({ emailErr: "" });
82
+ }
83
+
84
+ if (!this.state.password || this.state.password.length < 6) {
85
+ this.setState({ passErr: "Password must be at least 6 characters" });
86
+ valid = false;
87
+ } else {
88
+ this.setState({ passErr: "" });
89
+ }
90
+
91
+ if (!this.state.isLogin) {
92
+ if (this.state.password !== this.state.confirm) {
93
+ this.setState({ confirmErr: "Passwords do not match" });
94
+ valid = false;
95
+ } else {
96
+ this.setState({ confirmErr: "" });
97
+ }
98
+ }
99
+
100
+ if (!valid) return;
101
+
102
+ if (this.state.isLogin) {
103
+ alert("Logged in successfully! (demo)");
104
+ } else {
105
+ alert("Account created successfully! (demo)");
106
+ this.setState({ isLogin: true });
107
+ }
108
+
109
+ this.setState({ email: "", password: "", confirm: "" });
110
+ };
111
+
112
+ private toggleMode = (e: Event): void => {
113
+ e.preventDefault();
114
+ this.setState({ isLogin: !this.state.isLogin, emailErr: "", passErr: "", confirmErr: "" });
115
+ };
116
+
117
+ render() {
118
+ return (
119
+ <ParticlesProvider init={async (engine: Engine): Promise<void> => { await loadSlim(engine); }}>
120
+ <div className="auth-container">
121
+ <div className="auth-card">
122
+ <div className="auth-header">
123
+ <h1>{this.state.isLogin ? "Login" : "Register"}</h1>
124
+ <button className="theme-btn" onClick={this.toggleTheme} aria-label="Toggle theme">{this.state.theme === "dark" ? "🌙" : "☀️"}</button>
125
+ </div>
126
+ <form onSubmit={this.handleSubmit} noValidate>
127
+ <div className="form-group">
128
+ <label htmlFor="email">Email</label>
129
+ <input id="email" type="email" placeholder="you@example.com" value={this.state.email} onInput={(e) => this.setState({ email: (e.target as HTMLInputElement).value })} required />
130
+ <span className="error">{this.state.emailErr}</span>
131
+ </div>
132
+ <div className="form-group">
133
+ <label htmlFor="password">Password</label>
134
+ <input id="password" type="password" placeholder="Enter password" value={this.state.password} onInput={(e) => this.setState({ password: (e.target as HTMLInputElement).value })} required />
135
+ <span className="error">{this.state.passErr}</span>
136
+ </div>
137
+ {!this.state.isLogin && (
138
+ <div className="form-group">
139
+ <label htmlFor="confirmPassword">Confirm Password</label>
140
+ <input id="confirmPassword" type="password" placeholder="Confirm password" value={this.state.confirm} onInput={(e) => this.setState({ confirm: (e.target as HTMLInputElement).value })} />
141
+ <span className="error">{this.state.confirmErr}</span>
142
+ </div>
143
+ )}
144
+ <button type="submit">{this.state.isLogin ? "Sign In" : "Create Account"}</button>
145
+ </form>
146
+ <p className="toggle-text">
147
+ <span>{this.state.isLogin ? "Don't have an account?" : "Already have an account?"}</span>
148
+ <a href="#" onClick={this.toggleMode}>{this.state.isLogin ? "Register" : "Login"}</a>
149
+ </p>
150
+ </div>
151
+ </div>
152
+ <Particles id="tsparticles" options={options} />
153
+ </ParticlesProvider>
154
+ );
155
+ }
156
+ };
157
+
158
+ export default App;
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "jquery": "^3.7.1",
13
+ "@tsparticles/jquery": "^4.1.3",
14
+ "@tsparticles/slim": "^4.1.3",
15
+ "@tsparticles/configs": "^4.1.3",
16
+ "@tsparticles/engine": "^4.1.3"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.7.2",
20
+ "vite": "^6.0.0"
21
+ }
22
+ }
@@ -0,0 +1,110 @@
1
+ import $ from "jquery";
2
+ import "@tsparticles/jquery";
3
+ import { tsParticles } from "@tsparticles/engine";
4
+ import { loadSlim } from "@tsparticles/slim";
5
+ import "./style.css";
6
+
7
+ let isLogin = true;
8
+
9
+ function validateEmail(val: string): boolean {
10
+ return /^[^\s@]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/.test(val);
11
+ }
12
+
13
+ function setTheme(theme: string): void {
14
+ document.documentElement.setAttribute("data-theme", theme);
15
+ $("#themeToggle").text(theme === "dark" ? "🌙" : "☀️");
16
+ localStorage.setItem("theme", theme);
17
+ }
18
+
19
+ const savedTheme = localStorage.getItem("theme") || "dark";
20
+ setTheme(savedTheme);
21
+
22
+ $("#themeToggle").on("click", () => {
23
+ const current = document.documentElement.getAttribute("data-theme") || "dark";
24
+ setTheme(current === "dark" ? "light" : "dark");
25
+ });
26
+
27
+ function validate(): boolean {
28
+ let valid = true;
29
+
30
+ const email = $("#email").val() as string;
31
+ const password = $("#password").val() as string;
32
+ const confirm = $("#confirmPassword").val() as string;
33
+
34
+ if (!email || !validateEmail(email)) {
35
+ $("#emailError").text("Please enter a valid email address");
36
+ valid = false;
37
+ } else {
38
+ $("#emailError").text("");
39
+ }
40
+
41
+ if (!password || password.length < 6) {
42
+ $("#passwordError").text("Password must be at least 6 characters");
43
+ valid = false;
44
+ } else {
45
+ $("#passwordError").text("");
46
+ }
47
+
48
+ if (!isLogin && password !== confirm) {
49
+ $("#confirmError").text("Passwords do not match");
50
+ valid = false;
51
+ } else {
52
+ $("#confirmError").text("");
53
+ }
54
+
55
+ return valid;
56
+ }
57
+
58
+ function toggleMode(): void {
59
+ isLogin = !isLogin;
60
+ $("#authTitle").text(isLogin ? "Login" : "Register");
61
+ $("#submitBtn").text(isLogin ? "Sign In" : "Create Account");
62
+ $("#toggleLabel").text(isLogin ? "Don't have an account?" : "Already have an account?");
63
+ $("#toggleAuth").text(isLogin ? "Register" : "Login");
64
+ $("#confirmGroup").toggle(!isLogin);
65
+ $("#emailError").text("");
66
+ $("#passwordError").text("");
67
+ $("#confirmError").text("");
68
+ }
69
+
70
+ $("#toggleAuth").on("click", (e) => {
71
+ e.preventDefault();
72
+ toggleMode();
73
+ });
74
+
75
+ $("#authForm").on("submit", (e) => {
76
+ e.preventDefault();
77
+
78
+ if (!validate()) return;
79
+
80
+ if (isLogin) {
81
+ alert("Logged in successfully! (demo)");
82
+ } else {
83
+ alert("Account created successfully! (demo)");
84
+ toggleMode();
85
+ }
86
+
87
+ $("#authForm")[0].reset();
88
+ });
89
+
90
+ (async () => {
91
+ await loadSlim(tsParticles);
92
+
93
+ $("#tsparticles").particles().load({
94
+ background: { color: { value: "transparent" } },
95
+ fpsLimit: 60,
96
+ particles: {
97
+ number: { value: 60, density: { enable: true } },
98
+ color: { value: ["#6c5ce7", "#a29bfe", "#fd79a8", "#00cec9"] },
99
+ shape: { type: "circle" },
100
+ opacity: { value: 0.5, random: true },
101
+ size: { value: { min: 1, max: 3 }, random: true },
102
+ move: { enable: true, speed: 1, direction: "none", random: true, straight: false, outModes: { default: "out" } },
103
+ },
104
+ interactivity: {
105
+ events: { onHover: { enable: true, mode: "bubble" } },
106
+ modes: { bubble: { distance: 200, size: 6, duration: 2, opacity: 0.8 } },
107
+ },
108
+ detectRetina: true,
109
+ });
110
+ })();