create-absolutejs 0.3.3 → 0.3.5

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 (37) hide show
  1. package/README.md +76 -34
  2. package/dist/data.js +1 -1
  3. package/dist/generators/configurations/generatePackageJson.js +2 -1
  4. package/dist/generators/html/generateHTMLPage.d.ts +2 -0
  5. package/dist/generators/html/generateHTMLPage.js +66 -0
  6. package/dist/generators/html/scaffoldHTML.d.ts +1 -1
  7. package/dist/generators/html/scaffoldHTML.js +10 -3
  8. package/dist/generators/htmx/generateHTMXPage.d.ts +2 -0
  9. package/dist/{templates/htmx/pages/HTMXExample.html → generators/htmx/generateHTMXPage.js} +8 -9
  10. package/dist/generators/htmx/scaffoldHTMX.d.ts +1 -1
  11. package/dist/generators/htmx/scaffoldHTMX.js +19 -6
  12. package/dist/generators/project/generateMarkupCSS.d.ts +2 -1
  13. package/dist/generators/project/generateMarkupCSS.js +19 -4
  14. package/dist/generators/project/generateServer.js +3 -3
  15. package/dist/generators/project/scaffoldFrontends.d.ts +2 -2
  16. package/dist/generators/project/scaffoldFrontends.js +6 -1
  17. package/dist/generators/react/generateReactPage.d.ts +2 -0
  18. package/dist/generators/react/generateReactPage.js +23 -0
  19. package/dist/generators/react/scaffoldReact.d.ts +1 -1
  20. package/dist/generators/react/scaffoldReact.js +5 -2
  21. package/dist/generators/svelte/generateSveltePage.d.ts +2 -0
  22. package/dist/{templates/svelte/pages/SvelteExample.svelte → generators/svelte/generateSveltePage.js} +8 -8
  23. package/dist/generators/svelte/scaffoldSvelte.d.ts +1 -1
  24. package/dist/generators/svelte/scaffoldSvelte.js +7 -1
  25. package/dist/generators/vue/generateVuePage.d.ts +2 -0
  26. package/dist/{templates/vue/pages/VueExample.vue → generators/vue/generateVuePage.js} +7 -7
  27. package/dist/generators/vue/scaffoldVue.d.ts +1 -1
  28. package/dist/generators/vue/scaffoldVue.js +8 -2
  29. package/dist/messages.js +22 -16
  30. package/dist/scaffold.js +1 -0
  31. package/dist/templates/htmx/htmx.2.0.6.min.js +1 -0
  32. package/dist/types.d.ts +1 -0
  33. package/dist/utils/formatNavLink.d.ts +1 -0
  34. package/dist/utils/formatNavLink.js +6 -0
  35. package/dist/utils/parseCommandLineOptions.js +61 -37
  36. package/package.json +1 -1
  37. package/dist/templates/react/components/Dropdown.tsx +0 -23
package/README.md CHANGED
@@ -32,7 +32,7 @@ By default, the CLI will interactively prompt you for any missing configuration
32
32
  ## Options
33
33
 
34
34
  ```text
35
- Usage: create-absolute [options] [project-name]
35
+ Usage: create-absolute [project-name] [options]
36
36
  ```
37
37
 
38
38
  ### Arguments
@@ -43,61 +43,103 @@ Usage: create-absolute [options] [project-name]
43
43
  ### Options
44
44
 
45
45
  - `--help`, `-h`
46
- Show the help message and exit.
46
+ Show this help message and exit.
47
+
47
48
  - `--debug`, `-d`
48
49
  Display a summary of the project configuration after creation.
49
- - `--angular <name>`
50
- Directory name for an Angular frontend.
51
- - `--assets <name>`
50
+
51
+ - `--angular`
52
+ Include an Angular frontend.
53
+
54
+ - `--angular-dir <dir>`
55
+ Specify the directory for and use the Angular frontend.
56
+
57
+ - `--assets <dir>`
52
58
  Directory name for your static assets.
53
- - `--auth <provider|none>`
54
- Preconfigured auth plugin or `none` to skip auth setup.
59
+
60
+ - `--auth <plugin|none>`
61
+ Pre-configured auth plugin (currently only `absolute-auth`) or `none`.
62
+
63
+ - `--biome`
64
+ Use Biome for code quality and formatting.
65
+
55
66
  - `--build <dir>`
56
67
  Output directory for build artifacts.
57
- - `--database <name>`
68
+
69
+ - `--database <dir>`
58
70
  Directory name for your database files.
71
+
59
72
  - `--directory <default|custom>`
60
73
  Directory-naming strategy: `default` or `custom`.
74
+
61
75
  - `--engine <engine|none>`
62
76
  Database engine (`postgresql` | `mysql` | `sqlite` | `mongodb` | `redis` | `singlestore` | `cockroachdb` | `mssql`) or `none`.
63
- - `--frontend <framework>`
64
- Frontend framework(s) to include: one or more of `react`, `svelte`, `html`, `htmx`, `vue`, `angular`.
77
+
78
+ - `--eslint+prettier`
79
+ Use ESLint + Prettier for code quality and formatting.
80
+
65
81
  - `--git`
66
82
  Initialize a Git repository.
83
+
67
84
  - `--host <provider|none>`
68
85
  Database host provider (`neon` | `planetscale` | `supabase` | `turso` | `vercel` | `upstash` | `atlas`) or `none`.
69
- - `--html <name>`
70
- Directory name for an HTML frontend.
71
- - `--htmx <name>`
72
- Directory name for an HTMX frontend.
73
- - `--lang <ts|js>`
74
- Language: `ts` or `js`.
86
+
87
+ - `--html`
88
+ Include a plain HTML frontend.
89
+
90
+ - `--html-dir <dir>`
91
+ Specify the directory for and use the HTML frontend.
92
+
93
+ - `--html-scripts`
94
+ Enable HTML scripting with TypeScript.
95
+
96
+ - `--htmx`
97
+ Include an HTMX frontend.
98
+
99
+ - `--htmx-dir <dir>`
100
+ Specify the directory for and use the HTMX frontend.
101
+
102
+ - `--install`
103
+ Use the same package manager to install dependencies.
104
+
75
105
  - `--lts`
76
- Use the latest published versions of required packages.
77
- - `--npm`
78
- Use the package manager that invoked this command to install dependencies.
106
+ Use LTS versions of required packages.
107
+
79
108
  - `--orm <drizzle|prisma|none>`
80
- ORM to configure: `drizzle`, `prisma`, or `none`.
109
+ ORM to configure: `drizzle` | `prisma` | `none`.
110
+
81
111
  - `--plugin <plugin>`
82
- Elysia plugin(s) to include (can be specified multiple times), or `none` to skip plugins.
83
- - `--quality <eslint+prettier|biome>`
84
- Code quality tool: `eslint+prettier` or `biome`.
85
- - `--react <name>`
86
- Directory name for a React frontend.
87
- - `--script <ts|js|none>`
88
- HTML scripting option: `ts`, `js`, or `none`.
112
+ Elysia plugin(s) to include (repeatable); `none` skips plugin setup.
113
+
114
+ - `--react`
115
+ Include a React frontend.
116
+
117
+ - `--react-dir <dir>`
118
+ Specify the directory for and use the React frontend.
119
+
89
120
  - `--skip`
90
- Skip **all** non-required prompts and use `none` for every optional configuration.
91
- - `--svelte <name>`
92
- Directory name for a Svelte frontend.
121
+ Skip non-required prompts; uses `none` for all optional configs.
122
+
123
+ - `--svelte`
124
+ Include a Svelte frontend.
125
+
126
+ - `--svelte-dir <dir>`
127
+ Specify the directory for and use the Svelte frontend.
128
+
93
129
  - `--tailwind`
94
130
  Include Tailwind CSS setup.
95
- - `--tailwind-input <path>`
131
+
132
+ - `--tailwind-input <file>`
96
133
  Path to your Tailwind CSS entry file.
97
- - `--tailwind-output <path>`
134
+
135
+ - `--tailwind-output <file>`
98
136
  Path for the generated Tailwind CSS bundle.
99
- - `--vue <name>`
100
- Directory name for a Vue frontend.
137
+
138
+ - `--vue`
139
+ Include a Vue frontend.
140
+
141
+ - `--vue-dir <dir>`
142
+ Specify the directory for and use the Vue frontend.
101
143
 
102
144
  ## Directory Configuration
103
145
 
package/dist/data.js CHANGED
@@ -108,7 +108,7 @@ export const defaultPlugins = [
108
108
  { isPlugin: false, packageName: 'build' },
109
109
  { isPlugin: true, packageName: 'networking' }
110
110
  ],
111
- latestVersion: '0.11.1',
111
+ latestVersion: '0.12.2',
112
112
  value: '@absolutejs/absolute'
113
113
  },
114
114
  {
@@ -14,6 +14,7 @@ export const createPackageJson = ({ projectName, authProvider, plugins, useTailw
14
14
  const requiresSvelte = frontendDirectories['svelte'] !== undefined;
15
15
  const requiresVue = frontendDirectories['vue'] !== undefined;
16
16
  const requiresHtmx = frontendDirectories['htmx'] !== undefined;
17
+ const requiresHtml = frontendDirectories['html'] !== undefined;
17
18
  for (const p of defaultPlugins) {
18
19
  dependencies[p.value] = resolveVersion(p.value, p.latestVersion);
19
20
  }
@@ -57,7 +58,7 @@ export const createPackageJson = ({ projectName, authProvider, plugins, useTailw
57
58
  void (latest && s.stop(green('Package versions resolved')));
58
59
  const scripts = {
59
60
  dev: 'bun run --watch src/backend/server.ts',
60
- format: `prettier --write "./**/*.{js,ts,css,json,mjs,md${requiresReact ? ',jsx,tsx' : ''}${requiresSvelte ? ',svelte' : ''}${requiresVue ? ',vue' : ''}}"`,
61
+ format: `prettier --write "./**/*.{js,ts,css,json,mjs,md${requiresReact ? ',jsx,tsx' : ''}${requiresSvelte ? ',svelte' : ''}${requiresVue ? ',vue' : ''}${requiresHtml || requiresHtmx ? ',html' : ''}}"`,
61
62
  lint: 'eslint ./src',
62
63
  test: 'echo "Error: no test specified" && exit 1',
63
64
  typecheck: 'bun run tsc --noEmit'
@@ -0,0 +1,2 @@
1
+ import { Frontend } from '../../types';
2
+ export declare const generateHTMLPage: (frontends: Frontend[]) => string;
@@ -0,0 +1,66 @@
1
+ import { formatNavLink } from '../../utils/formatNavLink';
2
+ export const generateHTMLPage = (frontends) => {
3
+ const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ return `<!doctype html>
5
+ <html>
6
+ <head>
7
+ <title>AbsoluteJS + HTML</title>
8
+ <meta name="description" content="AbsoluteJS HTML Example" />
9
+ <meta charset="utf-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
11
+ <link
12
+ rel="stylesheet"
13
+ type="text/css"
14
+ href="../styles/html-example.css"
15
+ />
16
+ <link rel="icon" href="/assets/ico/favicon.ico" />
17
+ </head>
18
+ <body>
19
+ <header>
20
+ <a href="/">AbsoluteJS</a>
21
+ <details>
22
+ <summary>Pages</summary>
23
+ <nav>
24
+ ${navLinks}
25
+ </nav>
26
+ </details>
27
+ </header>
28
+ <main>
29
+ <nav>
30
+ <a href="https://absolutejs.com" target="_blank">
31
+ <img
32
+ class="logo"
33
+ src="/assets/png/absolutejs-temp.png"
34
+ alt="AbsoluteJS Logo"
35
+ />
36
+ </a>
37
+ <a href="https://html.spec.whatwg.org/multipage/">
38
+ <img
39
+ class="logo html"
40
+ src="/assets/svg/HTML5_Badge.svg"
41
+ alt="HTML Logo"
42
+ />
43
+ </a>
44
+ </nav>
45
+ <h1>AbsoluteJS + HTML</h1>
46
+ <button id="counter-button">
47
+ count is <span id="counter">0</span>
48
+ </button>
49
+ <p>
50
+ Edit <code>example/html/pages/HtmlExample.html</code> save and
51
+ rebuild to update the page.
52
+ </p>
53
+ <p style="color: #777">( Hot Module Reloading is coming soon )</p>
54
+ <p style="margin-top: 2rem">
55
+ Explore the other pages to see how AbsoluteJS seamlessly unifies
56
+ multiple frameworks on a single server.
57
+ </p>
58
+ <p style="margin-top: 2rem; font-size: 1rem; color: #777">
59
+ Click on the AbsoluteJS and HTML logos to learn more.
60
+ </p>
61
+ </main>
62
+ <script src="../scripts/typescript-example.ts"></script>
63
+ </body>
64
+ </html>
65
+ `;
66
+ };
@@ -2,5 +2,5 @@ import { ScaffoldFrontendProps } from '../../types';
2
2
  type ScaffoldHTMLProps = ScaffoldFrontendProps & {
3
3
  useHTMLScripts: boolean;
4
4
  };
5
- export declare const scaffoldHTML: ({ isSingleFrontend, targetDirectory, useHTMLScripts, templatesDirectory, projectAssetsDirectory }: ScaffoldHTMLProps) => void;
5
+ export declare const scaffoldHTML: ({ isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }: ScaffoldHTMLProps) => void;
6
6
  export {};
@@ -1,14 +1,21 @@
1
1
  import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
- export const scaffoldHTML = ({ isSingleFrontend, targetDirectory, useHTMLScripts, templatesDirectory, projectAssetsDirectory }) => {
4
+ import { generateHTMLPage } from './generateHTMLPage';
5
+ export const scaffoldHTML = ({ isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }) => {
5
6
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'HTML5_Badge.svg'), join(projectAssetsDirectory, 'svg', 'HTML5_Badge.svg'));
6
- cpSync(join(templatesDirectory, 'html'), targetDirectory, {
7
+ const htmlPage = generateHTMLPage(frontends);
8
+ const pagesDirectory = join(targetDirectory, 'pages');
9
+ mkdirSync(pagesDirectory, { recursive: true });
10
+ const htmlFilePath = join(pagesDirectory, 'HTMLExample.html');
11
+ writeFileSync(htmlFilePath, htmlPage, 'utf-8');
12
+ const scriptsDirectory = join(targetDirectory, 'scripts');
13
+ cpSync(join(templatesDirectory, 'html', 'scripts'), scriptsDirectory, {
7
14
  recursive: true
8
15
  });
9
16
  const cssOutputDir = join(targetDirectory, 'styles');
10
17
  mkdirSync(cssOutputDir, { recursive: true });
11
18
  const cssOutputFile = join(cssOutputDir, 'html-example.css');
12
- const htmlCSS = generateMarkupCSS(isSingleFrontend);
19
+ const htmlCSS = generateMarkupCSS('html', '#e34f26', isSingleFrontend);
13
20
  writeFileSync(cssOutputFile, htmlCSS, 'utf-8');
14
21
  };
@@ -0,0 +1,2 @@
1
+ import { Frontend } from '../../types';
2
+ export declare const generateHTMXPage: (isSingle: boolean, frontends: Frontend[]) => string;
@@ -1,4 +1,7 @@
1
- <!doctype html>
1
+ import { formatNavLink } from '../../utils/formatNavLink';
2
+ export const generateHTMXPage = (isSingle, frontends) => {
3
+ const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ return `<!doctype html>
2
5
  <html>
3
6
  <head>
4
7
  <title>AbsoluteJS + HTMX</title>
@@ -11,8 +14,7 @@
11
14
  href="../styles/htmx-example.css"
12
15
  />
13
16
  <link rel="icon" href="/assets/ico/favicon.ico" />
14
- <!-- TODO : Download htmx instead of from CDN -->
15
- <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js"></script>
17
+ <script src="${isSingle ? '' : '/htmx'}/htmx.min.js"></script>
16
18
  </head>
17
19
  <body
18
20
  hx-post="/htmx/reset"
@@ -27,12 +29,7 @@
27
29
  >
28
30
  <summary>Pages</summary>
29
31
  <nav>
30
- <a href="/html">HTML</a>
31
- <a href="/react">React</a>
32
- <a href="/htmx">HTMX</a>
33
- <a href="/svelte">Svelte</a>
34
- <a href="/vue">Vue</a>
35
- <a href="/angular">Angular</a>
32
+ ${navLinks}
36
33
  </nav>
37
34
  </details>
38
35
  </header>
@@ -90,3 +87,5 @@
90
87
  </main>
91
88
  </body>
92
89
  </html>
90
+ `;
91
+ };
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldHTMX: ({ targetDirectory, templatesDirectory, projectAssetsDirectory, isSingleFrontend }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldHTMX: ({ targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }: ScaffoldFrontendProps) => void;
@@ -1,15 +1,28 @@
1
- import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
1
+ import { copyFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
+ import { Glob } from 'bun';
3
4
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
- export const scaffoldHTMX = ({ targetDirectory, templatesDirectory, projectAssetsDirectory, isSingleFrontend }) => {
5
+ import { generateHTMXPage } from './generateHTMXPage';
6
+ export const scaffoldHTMX = ({ targetDirectory, templatesDirectory, projectAssetsDirectory, frontends, isSingleFrontend }) => {
5
7
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-black.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-black.svg'));
6
8
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'htmx-logo-white.svg'), join(projectAssetsDirectory, 'svg', 'htmx-logo-white.svg'));
7
- cpSync(join(templatesDirectory, 'htmx'), targetDirectory, {
8
- recursive: true
9
- });
9
+ const glob = new Glob('htmx*.min.js');
10
+ for (const relativePath of glob.scanSync({
11
+ cwd: join(templatesDirectory, 'htmx')
12
+ })) {
13
+ const src = join(templatesDirectory, 'htmx', relativePath);
14
+ const dest = join(targetDirectory, 'htmx.min.js');
15
+ copyFileSync(src, dest);
16
+ break;
17
+ }
18
+ const htmxPage = generateHTMXPage(isSingleFrontend, frontends);
19
+ const pagesDirectory = join(targetDirectory, 'pages');
20
+ mkdirSync(pagesDirectory, { recursive: true });
21
+ const htmxFilePath = join(pagesDirectory, 'HTMXExample.html');
22
+ writeFileSync(htmxFilePath, htmxPage, 'utf-8');
10
23
  const cssOutputDir = join(targetDirectory, 'styles');
11
24
  mkdirSync(cssOutputDir, { recursive: true });
12
25
  const cssOutputFile = join(cssOutputDir, 'htmx-example.css');
13
- const htmxCSS = generateMarkupCSS(isSingleFrontend);
26
+ const htmxCSS = generateMarkupCSS('htmx', '#3465a4', isSingleFrontend);
14
27
  writeFileSync(cssOutputFile, htmxCSS, 'utf-8');
15
28
  };
@@ -1 +1,2 @@
1
- export declare const generateMarkupCSS: (isSingleFrontend: boolean) => string;
1
+ import { Frontend } from '../../types';
2
+ export declare const generateMarkupCSS: (frontend: Frontend, color: string, isSingleFrontend: boolean) => string;
@@ -1,4 +1,4 @@
1
- export const generateMarkupCSS = (isSingleFrontend) => `@import url('${isSingleFrontend ? '../styles/reset.css' : '../../styles/reset.css'}');
1
+ export const generateMarkupCSS = (frontend, color, isSingleFrontend) => `@import url('${isSingleFrontend ? '../styles/reset.css' : '../../styles/reset.css'}');
2
2
 
3
3
  header {
4
4
  align-items: center;
@@ -49,8 +49,8 @@ h1 {
49
49
  filter: drop-shadow(0 0 2rem #5fbeeb);
50
50
  }
51
51
 
52
- .logo.html:hover {
53
- filter: drop-shadow(0 0 2rem #e34f26);
52
+ .logo.${frontend}:hover {
53
+ filter: drop-shadow(0 0 2rem ${color});
54
54
  }
55
55
 
56
56
  nav {
@@ -130,4 +130,19 @@ header details nav a {
130
130
  background-color: #ffffff;
131
131
  }
132
132
  }
133
- `;
133
+ ${frontend === 'react'
134
+ ? `\n@keyframes logo-spin {
135
+ from {
136
+ transform: rotate(0deg);
137
+ }
138
+ to {
139
+ transform: rotate(360deg);
140
+ }
141
+ }
142
+
143
+ @media (prefers-reduced-motion: no-preference) {
144
+ a:nth-of-type(2) .logo {
145
+ animation: logo-spin infinite 20s linear;
146
+ }
147
+ }`
148
+ : ''}`;
@@ -102,7 +102,7 @@ export const createServerFile = ({ tailwind, frontendDirectories, serverFilePath
102
102
  const manifestOptions = [
103
103
  `assetsDirectory: '${assetsDirectory}'`,
104
104
  `buildDirectory: '${buildDirectory}'`,
105
- ...Object.entries(frontendDirectories).map(([frameworkName, directory]) => `${frameworkName}Directory: './src/frontend/${directory}'`),
105
+ ...Object.entries(frontendDirectories).map(([frameworkName, directory]) => `${frameworkName}Directory: 'src/frontend/${directory}'`),
106
106
  tailwind ? `tailwind: ${JSON.stringify(tailwind)}` : ''
107
107
  ].filter(Boolean);
108
108
  const manifestDeclaration = `${nonFrameworkOnly ? '' : 'const manifest = '}await build({
@@ -128,7 +128,7 @@ export const createServerFile = ({ tailwind, frontendDirectories, serverFilePath
128
128
  if (index === 0) {
129
129
  acc.indexRoute = `.get('/', () => ${handler})`;
130
130
  }
131
- acc.otherRoutes.push(`.post('/htmx/reset', ({ resetScopedStore }) => resetScopedStore())`, `.get('/htmx/count', ({ scopedStore }) => scopedStore.count)`, `.post('/htmx/increment', ({ scopedStore }) => ++scopedStore.count)`, `.get('htmx', () => ${handler})`);
131
+ acc.otherRoutes.push(`.get('/htmx', () => ${handler})`, `.post('/htmx/reset', ({ resetScopedStore }) => resetScopedStore())`, `.get('/htmx/count', ({ scopedStore }) => scopedStore.count)`, `.post('/htmx/increment', ({ scopedStore }) => ++scopedStore.count)`);
132
132
  return acc;
133
133
  default:
134
134
  return acc;
@@ -136,7 +136,7 @@ export const createServerFile = ({ tailwind, frontendDirectories, serverFilePath
136
136
  if (index === 0) {
137
137
  acc.indexRoute = `.get('/', () => ${handler})`;
138
138
  }
139
- acc.otherRoutes.push(`.get('${frameworkName}', () => ${handler})`);
139
+ acc.otherRoutes.push(`.get('/${frameworkName}', () => ${handler})`);
140
140
  return acc;
141
141
  }, { indexRoute: null, otherRoutes: [] });
142
142
  const routes = [routesData.indexRoute ?? '', ...routesData.otherRoutes]
@@ -1,8 +1,8 @@
1
1
  import type { CreateConfiguration } from '../../types';
2
- type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories'> & {
2
+ type ScaffoldFrontendsProps = Pick<CreateConfiguration, 'useHTMLScripts' | 'frontendDirectories' | 'frontends'> & {
3
3
  frontendDirectory: string;
4
4
  templatesDirectory: string;
5
5
  projectAssetsDirectory: string;
6
6
  };
7
- export declare const scaffoldFrontends: ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories }: ScaffoldFrontendsProps) => void;
7
+ export declare const scaffoldFrontends: ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }: ScaffoldFrontendsProps) => void;
8
8
  export {};
@@ -5,7 +5,7 @@ import { scaffoldHTMX } from '../htmx/scaffoldHTMX';
5
5
  import { scaffoldReact } from '../react/scaffoldReact';
6
6
  import { scaffoldSvelte } from '../svelte/scaffoldSvelte';
7
7
  import { scaffoldVue } from '../vue/scaffoldVue';
8
- export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories }) => {
8
+ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, projectAssetsDirectory, useHTMLScripts, frontendDirectories, frontends }) => {
9
9
  const stylesTargetDirectory = join(frontendDirectory, 'styles');
10
10
  cpSync(join(templatesDirectory, 'styles'), stylesTargetDirectory, {
11
11
  recursive: true
@@ -25,6 +25,7 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
25
25
  switch (frontendName) {
26
26
  case 'react':
27
27
  scaffoldReact({
28
+ frontends,
28
29
  isSingleFrontend,
29
30
  projectAssetsDirectory,
30
31
  targetDirectory,
@@ -33,6 +34,7 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
33
34
  break;
34
35
  case 'svelte':
35
36
  scaffoldSvelte({
37
+ frontends,
36
38
  isSingleFrontend,
37
39
  projectAssetsDirectory,
38
40
  targetDirectory,
@@ -41,6 +43,7 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
41
43
  break;
42
44
  case 'vue':
43
45
  scaffoldVue({
46
+ frontends,
44
47
  projectAssetsDirectory,
45
48
  targetDirectory,
46
49
  templatesDirectory
@@ -51,6 +54,7 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
51
54
  break;
52
55
  case 'html':
53
56
  scaffoldHTML({
57
+ frontends,
54
58
  isSingleFrontend,
55
59
  projectAssetsDirectory,
56
60
  targetDirectory,
@@ -60,6 +64,7 @@ export const scaffoldFrontends = ({ frontendDirectory, templatesDirectory, proje
60
64
  break;
61
65
  case 'htmx':
62
66
  scaffoldHTMX({
67
+ frontends,
63
68
  isSingleFrontend,
64
69
  projectAssetsDirectory,
65
70
  targetDirectory,
@@ -0,0 +1,2 @@
1
+ import { Frontend } from '../../types';
2
+ export declare const generateDropdownComponent: (frontends: Frontend[]) => string;
@@ -0,0 +1,23 @@
1
+ import { formatNavLink } from '../../utils/formatNavLink';
2
+ export const generateDropdownComponent = (frontends) => {
3
+ const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ return `import { useState } from 'react';
5
+
6
+ export const Dropdown = () => {
7
+ const [isOpen, setIsOpen] = useState(false);
8
+
9
+ return (
10
+ <details
11
+ onPointerEnter={() => setIsOpen(true)}
12
+ onPointerLeave={() => setIsOpen(false)}
13
+ open={isOpen}
14
+ >
15
+ <summary>Pages</summary>
16
+ <nav>
17
+ ${navLinks}
18
+ </nav>
19
+ </details>
20
+ );
21
+ };
22
+ `;
23
+ };
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldReact: ({ isSingleFrontend, targetDirectory, templatesDirectory, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldReact: ({ isSingleFrontend, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
@@ -1,14 +1,17 @@
1
1
  import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { generateMarkupCSS } from '../project/generateMarkupCSS';
4
- export const scaffoldReact = ({ isSingleFrontend, targetDirectory, templatesDirectory, projectAssetsDirectory }) => {
4
+ import { generateDropdownComponent } from './generateReactPage';
5
+ export const scaffoldReact = ({ isSingleFrontend, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }) => {
5
6
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'react.svg'), join(projectAssetsDirectory, 'svg', 'react.svg'));
6
7
  cpSync(join(templatesDirectory, 'react'), targetDirectory, {
7
8
  recursive: true
8
9
  });
10
+ const dropdownComponent = generateDropdownComponent(frontends);
11
+ writeFileSync(join(targetDirectory, 'components', 'Dropdown.tsx'), dropdownComponent, 'utf-8');
9
12
  const cssOutputDir = join(targetDirectory, 'styles');
10
13
  mkdirSync(cssOutputDir, { recursive: true });
11
14
  const cssOutputFile = join(cssOutputDir, 'react-example.css');
12
- const reactCSS = generateMarkupCSS(isSingleFrontend);
15
+ const reactCSS = generateMarkupCSS('react', '#61dafbaa', isSingleFrontend);
13
16
  writeFileSync(cssOutputFile, reactCSS, 'utf-8');
14
17
  };
@@ -0,0 +1,2 @@
1
+ import { Frontend } from '../../types';
2
+ export declare const generateSveltePage: (frontends: Frontend[]) => string;
@@ -1,4 +1,7 @@
1
- <script lang="ts">
1
+ import { formatNavLink } from '../../utils/formatNavLink';
2
+ export const generateSveltePage = (frontends) => {
3
+ const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ return `<script lang="ts">
2
5
  type SvelteExampleProps = {
3
6
  initialCount: number;
4
7
  cssPath: string;
@@ -22,7 +25,7 @@
22
25
  crossOrigin="anonymous"
23
26
  />
24
27
  <link
25
- href={`https://fonts.googleapis.com/css2?family=Poppins:wght@100..900&display=swap`}
28
+ href={\`https://fonts.googleapis.com/css2?family=Poppins:wght@100..900&display=swap\`}
26
29
  rel="stylesheet"
27
30
  />
28
31
  <link rel="stylesheet" href={cssPath} type="text/css" />
@@ -37,12 +40,7 @@
37
40
  >
38
41
  <summary>Pages</summary>
39
42
  <nav>
40
- <a href="/html">HTML</a>
41
- <a href="/react">React</a>
42
- <a href="/htmx">HTMX</a>
43
- <a href="/svelte">Svelte</a>
44
- <a href="/vue">Vue</a>
45
- <a href="/angular">Angular</a>
43
+ ${navLinks}
46
44
  </nav>
47
45
  </details>
48
46
  </header>
@@ -213,3 +211,5 @@
213
211
  }
214
212
  }
215
213
  </style>
214
+ `;
215
+ };
@@ -1,2 +1,2 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
- export declare const scaffoldSvelte: ({ isSingleFrontend, targetDirectory, templatesDirectory, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
2
+ export declare const scaffoldSvelte: ({ isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory }: ScaffoldFrontendProps) => void;
@@ -1,10 +1,16 @@
1
1
  import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
- export const scaffoldSvelte = ({ isSingleFrontend, targetDirectory, templatesDirectory, projectAssetsDirectory }) => {
3
+ import { generateSveltePage } from './generateSveltePage';
4
+ export const scaffoldSvelte = ({ isSingleFrontend, targetDirectory, frontends, templatesDirectory, projectAssetsDirectory }) => {
4
5
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'svelte-logo.svg'), join(projectAssetsDirectory, 'svg', 'svelte-logo.svg'));
5
6
  cpSync(join(templatesDirectory, 'svelte'), targetDirectory, {
6
7
  recursive: true
7
8
  });
9
+ const sveltePage = generateSveltePage(frontends);
10
+ const pagesDirectory = join(targetDirectory, 'pages');
11
+ mkdirSync(pagesDirectory, { recursive: true });
12
+ const svelteFilePath = join(pagesDirectory, 'SvelteExample.svelte');
13
+ writeFileSync(svelteFilePath, sveltePage, 'utf-8');
8
14
  const cssOutputDirectory = join(targetDirectory, 'styles');
9
15
  mkdirSync(cssOutputDirectory, { recursive: true });
10
16
  const cssOutputFile = join(cssOutputDirectory, 'svelte-example.css');
@@ -0,0 +1,2 @@
1
+ import { Frontend } from '../../types';
2
+ export declare const generateVuePage: (frontends: Frontend[]) => string;
@@ -1,4 +1,7 @@
1
- <script setup lang="ts">
1
+ import { formatNavLink } from '../../utils/formatNavLink';
2
+ export const generateVuePage = (frontends) => {
3
+ const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
4
+ return `<script setup lang="ts">
2
5
  import { ref } from 'vue';
3
6
  import CountButton from '../components/CountButton.vue';
4
7
 
@@ -20,12 +23,7 @@ const isOpen = ref(false);
20
23
  >
21
24
  <summary>Pages</summary>
22
25
  <nav>
23
- <a href="/html">HTML</a>
24
- <a href="/react">React</a>
25
- <a href="/htmx">HTMX</a>
26
- <a href="/svelte">Svelte</a>
27
- <a href="/vue">Vue</a>
28
- <a href="/angular">Angular</a>
26
+ ${navLinks}
29
27
  </nav>
30
28
  </details>
31
29
  </header>
@@ -264,3 +262,5 @@ header details nav a {
264
262
  }
265
263
  }
266
264
  </style>
265
+ `;
266
+ };
@@ -1,4 +1,4 @@
1
1
  import { ScaffoldFrontendProps } from '../../types';
2
2
  type ScaffoldVueProps = Omit<ScaffoldFrontendProps, 'isSingleFrontend'>;
3
- export declare const scaffoldVue: ({ targetDirectory, templatesDirectory, projectAssetsDirectory }: ScaffoldVueProps) => void;
3
+ export declare const scaffoldVue: ({ targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }: ScaffoldVueProps) => void;
4
4
  export {};
@@ -1,8 +1,14 @@
1
- import { cpSync, copyFileSync } from 'fs';
1
+ import { cpSync, copyFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
- export const scaffoldVue = ({ targetDirectory, templatesDirectory, projectAssetsDirectory }) => {
3
+ import { generateVuePage } from './generateVuePage';
4
+ export const scaffoldVue = ({ targetDirectory, templatesDirectory, frontends, projectAssetsDirectory }) => {
4
5
  copyFileSync(join(templatesDirectory, 'assets', 'svg', 'vue-logo.svg'), join(projectAssetsDirectory, 'svg', 'vue-logo.svg'));
5
6
  cpSync(join(templatesDirectory, 'vue'), targetDirectory, {
6
7
  recursive: true
7
8
  });
9
+ const vuePage = generateVuePage(frontends);
10
+ const pagesDirectory = join(targetDirectory, 'pages');
11
+ mkdirSync(pagesDirectory, { recursive: true });
12
+ const vueFilePath = join(pagesDirectory, 'VueExample.vue');
13
+ writeFileSync(vueFilePath, vuePage, 'utf-8');
8
14
  };
package/dist/messages.js CHANGED
@@ -11,31 +11,37 @@ Options:
11
11
  ${cyan('--help, -h')} Show this help message and exit
12
12
  ${cyan('--debug, -d')} Display a summary of the project configuration after creation
13
13
 
14
- ${cyan('--angular')} ${dim(cyan('<dir>'))} Directory name for an Angular frontend
14
+ ${cyan('--angular')} Include an Angular frontend
15
+ ${cyan('--angular-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Angular frontend
15
16
  ${cyan('--assets')} ${dim(cyan('<dir>'))} Directory name for your static assets
16
- ${cyan('--auth')} ${dim(cyan('<plugin>'))} Preconfigured auth plugin (currently only "absolute-auth") or 'none' to skip auth setup
17
+ ${cyan('--auth')} ${dim(cyan('<plugin>'))} Pre-configured auth plugin (currently only "absolute-auth") or 'none'
18
+ ${cyan('--biome')} Use Biome for code quality and formatting
17
19
  ${cyan('--build')} ${dim(cyan('<dir>'))} Output directory for build artifacts
18
20
  ${cyan('--database')} ${dim(cyan('<dir>'))} Directory name for your database files
19
21
  ${cyan('--directory')} ${dim(cyan('<mode>'))} Directory-naming strategy: "default" or "custom"
20
- ${cyan('--engine')} ${dim(cyan('<engine>'))} Database engine (postgresql | mysql | sqlite | mongodb | redis | singlestore | cockroachdb | mssql) or 'none' to skip database setup
21
- ${cyan('--frontend')} ${dim(cyan('<name>'))} Frontend framework(s) to include: one or more of "react", "svelte", "html", "htmx", "vue", "angular"
22
+ ${cyan('--engine')} ${dim(cyan('<engine>'))} Database engine (postgresql | mysql | sqlite | mongodb | redis | singlestore | cockroachdb | mssql) or 'none'
23
+ ${cyan('--eslint+prettier')} Use ESLint + Prettier for code quality and formatting
22
24
  ${cyan('--git')} Initialize a Git repository
23
- ${cyan('--host')} ${dim(cyan('<host>'))} Database host provider (neon | planetscale | supabase | turso | vercel | upstash | atlas) or 'none' to skip database host setup
24
- ${cyan('--html')} ${dim(cyan('<dir>'))} Directory name for an HTML frontend
25
- ${cyan('--htmx')} ${dim(cyan('<dir>'))} Directory name for an HTMX frontend
25
+ ${cyan('--host')} ${dim(cyan('<host>'))} Database host provider (neon | planetscale | supabase | turso | vercel | upstash | atlas) or 'none'
26
+ ${cyan('--html')} Include a plain HTML frontend
27
+ ${cyan('--html-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the HTML frontend
28
+ ${cyan('--html-scripts')} Enable HTML scripting with TypeScript
29
+ ${cyan('--htmx')} Include an HTMX frontend
30
+ ${cyan('--htmx-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the HTMX frontend
31
+ ${cyan('--install')} Use the same package manager to install dependencies
26
32
  ${cyan('--lts')} Use LTS versions of required packages
27
- ${cyan('--npm')} Use the package manager that invoked this command to install dependencies
28
- ${cyan('--orm')} ${dim(cyan('<orm>'))} ORM to configure: "drizzle" or "prisma" or 'none' to skip ORM setup
29
- ${cyan('--plugin')} ${dim(cyan('<plugin>'))} Elysia plugin(s) to include (can be specified multiple times), passing 'none' will skip plugin setup and ignore any other plugin options
30
- ${cyan('--quality')} ${dim(cyan('<tool>'))} Code quality tool: "eslint+prettier" or "biome"
31
- ${cyan('--react')} ${dim(cyan('<dir>'))} Directory name for a React frontend
32
- ${cyan('--html-script')} ${dim(cyan('<option>'))} Enable HTML scripting with TypeScript
33
- ${cyan('--skip')} Skips non required prompts and uses 'none' for all optional configurations
34
- ${cyan('--svelte')} ${dim(cyan('<dir>'))} Directory name for a Svelte frontend
33
+ ${cyan('--orm')} ${dim(cyan('<orm>'))} ORM to configure: "drizzle" | "prisma" | 'none'
34
+ ${cyan('--plugin')} ${dim(cyan('<plugin>'))} Elysia plugin(s) to include (repeatable); 'none' skips plugin setup
35
+ ${cyan('--react')} Include a React frontend
36
+ ${cyan('--react-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the React frontend
37
+ ${cyan('--skip')} Skip non-required prompts; uses 'none' for all optional configs
38
+ ${cyan('--svelte')} Include a Svelte frontend
39
+ ${cyan('--svelte-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Svelte frontend
35
40
  ${cyan('--tailwind')} Include Tailwind CSS setup
36
41
  ${cyan('--tailwind-input')} ${dim(cyan('<file>'))} Path to your Tailwind CSS entry file
37
42
  ${cyan('--tailwind-output')} ${dim(cyan('<file>'))} Path for the generated Tailwind CSS bundle
38
- ${cyan('--vue')} ${dim(cyan('<dir>'))} Directory name for a Vue frontend
43
+ ${cyan('--vue')} Include a Vue frontend
44
+ ${cyan('--vue-dir')} ${dim(cyan('<dir>'))} Specify the directory for and use the Vue frontend
39
45
  `;
40
46
  export const getOutroMessage = ({ projectName, packageManager, installDependenciesNow }) => `${green('Created successfully')}, you can now run:\n\n` +
41
47
  `${cyan('cd')} ${projectName}\n` +
package/dist/scaffold.js CHANGED
@@ -55,6 +55,7 @@ useHTMLScripts, useTailwind, databaseDirectory, orm, frontends, plugins, authPro
55
55
  scaffoldFrontends({
56
56
  frontendDirectories,
57
57
  frontendDirectory,
58
+ frontends,
58
59
  projectAssetsDirectory,
59
60
  templatesDirectory,
60
61
  useHTMLScripts
@@ -0,0 +1 @@
1
+ var htmx=function(){"use strict";const Q={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){const n=dn(e,t||"post");return n.values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,allowScriptTags:true,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:true,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null,disableInheritance:false,responseHandling:[{code:"204",swap:false},{code:"[23]..",swap:true},{code:"[45]..",swap:false,error:true}],allowNestedOobSwaps:true,historyRestoreAsHxRequest:true},parseInterval:null,location:location,_:null,version:"2.0.6"};Q.onLoad=j;Q.process=Ft;Q.on=xe;Q.off=be;Q.trigger=ae;Q.ajax=Ln;Q.find=f;Q.findAll=x;Q.closest=g;Q.remove=z;Q.addClass=K;Q.removeClass=G;Q.toggleClass=W;Q.takeClass=Z;Q.swap=$e;Q.defineExtension=zn;Q.removeExtension=$n;Q.logAll=V;Q.logNone=_;Q.parseInterval=d;Q._=e;const n={addTriggerHandler:St,bodyContains:se,canAccessLocalStorage:B,findThisElement:Se,filterValues:yn,swap:$e,hasAttribute:s,getAttributeValue:a,getClosestAttributeValue:ne,getClosestMatch:q,getExpressionVars:Tn,getHeaders:mn,getInputValues:dn,getInternalData:oe,getSwapSpecification:bn,getTriggerSpecs:st,getTarget:Ee,makeFragment:P,mergeObjects:le,makeSettleInfo:Sn,oobSwap:He,querySelectorExt:ue,settleImmediately:Yt,shouldCancel:ht,triggerEvent:ae,triggerErrorEvent:fe,withExtensions:jt};const de=["get","post","put","delete","patch"];const T=de.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e instanceof Element&&e.getAttribute(t)}function s(e,t){return!!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function a(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){const t=e.parentElement;if(!t&&e.parentNode instanceof ShadowRoot)return e.parentNode;return t}function te(){return document}function y(e,t){return e.getRootNode?e.getRootNode({composed:t}):te()}function q(e,t){while(e&&!t(e)){e=u(e)}return e||null}function o(e,t,n){const r=a(t,n);const o=a(t,"hx-disinherit");var i=a(t,"hx-inherit");if(e!==t){if(Q.config.disableInheritance){if(i&&(i==="*"||i.split(" ").indexOf(n)>=0)){return r}else{return null}}if(o&&(o==="*"||o.split(" ").indexOf(n)>=0)){return"unset"}}return r}function ne(t,n){let r=null;q(t,function(e){return!!(r=o(t,ce(e),n))});if(r!=="unset"){return r}}function h(e,t){return e instanceof Element&&e.matches(t)}function A(e){const t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;const n=t.exec(e);if(n){return n[1].toLowerCase()}else{return""}}function L(e){const t=new DOMParser;return t.parseFromString(e,"text/html")}function N(e,t){while(t.childNodes.length>0){e.append(t.childNodes[0])}}function r(e){const t=te().createElement("script");ie(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}return t}function i(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function I(e){Array.from(e.querySelectorAll("script")).forEach(e=>{if(i(e)){const t=r(e);const n=e.parentNode;try{n.insertBefore(t,e)}catch(e){R(e)}finally{e.remove()}}})}function P(e){const t=e.replace(/<head(\s[^>]*)?>[\s\S]*?<\/head>/i,"");const n=A(t);let r;if(n==="html"){r=new DocumentFragment;const i=L(e);N(r,i.body);r.title=i.title}else if(n==="body"){r=new DocumentFragment;const i=L(t);N(r,i.body);r.title=i.title}else{const i=L('<body><template class="internal-htmx-wrapper">'+t+"</template></body>");r=i.querySelector("template").content;r.title=i.title;var o=r.querySelector("title");if(o&&o.parentNode===r){o.remove();r.title=o.innerText}}if(r){if(Q.config.allowScriptTags){I(r)}else{r.querySelectorAll("script").forEach(e=>e.remove())}}return r}function re(e){if(e){e()}}function t(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function D(e){return typeof e==="function"}function k(e){return t(e,"Object")}function oe(e){const t="htmx-internal-data";let n=e[t];if(!n){n=e[t]={}}return n}function M(t){const n=[];if(t){for(let e=0;e<t.length;e++){n.push(t[e])}}return n}function ie(t,n){if(t){for(let e=0;e<t.length;e++){n(t[e])}}}function F(e){const t=e.getBoundingClientRect();const n=t.top;const r=t.bottom;return n<window.innerHeight&&r>=0}function se(e){return e.getRootNode({composed:true})===document}function X(e){return e.trim().split(/\s+/)}function le(e,t){for(const n in t){if(t.hasOwnProperty(n)){e[n]=t[n]}}return e}function v(e){try{return JSON.parse(e)}catch(e){R(e);return null}}function B(){const e="htmx:sessionStorageTest";try{sessionStorage.setItem(e,e);sessionStorage.removeItem(e);return true}catch(e){return false}}function U(e){const t=new URL(e,"http://x");if(t){e=t.pathname+t.search}if(e!="/"){e=e.replace(/\/+$/,"")}return e}function e(e){return On(te().body,function(){return eval(e)})}function j(t){const e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,n){if(console){console.log(t,e,n)}}}function _(){Q.logger=null}function f(e,t){if(typeof e!=="string"){return e.querySelector(t)}else{return f(te(),e)}}function x(e,t){if(typeof e!=="string"){return e.querySelectorAll(t)}else{return x(te(),e)}}function b(){return window}function z(e,t){e=w(e);if(t){b().setTimeout(function(){z(e);e=null},t)}else{u(e).removeChild(e)}}function ce(e){return e instanceof Element?e:null}function $(e){return e instanceof HTMLElement?e:null}function J(e){return typeof e==="string"?e:null}function p(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function K(e,t,n){e=ce(w(e));if(!e){return}if(n){b().setTimeout(function(){K(e,t);e=null},n)}else{e.classList&&e.classList.add(t)}}function G(e,t,n){let r=ce(w(e));if(!r){return}if(n){b().setTimeout(function(){G(r,t);r=null},n)}else{if(r.classList){r.classList.remove(t);if(r.classList.length===0){r.removeAttribute("class")}}}}function W(e,t){e=w(e);e.classList.toggle(t)}function Z(e,t){e=w(e);ie(e.parentElement.children,function(e){G(e,t)});K(ce(e),t)}function g(e,t){e=ce(w(e));if(e){return e.closest(t)}return null}function l(e,t){return e.substring(0,t.length)===t}function Y(e,t){return e.substring(e.length-t.length)===t}function pe(e){const t=e.trim();if(l(t,"<")&&Y(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function m(t,r,n){if(r.indexOf("global ")===0){return m(t,r.slice(7),true)}t=w(t);const o=[];{let t=0;let n=0;for(let e=0;e<r.length;e++){const l=r[e];if(l===","&&t===0){o.push(r.substring(n,e));n=e+1;continue}if(l==="<"){t++}else if(l==="/"&&e<r.length-1&&r[e+1]===">"){t--}}if(n<r.length){o.push(r.substring(n))}}const i=[];const s=[];while(o.length>0){const r=pe(o.shift());let e;if(r.indexOf("closest ")===0){e=g(ce(t),pe(r.slice(8)))}else if(r.indexOf("find ")===0){e=f(p(t),pe(r.slice(5)))}else if(r==="next"||r==="nextElementSibling"){e=ce(t).nextElementSibling}else if(r.indexOf("next ")===0){e=ge(t,pe(r.slice(5)),!!n)}else if(r==="previous"||r==="previousElementSibling"){e=ce(t).previousElementSibling}else if(r.indexOf("previous ")===0){e=me(t,pe(r.slice(9)),!!n)}else if(r==="document"){e=document}else if(r==="window"){e=window}else if(r==="body"){e=document.body}else if(r==="root"){e=y(t,!!n)}else if(r==="host"){e=t.getRootNode().host}else{s.push(r)}if(e){i.push(e)}}if(s.length>0){const e=s.join(",");const c=p(y(t,!!n));i.push(...M(c.querySelectorAll(e)))}return i}var ge=function(t,e,n){const r=p(y(t,n)).querySelectorAll(e);for(let e=0;e<r.length;e++){const o=r[e];if(o.compareDocumentPosition(t)===Node.DOCUMENT_POSITION_PRECEDING){return o}}};var me=function(t,e,n){const r=p(y(t,n)).querySelectorAll(e);for(let e=r.length-1;e>=0;e--){const o=r[e];if(o.compareDocumentPosition(t)===Node.DOCUMENT_POSITION_FOLLOWING){return o}}};function ue(e,t){if(typeof e!=="string"){return m(e,t)[0]}else{return m(te().body,e)[0]}}function w(e,t){if(typeof e==="string"){return f(p(t)||document,e)}else{return e}}function ye(e,t,n,r){if(D(t)){return{target:te().body,event:J(e),listener:t,options:n}}else{return{target:w(e),event:J(t),listener:n,options:r}}}function xe(t,n,r,o){Gn(function(){const e=ye(t,n,r,o);e.target.addEventListener(e.event,e.listener,e.options)});const e=D(n);return e?n:r}function be(t,n,r){Gn(function(){const e=ye(t,n,r);e.target.removeEventListener(e.event,e.listener)});return D(n)?n:r}const ve=te().createElement("output");function we(t,n){const e=ne(t,n);if(e){if(e==="this"){return[Se(t,n)]}else{const r=m(t,e);const o=/(^|,)(\s*)inherit(\s*)($|,)/.test(e);if(o){const i=ce(q(t,function(e){return e!==t&&s(ce(e),n)}));if(i){r.push(...we(i,n))}}if(r.length===0){R('The selector "'+e+'" on '+n+" returned no matches!");return[ve]}else{return r}}}}function Se(e,t){return ce(q(e,function(e){return a(ce(e),t)!=null}))}function Ee(e){const t=ne(e,"hx-target");if(t){if(t==="this"){return Se(e,"hx-target")}else{return ue(e,t)}}else{const n=oe(e);if(n.boosted){return te().body}else{return e}}}function Ce(e){return Q.config.attributesToSettle.includes(e)}function Oe(t,n){ie(t.attributes,function(e){if(!n.hasAttribute(e.name)&&Ce(e.name)){t.removeAttribute(e.name)}});ie(n.attributes,function(e){if(Ce(e.name)){t.setAttribute(e.name,e.value)}})}function Re(t,e){const n=Jn(e);for(let e=0;e<n.length;e++){const r=n[e];try{if(r.isInlineSwap(t)){return true}}catch(e){R(e)}}return t==="outerHTML"}function He(e,o,i,t){t=t||te();let n="#"+CSS.escape(ee(o,"id"));let s="outerHTML";if(e==="true"){}else if(e.indexOf(":")>0){s=e.substring(0,e.indexOf(":"));n=e.substring(e.indexOf(":")+1)}else{s=e}o.removeAttribute("hx-swap-oob");o.removeAttribute("data-hx-swap-oob");const r=m(t,n,false);if(r.length){ie(r,function(e){let t;const n=o.cloneNode(true);t=te().createDocumentFragment();t.appendChild(n);if(!Re(s,e)){t=p(n)}const r={shouldSwap:true,target:e,fragment:t};if(!ae(e,"htmx:oobBeforeSwap",r))return;e=r.target;if(r.shouldSwap){qe(t);_e(s,e,e,t,i);Te()}ie(i.elts,function(e){ae(e,"htmx:oobAfterSwap",r)})});o.parentNode.removeChild(o)}else{o.parentNode.removeChild(o);fe(te().body,"htmx:oobErrorNoTarget",{content:o})}return e}function Te(){const e=f("#--htmx-preserve-pantry--");if(e){for(const t of[...e.children]){const n=f("#"+t.id);n.parentNode.moveBefore(t,n);n.remove()}e.remove()}}function qe(e){ie(x(e,"[hx-preserve], [data-hx-preserve]"),function(e){const t=a(e,"id");const n=te().getElementById(t);if(n!=null){if(e.moveBefore){let e=f("#--htmx-preserve-pantry--");if(e==null){te().body.insertAdjacentHTML("afterend","<div id='--htmx-preserve-pantry--'></div>");e=f("#--htmx-preserve-pantry--")}e.moveBefore(n,null)}else{e.parentNode.replaceChild(n,e)}}})}function Ae(l,e,c){ie(e.querySelectorAll("[id]"),function(t){const n=ee(t,"id");if(n&&n.length>0){const r=n.replace("'","\\'");const o=t.tagName.replace(":","\\:");const e=p(l);const i=e&&e.querySelector(o+"[id='"+r+"']");if(i&&i!==e){const s=t.cloneNode();Oe(t,i);c.tasks.push(function(){Oe(t,s)})}}})}function Le(e){return function(){G(e,Q.config.addedClass);Ft(ce(e));Ne(p(e));ae(e,"htmx:load")}}function Ne(e){const t="[autofocus]";const n=$(h(e,t)?e:e.querySelector(t));if(n!=null){n.focus()}}function c(e,t,n,r){Ae(e,n,r);while(n.childNodes.length>0){const o=n.firstChild;K(ce(o),Q.config.addedClass);e.insertBefore(o,t);if(o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE){r.tasks.push(Le(o))}}}function Ie(e,t){let n=0;while(n<e.length){t=(t<<5)-t+e.charCodeAt(n++)|0}return t}function Pe(t){let n=0;for(let e=0;e<t.attributes.length;e++){const r=t.attributes[e];if(r.value){n=Ie(r.name,n);n=Ie(r.value,n)}}return n}function De(t){const n=oe(t);if(n.onHandlers){for(let e=0;e<n.onHandlers.length;e++){const r=n.onHandlers[e];be(t,r.event,r.listener)}delete n.onHandlers}}function ke(e){const t=oe(e);if(t.timeout){clearTimeout(t.timeout)}if(t.listenerInfos){ie(t.listenerInfos,function(e){if(e.on){be(e.on,e.trigger,e.listener)}})}De(e);ie(Object.keys(t),function(e){if(e!=="firstInitCompleted")delete t[e]})}function S(e){ae(e,"htmx:beforeCleanupElement");ke(e);ie(e.children,function(e){S(e)})}function Me(t,e,n){if(t.tagName==="BODY"){return Ve(t,e,n)}let r;const o=t.previousSibling;const i=u(t);if(!i){return}c(i,t,e,n);if(o==null){r=i.firstChild}else{r=o.nextSibling}n.elts=n.elts.filter(function(e){return e!==t});while(r&&r!==t){if(r instanceof Element){n.elts.push(r)}r=r.nextSibling}S(t);t.remove()}function Fe(e,t,n){return c(e,e.firstChild,t,n)}function Xe(e,t,n){return c(u(e),e,t,n)}function Be(e,t,n){return c(e,null,t,n)}function Ue(e,t,n){return c(u(e),e.nextSibling,t,n)}function je(e){S(e);const t=u(e);if(t){return t.removeChild(e)}}function Ve(e,t,n){const r=e.firstChild;c(e,r,t,n);if(r){while(r.nextSibling){S(r.nextSibling);e.removeChild(r.nextSibling)}S(r);e.removeChild(r)}}function _e(t,e,n,r,o){switch(t){case"none":return;case"outerHTML":Me(n,r,o);return;case"afterbegin":Fe(n,r,o);return;case"beforebegin":Xe(n,r,o);return;case"beforeend":Be(n,r,o);return;case"afterend":Ue(n,r,o);return;case"delete":je(n);return;default:var i=Jn(e);for(let e=0;e<i.length;e++){const s=i[e];try{const l=s.handleSwap(t,n,r,o);if(l){if(Array.isArray(l)){for(let e=0;e<l.length;e++){const c=l[e];if(c.nodeType!==Node.TEXT_NODE&&c.nodeType!==Node.COMMENT_NODE){o.tasks.push(Le(c))}}}return}}catch(e){R(e)}}if(t==="innerHTML"){Ve(n,r,o)}else{_e(Q.config.defaultSwapStyle,e,n,r,o)}}}function ze(e,n,r){var t=x(e,"[hx-swap-oob], [data-hx-swap-oob]");ie(t,function(e){if(Q.config.allowNestedOobSwaps||e.parentElement===null){const t=a(e,"hx-swap-oob");if(t!=null){He(t,e,n,r)}}else{e.removeAttribute("hx-swap-oob");e.removeAttribute("data-hx-swap-oob")}});return t.length>0}function $e(h,d,p,g){if(!g){g={}}let m=null;let n=null;let e=function(){re(g.beforeSwapCallback);h=w(h);const r=g.contextElement?y(g.contextElement,false):te();const e=document.activeElement;let t={};t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null};const o=Sn(h);if(p.swapStyle==="textContent"){h.textContent=d}else{let n=P(d);o.title=g.title||n.title;if(g.historyRequest){n=n.querySelector("[hx-history-elt],[data-hx-history-elt]")||n}if(g.selectOOB){const i=g.selectOOB.split(",");for(let t=0;t<i.length;t++){const s=i[t].split(":",2);let e=s[0].trim();if(e.indexOf("#")===0){e=e.substring(1)}const l=s[1]||"true";const c=n.querySelector("#"+e);if(c){He(l,c,o,r)}}}ze(n,o,r);ie(x(n,"template"),function(e){if(e.content&&ze(e.content,o,r)){e.remove()}});if(g.select){const u=te().createDocumentFragment();ie(n.querySelectorAll(g.select),function(e){u.appendChild(e)});n=u}qe(n);_e(p.swapStyle,g.contextElement,h,n,o);Te()}if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){const f=document.getElementById(ee(t.elt,"id"));const a={preventScroll:p.focusScroll!==undefined?!p.focusScroll:!Q.config.defaultFocusScroll};if(f){if(t.start&&f.setSelectionRange){try{f.setSelectionRange(t.start,t.end)}catch(e){}}f.focus(a)}}h.classList.remove(Q.config.swappingClass);ie(o.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ae(e,"htmx:afterSwap",g.eventInfo)});re(g.afterSwapCallback);if(!p.ignoreTitle){Bn(o.title)}const n=function(){ie(o.tasks,function(e){e.call()});ie(o.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ae(e,"htmx:afterSettle",g.eventInfo)});if(g.anchor){const e=ce(w("#"+g.anchor));if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}En(o.elts,p);re(g.afterSettleCallback);re(m)};if(p.settleDelay>0){b().setTimeout(n,p.settleDelay)}else{n()}};let t=Q.config.globalViewTransitions;if(p.hasOwnProperty("transition")){t=p.transition}const r=g.contextElement||te();if(t&&ae(r,"htmx:beforeTransition",g.eventInfo)&&typeof Promise!=="undefined"&&document.startViewTransition){const o=new Promise(function(e,t){m=e;n=t});const i=e;e=function(){document.startViewTransition(function(){i();return o})}}try{if(p?.swapDelay&&p.swapDelay>0){b().setTimeout(e,p.swapDelay)}else{e()}}catch(e){fe(r,"htmx:swapError",g.eventInfo);re(n);throw e}}function Je(e,t,n){const r=e.getResponseHeader(t);if(r.indexOf("{")===0){const o=v(r);for(const i in o){if(o.hasOwnProperty(i)){let e=o[i];if(k(e)){n=e.target!==undefined?e.target:n}else{e={value:e}}ae(n,i,e)}}}else{const s=r.split(",");for(let e=0;e<s.length;e++){ae(n,s[e].trim(),[])}}}const Ke=/\s/;const E=/[\s,]/;const Ge=/[_$a-zA-Z]/;const We=/[_$a-zA-Z0-9]/;const Ze=['"',"'","/"];const C=/[^\s]/;const Ye=/[{(]/;const Qe=/[})]/;function et(e){const t=[];let n=0;while(n<e.length){if(Ge.exec(e.charAt(n))){var r=n;while(We.exec(e.charAt(n+1))){n++}t.push(e.substring(r,n+1))}else if(Ze.indexOf(e.charAt(n))!==-1){const o=e.charAt(n);var r=n;n++;while(n<e.length&&e.charAt(n)!==o){if(e.charAt(n)==="\\"){n++}n++}t.push(e.substring(r,n+1))}else{const i=e.charAt(n);t.push(i)}n++}return t}function tt(e,t,n){return Ge.exec(e.charAt(0))&&e!=="true"&&e!=="false"&&e!=="this"&&e!==n&&t!=="."}function nt(r,o,i){if(o[0]==="["){o.shift();let e=1;let t=" return (function("+i+"){ return (";let n=null;while(o.length>0){const s=o[0];if(s==="]"){e--;if(e===0){if(n===null){t=t+"true"}o.shift();t+=")})";try{const l=On(r,function(){return Function(t)()},function(){return true});l.source=t;return l}catch(e){fe(te().body,"htmx:syntax:error",{error:e,source:t});return null}}}else if(s==="["){e++}if(tt(s,n,i)){t+="(("+i+"."+s+") ? ("+i+"."+s+") : (window."+s+"))"}else{t=t+s}n=o.shift()}}}function O(e,t){let n="";while(e.length>0&&!t.test(e[0])){n+=e.shift()}return n}function rt(e){let t;if(e.length>0&&Ye.test(e[0])){e.shift();t=O(e,Qe).trim();e.shift()}else{t=O(e,E)}return t}const ot="input, textarea, select";function it(e,t,n){const r=[];const o=et(t);do{O(o,C);const l=o.length;const c=O(o,/[,\[\s]/);if(c!==""){if(c==="every"){const u={trigger:"every"};O(o,C);u.pollInterval=d(O(o,/[,\[\s]/));O(o,C);var i=nt(e,o,"event");if(i){u.eventFilter=i}r.push(u)}else{const f={trigger:c};var i=nt(e,o,"event");if(i){f.eventFilter=i}O(o,C);while(o.length>0&&o[0]!==","){const a=o.shift();if(a==="changed"){f.changed=true}else if(a==="once"){f.once=true}else if(a==="consume"){f.consume=true}else if(a==="delay"&&o[0]===":"){o.shift();f.delay=d(O(o,E))}else if(a==="from"&&o[0]===":"){o.shift();if(Ye.test(o[0])){var s=rt(o)}else{var s=O(o,E);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();const h=rt(o);if(h.length>0){s+=" "+h}}}f.from=s}else if(a==="target"&&o[0]===":"){o.shift();f.target=rt(o)}else if(a==="throttle"&&o[0]===":"){o.shift();f.throttle=d(O(o,E))}else if(a==="queue"&&o[0]===":"){o.shift();f.queue=O(o,E)}else if(a==="root"&&o[0]===":"){o.shift();f[a]=rt(o)}else if(a==="threshold"&&o[0]===":"){o.shift();f[a]=O(o,E)}else{fe(e,"htmx:syntax:error",{token:o.shift()})}O(o,C)}r.push(f)}}if(o.length===l){fe(e,"htmx:syntax:error",{token:o.shift()})}O(o,C)}while(o[0]===","&&o.shift());if(n){n[t]=r}return r}function st(e){const t=a(e,"hx-trigger");let n=[];if(t){const r=Q.config.triggerSpecsCache;n=r&&r[t]||it(e,t,r)}if(n.length>0){return n}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,ot)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function lt(e){oe(e).cancelled=true}function ct(e,t,n){const r=oe(e);r.timeout=b().setTimeout(function(){if(se(e)&&r.cancelled!==true){if(!pt(n,e,Bt("hx:poll:trigger",{triggerSpec:n,target:e}))){t(e)}ct(e,t,n)}},n.pollInterval)}function ut(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function ft(e){return g(e,Q.config.disableSelector)}function at(t,n,e){if(t instanceof HTMLAnchorElement&&ut(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"&&String(ee(t,"method")).toLowerCase()!=="dialog"){n.boosted=true;let r,o;if(t.tagName==="A"){r="get";o=ee(t,"href")}else{const i=ee(t,"method");r=i?i.toLowerCase():"get";o=ee(t,"action");if(o==null||o===""){o=location.href}if(r==="get"&&o.includes("?")){o=o.replace(/\?[^#]+/,"")}}e.forEach(function(e){gt(t,function(e,t){const n=ce(e);if(ft(n)){S(n);return}he(r,o,n,t)},n,e,true)})}}function ht(e,t){if(e.type==="submit"||e.type==="click"){t=ce(e.target)||t;if(t.tagName==="FORM"){return true}if(t.form&&t.type==="submit"){return true}t=t.closest("a");if(t&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function dt(e,t){return oe(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function pt(e,t,n){const r=e.eventFilter;if(r){try{return r.call(t,n)!==true}catch(e){const o=r.source;fe(te().body,"htmx:eventFilter:error",{error:e,source:o});return true}}return false}function gt(l,c,e,u,f){const a=oe(l);let t;if(u.from){t=m(l,u.from)}else{t=[l]}if(u.changed){if(!("lastValue"in a)){a.lastValue=new WeakMap}t.forEach(function(e){if(!a.lastValue.has(u)){a.lastValue.set(u,new WeakMap)}a.lastValue.get(u).set(e,e.value)})}ie(t,function(i){const s=function(e){if(!se(l)){i.removeEventListener(u.trigger,s);return}if(dt(l,e)){return}if(f||ht(e,l)){e.preventDefault()}if(pt(u,l,e)){return}const t=oe(e);t.triggerSpec=u;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(l)<0){t.handledFor.push(l);if(u.consume){e.stopPropagation()}if(u.target&&e.target){if(!h(ce(e.target),u.target)){return}}if(u.once){if(a.triggeredOnce){return}else{a.triggeredOnce=true}}if(u.changed){const n=e.target;const r=n.value;const o=a.lastValue.get(u);if(o.has(n)&&o.get(n)===r){return}o.set(n,r)}if(a.delayed){clearTimeout(a.delayed)}if(a.throttle){return}if(u.throttle>0){if(!a.throttle){ae(l,"htmx:trigger");c(l,e);a.throttle=b().setTimeout(function(){a.throttle=null},u.throttle)}}else if(u.delay>0){a.delayed=b().setTimeout(function(){ae(l,"htmx:trigger");c(l,e)},u.delay)}else{ae(l,"htmx:trigger");c(l,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:u.trigger,listener:s,on:i});i.addEventListener(u.trigger,s)})}let mt=false;let yt=null;function xt(){if(!yt){yt=function(){mt=true};window.addEventListener("scroll",yt);window.addEventListener("resize",yt);setInterval(function(){if(mt){mt=false;ie(te().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){bt(e)})}},200)}}function bt(e){if(!s(e,"data-hx-revealed")&&F(e)){e.setAttribute("data-hx-revealed","true");const t=oe(e);if(t.initHash){ae(e,"revealed")}else{e.addEventListener("htmx:afterProcessNode",function(){ae(e,"revealed")},{once:true})}}}function vt(e,t,n,r){const o=function(){if(!n.loaded){n.loaded=true;ae(e,"htmx:trigger");t(e)}};if(r>0){b().setTimeout(o,r)}else{o()}}function wt(t,n,e){let i=false;ie(de,function(r){if(s(t,"hx-"+r)){const o=a(t,"hx-"+r);i=true;n.path=o;n.verb=r;e.forEach(function(e){St(t,e,n,function(e,t){const n=ce(e);if(ft(n)){S(n);return}he(r,o,n,t)})})}});return i}function St(r,e,t,n){if(e.trigger==="revealed"){xt();gt(r,n,t,e);bt(ce(r))}else if(e.trigger==="intersect"){const o={};if(e.root){o.root=ue(r,e.root)}if(e.threshold){o.threshold=parseFloat(e.threshold)}const i=new IntersectionObserver(function(t){for(let e=0;e<t.length;e++){const n=t[e];if(n.isIntersecting){ae(r,"intersect");break}}},o);i.observe(ce(r));gt(ce(r),n,t,e)}else if(!t.firstInitCompleted&&e.trigger==="load"){if(!pt(e,r,Bt("load",{elt:r}))){vt(ce(r),n,t,e.delay)}}else if(e.pollInterval>0){t.polling=true;ct(ce(r),n,e)}else{gt(r,n,t,e)}}function Et(e){const t=ce(e);if(!t){return false}const n=t.attributes;for(let e=0;e<n.length;e++){const r=n[e].name;if(l(r,"hx-on:")||l(r,"data-hx-on:")||l(r,"hx-on-")||l(r,"data-hx-on-")){return true}}return false}const Ct=(new XPathEvaluator).createExpression('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or'+' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]');function Ot(e,t){if(Et(e)){t.push(ce(e))}const n=Ct.evaluate(e);let r=null;while(r=n.iterateNext())t.push(ce(r))}function Rt(e){const t=[];if(e instanceof DocumentFragment){for(const n of e.childNodes){Ot(n,t)}}else{Ot(e,t)}return t}function Ht(e){if(e.querySelectorAll){const n=", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]";const r=[];for(const i in Vn){const s=Vn[i];if(s.getSelectors){var t=s.getSelectors();if(t){r.push(t)}}}const o=e.querySelectorAll(T+n+", form, [type='submit'],"+" [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger]"+r.flat().map(e=>", "+e).join(""));return o}else{return[]}}function Tt(e){const t=At(e.target);const n=Nt(e);if(n){n.lastButtonClicked=t}}function qt(e){const t=Nt(e);if(t){t.lastButtonClicked=null}}function At(e){return g(ce(e),"button, input[type='submit']")}function Lt(e){return e.form||g(e,"form")}function Nt(e){const t=At(e.target);if(!t){return}const n=Lt(t);return oe(n)}function It(e){e.addEventListener("click",Tt);e.addEventListener("focusin",Tt);e.addEventListener("focusout",qt)}function Pt(t,e,n){const r=oe(t);if(!Array.isArray(r.onHandlers)){r.onHandlers=[]}let o;const i=function(e){On(t,function(){if(ft(t)){return}if(!o){o=new Function("event",n)}o.call(t,e)})};t.addEventListener(e,i);r.onHandlers.push({event:e,listener:i})}function Dt(t){De(t);for(let e=0;e<t.attributes.length;e++){const n=t.attributes[e].name;const r=t.attributes[e].value;if(l(n,"hx-on")||l(n,"data-hx-on")){const o=n.indexOf("-on")+3;const i=n.slice(o,o+1);if(i==="-"||i===":"){let e=n.slice(o+1);if(l(e,":")){e="htmx"+e}else if(l(e,"-")){e="htmx:"+e.slice(1)}else if(l(e,"htmx-")){e="htmx:"+e.slice(5)}Pt(t,e,r)}}}}function kt(t){ae(t,"htmx:beforeProcessNode");const n=oe(t);const e=st(t);const r=wt(t,n,e);if(!r){if(ne(t,"hx-boost")==="true"){at(t,n,e)}else if(s(t,"hx-trigger")){e.forEach(function(e){St(t,e,n,function(){})})}}if(t.tagName==="FORM"||ee(t,"type")==="submit"&&s(t,"form")){It(t)}n.firstInitCompleted=true;ae(t,"htmx:afterProcessNode")}function Mt(e){if(!(e instanceof Element)){return false}const t=oe(e);const n=Pe(e);if(t.initHash!==n){ke(e);t.initHash=n;return true}return false}function Ft(e){e=w(e);if(ft(e)){S(e);return}const t=[];if(Mt(e)){t.push(e)}ie(Ht(e),function(e){if(ft(e)){S(e);return}if(Mt(e)){t.push(e)}});ie(Rt(e),Dt);ie(t,kt)}function Xt(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function Bt(e,t){return new CustomEvent(e,{bubbles:true,cancelable:true,composed:true,detail:t})}function fe(e,t,n){ae(e,t,le({error:t},n))}function Ut(e){return e==="htmx:afterProcessNode"}function jt(e,t,n){ie(Jn(e,[],n),function(e){try{t(e)}catch(e){R(e)}})}function R(e){console.error(e)}function ae(e,t,n){e=w(e);if(n==null){n={}}n.elt=e;const r=Bt(t,n);if(Q.logger&&!Ut(t)){Q.logger(e,t,n)}if(n.error){R(n.error);ae(e,"htmx:error",{errorInfo:n})}let o=e.dispatchEvent(r);const i=Xt(t);if(o&&i!==t){const s=Bt(i,r.detail);o=o&&e.dispatchEvent(s)}jt(ce(e),function(e){o=o&&(e.onEvent(t,r)!==false&&!r.defaultPrevented)});return o}let Vt=location.pathname+location.search;function _t(e){Vt=e;if(B()){sessionStorage.setItem("htmx-current-path-for-history",e)}}function zt(){const e=te().querySelector("[hx-history-elt],[data-hx-history-elt]");return e||te().body}function $t(t,e){if(!B()){return}const n=Kt(e);const r=te().title;const o=window.scrollY;if(Q.config.historyCacheSize<=0){sessionStorage.removeItem("htmx-history-cache");return}t=U(t);const i=v(sessionStorage.getItem("htmx-history-cache"))||[];for(let e=0;e<i.length;e++){if(i[e].url===t){i.splice(e,1);break}}const s={url:t,content:n,title:r,scroll:o};ae(te().body,"htmx:historyItemCreated",{item:s,cache:i});i.push(s);while(i.length>Q.config.historyCacheSize){i.shift()}while(i.length>0){try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(te().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Jt(t){if(!B()){return null}t=U(t);const n=v(sessionStorage.getItem("htmx-history-cache"))||[];for(let e=0;e<n.length;e++){if(n[e].url===t){return n[e]}}return null}function Kt(e){const t=Q.config.requestClass;const n=e.cloneNode(true);ie(x(n,"."+t),function(e){G(e,t)});ie(x(n,"[data-disabled-by-htmx]"),function(e){e.removeAttribute("disabled")});return n.innerHTML}function Gt(){const e=zt();let t=Vt;if(B()){t=sessionStorage.getItem("htmx-current-path-for-history")}t=t||location.pathname+location.search;const n=te().querySelector('[hx-history="false" i],[data-hx-history="false" i]');if(!n){ae(te().body,"htmx:beforeHistorySave",{path:t,historyElt:e});$t(t,e)}if(Q.config.historyEnabled)history.replaceState({htmx:true},te().title,location.href)}function Wt(e){if(Q.config.getCacheBusterParam){e=e.replace(/org\.htmx\.cache-buster=[^&]*&?/,"");if(Y(e,"&")||Y(e,"?")){e=e.slice(0,-1)}}if(Q.config.historyEnabled){history.pushState({htmx:true},"",e)}_t(e)}function Zt(e){if(Q.config.historyEnabled)history.replaceState({htmx:true},"",e);_t(e)}function Yt(e){ie(e,function(e){e.call(undefined)})}function Qt(e){const t=new XMLHttpRequest;const n={swapStyle:"innerHTML",swapDelay:0,settleDelay:0};const r={path:e,xhr:t,historyElt:zt(),swapSpec:n};t.open("GET",e,true);if(Q.config.historyRestoreAsHxRequest){t.setRequestHeader("HX-Request","true")}t.setRequestHeader("HX-History-Restore-Request","true");t.setRequestHeader("HX-Current-URL",location.href);t.onload=function(){if(this.status>=200&&this.status<400){r.response=this.response;ae(te().body,"htmx:historyCacheMissLoad",r);$e(r.historyElt,r.response,n,{contextElement:r.historyElt,historyRequest:true});_t(r.path);ae(te().body,"htmx:historyRestore",{path:e,cacheMiss:true,serverResponse:r.response})}else{fe(te().body,"htmx:historyCacheMissLoadError",r)}};if(ae(te().body,"htmx:historyCacheMiss",r)){t.send()}}function en(e){Gt();e=e||location.pathname+location.search;const t=Jt(e);if(t){const n={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:t.scroll};const r={path:e,item:t,historyElt:zt(),swapSpec:n};if(ae(te().body,"htmx:historyCacheHit",r)){$e(r.historyElt,t.content,n,{contextElement:r.historyElt,title:t.title});_t(r.path);ae(te().body,"htmx:historyRestore",r)}}else{if(Q.config.refreshOnHistoryMiss){Q.location.reload(true)}else{Qt(e)}}}function tn(e){let t=we(e,"hx-indicator");if(t==null){t=[e]}ie(t,function(e){const t=oe(e);t.requestCount=(t.requestCount||0)+1;e.classList.add.call(e.classList,Q.config.requestClass)});return t}function nn(e){let t=we(e,"hx-disabled-elt");if(t==null){t=[]}ie(t,function(e){const t=oe(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","");e.setAttribute("data-disabled-by-htmx","")});return t}function rn(e,t){ie(e.concat(t),function(e){const t=oe(e);t.requestCount=(t.requestCount||1)-1});ie(e,function(e){const t=oe(e);if(t.requestCount===0){e.classList.remove.call(e.classList,Q.config.requestClass)}});ie(t,function(e){const t=oe(e);if(t.requestCount===0){e.removeAttribute("disabled");e.removeAttribute("data-disabled-by-htmx")}})}function on(t,n){for(let e=0;e<t.length;e++){const r=t[e];if(r.isSameNode(n)){return true}}return false}function sn(e){const t=e;if(t.name===""||t.name==null||t.disabled||g(t,"fieldset[disabled]")){return false}if(t.type==="button"||t.type==="submit"||t.tagName==="image"||t.tagName==="reset"||t.tagName==="file"){return false}if(t.type==="checkbox"||t.type==="radio"){return t.checked}return true}function ln(t,e,n){if(t!=null&&e!=null){if(Array.isArray(e)){e.forEach(function(e){n.append(t,e)})}else{n.append(t,e)}}}function cn(t,n,r){if(t!=null&&n!=null){let e=r.getAll(t);if(Array.isArray(n)){e=e.filter(e=>n.indexOf(e)<0)}else{e=e.filter(e=>e!==n)}r.delete(t);ie(e,e=>r.append(t,e))}}function un(e){if(e instanceof HTMLSelectElement&&e.multiple){return M(e.querySelectorAll("option:checked")).map(function(e){return e.value})}if(e instanceof HTMLInputElement&&e.files){return M(e.files)}return e.value}function fn(t,n,r,e,o){if(e==null||on(t,e)){return}else{t.push(e)}if(sn(e)){const i=ee(e,"name");ln(i,un(e),n);if(o){an(e,r)}}if(e instanceof HTMLFormElement){ie(e.elements,function(e){if(t.indexOf(e)>=0){cn(e.name,un(e),n)}else{t.push(e)}if(o){an(e,r)}});new FormData(e).forEach(function(e,t){if(e instanceof File&&e.name===""){return}ln(t,e,n)})}}function an(e,t){const n=e;if(n.willValidate){ae(n,"htmx:validation:validate");if(!n.checkValidity()){t.push({elt:n,message:n.validationMessage,validity:n.validity});ae(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})}}}function hn(n,e){for(const t of e.keys()){n.delete(t)}e.forEach(function(e,t){n.append(t,e)});return n}function dn(e,t){const n=[];const r=new FormData;const o=new FormData;const i=[];const s=oe(e);if(s.lastButtonClicked&&!se(s.lastButtonClicked)){s.lastButtonClicked=null}let l=e instanceof HTMLFormElement&&e.noValidate!==true||a(e,"hx-validate")==="true";if(s.lastButtonClicked){l=l&&s.lastButtonClicked.formNoValidate!==true}if(t!=="get"){fn(n,o,i,Lt(e),l)}fn(n,r,i,e,l);if(s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&ee(e,"type")==="submit"){const u=s.lastButtonClicked||e;const f=ee(u,"name");ln(f,u.value,o)}const c=we(e,"hx-include");ie(c,function(e){fn(n,r,i,ce(e),l);if(!h(e,"form")){ie(p(e).querySelectorAll(ot),function(e){fn(n,r,i,e,l)})}});hn(r,o);return{errors:i,formData:r,values:kn(r)}}function pn(e,t,n){if(e!==""){e+="&"}if(String(n)==="[object Object]"){n=JSON.stringify(n)}const r=encodeURIComponent(n);e+=encodeURIComponent(t)+"="+r;return e}function gn(e){e=Pn(e);let n="";e.forEach(function(e,t){n=pn(n,t,e)});return n}function mn(e,t,n){const r={"HX-Request":"true","HX-Trigger":ee(e,"id"),"HX-Trigger-Name":ee(e,"name"),"HX-Target":a(t,"id"),"HX-Current-URL":location.href};Cn(e,"hx-headers",false,r);if(n!==undefined){r["HX-Prompt"]=n}if(oe(e).boosted){r["HX-Boosted"]="true"}return r}function yn(n,e){const t=ne(e,"hx-params");if(t){if(t==="none"){return new FormData}else if(t==="*"){return n}else if(t.indexOf("not ")===0){ie(t.slice(4).split(","),function(e){e=e.trim();n.delete(e)});return n}else{const r=new FormData;ie(t.split(","),function(t){t=t.trim();if(n.has(t)){n.getAll(t).forEach(function(e){r.append(t,e)})}});return r}}else{return n}}function xn(e){return!!ee(e,"href")&&ee(e,"href").indexOf("#")>=0}function bn(e,t){const n=t||ne(e,"hx-swap");const r={swapStyle:oe(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&oe(e).boosted&&!xn(e)){r.show="top"}if(n){const s=X(n);if(s.length>0){for(let e=0;e<s.length;e++){const l=s[e];if(l.indexOf("swap:")===0){r.swapDelay=d(l.slice(5))}else if(l.indexOf("settle:")===0){r.settleDelay=d(l.slice(7))}else if(l.indexOf("transition:")===0){r.transition=l.slice(11)==="true"}else if(l.indexOf("ignoreTitle:")===0){r.ignoreTitle=l.slice(12)==="true"}else if(l.indexOf("scroll:")===0){const c=l.slice(7);var o=c.split(":");const u=o.pop();var i=o.length>0?o.join(":"):null;r.scroll=u;r.scrollTarget=i}else if(l.indexOf("show:")===0){const f=l.slice(5);var o=f.split(":");const a=o.pop();var i=o.length>0?o.join(":"):null;r.show=a;r.showTarget=i}else if(l.indexOf("focus-scroll:")===0){const h=l.slice("focus-scroll:".length);r.focusScroll=h=="true"}else if(e==0){r.swapStyle=l}else{R("Unknown modifier in hx-swap: "+l)}}}}return r}function vn(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function wn(t,n,r){let o=null;jt(n,function(e){if(o==null){o=e.encodeParameters(t,r,n)}});if(o!=null){return o}else{if(vn(n)){return hn(new FormData,Pn(r))}else{return gn(r)}}}function Sn(e){return{tasks:[],elts:[e]}}function En(e,t){const n=e[0];const r=e[e.length-1];if(t.scroll){var o=null;if(t.scrollTarget){o=ce(ue(n,t.scrollTarget))}if(t.scroll==="top"&&(n||o)){o=o||n;o.scrollTop=0}if(t.scroll==="bottom"&&(r||o)){o=o||r;o.scrollTop=o.scrollHeight}if(typeof t.scroll==="number"){b().setTimeout(function(){window.scrollTo(0,t.scroll)},0)}}if(t.show){var o=null;if(t.showTarget){let e=t.showTarget;if(t.showTarget==="window"){e="body"}o=ce(ue(n,e))}if(t.show==="top"&&(n||o)){o=o||n;o.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(r||o)){o=o||r;o.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Cn(r,e,o,i,s){if(i==null){i={}}if(r==null){return i}const l=a(r,e);if(l){let e=l.trim();let t=o;if(e==="unset"){return null}if(e.indexOf("javascript:")===0){e=e.slice(11);t=true}else if(e.indexOf("js:")===0){e=e.slice(3);t=true}if(e.indexOf("{")!==0){e="{"+e+"}"}let n;if(t){n=On(r,function(){if(s){return Function("event","return ("+e+")").call(r,s)}else{return Function("return ("+e+")").call(r)}},{})}else{n=v(e)}for(const c in n){if(n.hasOwnProperty(c)){if(i[c]==null){i[c]=n[c]}}}}return Cn(ce(u(r)),e,o,i,s)}function On(e,t,n){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return n}}function Rn(e,t,n){return Cn(e,"hx-vars",true,n,t)}function Hn(e,t,n){return Cn(e,"hx-vals",false,n,t)}function Tn(e,t){return le(Rn(e,t),Hn(e,t))}function qn(t,n,r){if(r!==null){try{t.setRequestHeader(n,r)}catch(e){t.setRequestHeader(n,encodeURIComponent(r));t.setRequestHeader(n+"-URI-AutoEncoded","true")}}}function An(t){if(t.responseURL){try{const e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(te().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function H(e,t){return t.test(e.getAllResponseHeaders())}function Ln(t,n,r){t=t.toLowerCase();if(r){if(r instanceof Element||typeof r==="string"){return he(t,n,null,null,{targetOverride:w(r)||ve,returnPromise:true})}else{let e=w(r.target);if(r.target&&!e||r.source&&!e&&!w(r.source)){e=ve}return he(t,n,w(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:e,swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(t,n,null,null,{returnPromise:true})}}function Nn(e){const t=[];while(e){t.push(e);e=e.parentElement}return t}function In(e,t,n){const r=new URL(t,location.protocol!=="about:"?location.href:window.origin);const o=location.protocol!=="about:"?location.origin:window.origin;const i=o===r.origin;if(Q.config.selfRequestsOnly){if(!i){return false}}return ae(e,"htmx:validateUrl",le({url:r,sameHost:i},n))}function Pn(e){if(e instanceof FormData)return e;const t=new FormData;for(const n in e){if(e.hasOwnProperty(n)){if(e[n]&&typeof e[n].forEach==="function"){e[n].forEach(function(e){t.append(n,e)})}else if(typeof e[n]==="object"&&!(e[n]instanceof Blob)){t.append(n,JSON.stringify(e[n]))}else{t.append(n,e[n])}}}return t}function Dn(r,o,e){return new Proxy(e,{get:function(t,e){if(typeof e==="number")return t[e];if(e==="length")return t.length;if(e==="push"){return function(e){t.push(e);r.append(o,e)}}if(typeof t[e]==="function"){return function(){t[e].apply(t,arguments);r.delete(o);t.forEach(function(e){r.append(o,e)})}}if(t[e]&&t[e].length===1){return t[e][0]}else{return t[e]}},set:function(e,t,n){e[t]=n;r.delete(o);e.forEach(function(e){r.append(o,e)});return true}})}function kn(o){return new Proxy(o,{get:function(e,t){if(typeof t==="symbol"){const r=Reflect.get(e,t);if(typeof r==="function"){return function(){return r.apply(o,arguments)}}else{return r}}if(t==="toJSON"){return()=>Object.fromEntries(o)}if(t in e){if(typeof e[t]==="function"){return function(){return o[t].apply(o,arguments)}}}const n=o.getAll(t);if(n.length===0){return undefined}else if(n.length===1){return n[0]}else{return Dn(e,t,n)}},set:function(t,n,e){if(typeof n!=="string"){return false}t.delete(n);if(e&&typeof e.forEach==="function"){e.forEach(function(e){t.append(n,e)})}else if(typeof e==="object"&&!(e instanceof Blob)){t.append(n,JSON.stringify(e))}else{t.append(n,e)}return true},deleteProperty:function(e,t){if(typeof t==="string"){e.delete(t)}return true},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}function he(t,n,r,o,i,k){let s=null;let l=null;i=i!=null?i:{};if(i.returnPromise&&typeof Promise!=="undefined"){var e=new Promise(function(e,t){s=e;l=t})}if(r==null){r=te().body}const M=i.handler||jn;const F=i.select||null;if(!se(r)){re(s);return e}const c=i.targetOverride||ce(Ee(r));if(c==null||c==ve){fe(r,"htmx:targetError",{target:ne(r,"hx-target")});re(l);return e}let u=oe(r);const f=u.lastButtonClicked;if(f){const A=ee(f,"formaction");if(A!=null){n=A}const L=ee(f,"formmethod");if(L!=null){if(de.includes(L.toLowerCase())){t=L}else{re(s);return e}}}const a=ne(r,"hx-confirm");if(k===undefined){const K=function(e){return he(t,n,r,o,i,!!e)};const G={target:c,elt:r,path:n,verb:t,triggeringEvent:o,etc:i,issueRequest:K,question:a};if(ae(r,"htmx:confirm",G)===false){re(s);return e}}let h=r;let d=ne(r,"hx-sync");let p=null;let X=false;if(d){const N=d.split(":");const I=N[0].trim();if(I==="this"){h=Se(r,"hx-sync")}else{h=ce(ue(r,I))}d=(N[1]||"drop").trim();u=oe(h);if(d==="drop"&&u.xhr&&u.abortable!==true){re(s);return e}else if(d==="abort"){if(u.xhr){re(s);return e}else{X=true}}else if(d==="replace"){ae(h,"htmx:abort")}else if(d.indexOf("queue")===0){const W=d.split(" ");p=(W[1]||"last").trim()}}if(u.xhr){if(u.abortable){ae(h,"htmx:abort")}else{if(p==null){if(o){const P=oe(o);if(P&&P.triggerSpec&&P.triggerSpec.queue){p=P.triggerSpec.queue}}if(p==null){p="last"}}if(u.queuedRequests==null){u.queuedRequests=[]}if(p==="first"&&u.queuedRequests.length===0){u.queuedRequests.push(function(){he(t,n,r,o,i)})}else if(p==="all"){u.queuedRequests.push(function(){he(t,n,r,o,i)})}else if(p==="last"){u.queuedRequests=[];u.queuedRequests.push(function(){he(t,n,r,o,i)})}re(s);return e}}const g=new XMLHttpRequest;u.xhr=g;u.abortable=X;const m=function(){u.xhr=null;u.abortable=false;if(u.queuedRequests!=null&&u.queuedRequests.length>0){const e=u.queuedRequests.shift();e()}};const B=ne(r,"hx-prompt");if(B){var y=prompt(B);if(y===null||!ae(r,"htmx:prompt",{prompt:y,target:c})){re(s);m();return e}}if(a&&!k){if(!confirm(a)){re(s);m();return e}}let x=mn(r,c,y);if(t!=="get"&&!vn(r)){x["Content-Type"]="application/x-www-form-urlencoded"}if(i.headers){x=le(x,i.headers)}const U=dn(r,t);let b=U.errors;const j=U.formData;if(i.values){hn(j,Pn(i.values))}const V=Pn(Tn(r,o));const v=hn(j,V);let w=yn(v,r);if(Q.config.getCacheBusterParam&&t==="get"){w.set("org.htmx.cache-buster",ee(c,"id")||"true")}if(n==null||n===""){n=location.href}const S=Cn(r,"hx-request");const _=oe(r).boosted;let E=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;const C={boosted:_,useUrlParams:E,formData:w,parameters:kn(w),unfilteredFormData:v,unfilteredParameters:kn(v),headers:x,elt:r,target:c,verb:t,errors:b,withCredentials:i.credentials||S.credentials||Q.config.withCredentials,timeout:i.timeout||S.timeout||Q.config.timeout,path:n,triggeringEvent:o};if(!ae(r,"htmx:configRequest",C)){re(s);m();return e}n=C.path;t=C.verb;x=C.headers;w=Pn(C.parameters);b=C.errors;E=C.useUrlParams;if(b&&b.length>0){ae(r,"htmx:validation:halted",C);re(s);m();return e}const z=n.split("#");const $=z[0];const O=z[1];let R=n;if(E){R=$;const Z=!w.keys().next().done;if(Z){if(R.indexOf("?")<0){R+="?"}else{R+="&"}R+=gn(w);if(O){R+="#"+O}}}if(!In(r,R,C)){fe(r,"htmx:invalidPath",C);re(l);m();return e}g.open(t.toUpperCase(),R,true);g.overrideMimeType("text/html");g.withCredentials=C.withCredentials;g.timeout=C.timeout;if(S.noHeaders){}else{for(const D in x){if(x.hasOwnProperty(D)){const Y=x[D];qn(g,D,Y)}}}const H={xhr:g,target:c,requestConfig:C,etc:i,boosted:_,select:F,pathInfo:{requestPath:n,finalRequestPath:R,responsePath:null,anchor:O}};g.onload=function(){try{const t=Nn(r);H.pathInfo.responsePath=An(g);M(r,H);if(H.keepIndicators!==true){rn(T,q)}ae(r,"htmx:afterRequest",H);ae(r,"htmx:afterOnLoad",H);if(!se(r)){let e=null;while(t.length>0&&e==null){const n=t.shift();if(se(n)){e=n}}if(e){ae(e,"htmx:afterRequest",H);ae(e,"htmx:afterOnLoad",H)}}re(s)}catch(e){fe(r,"htmx:onLoadError",le({error:e},H));throw e}finally{m()}};g.onerror=function(){rn(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:sendError",H);re(l);m()};g.onabort=function(){rn(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:sendAbort",H);re(l);m()};g.ontimeout=function(){rn(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:timeout",H);re(l);m()};if(!ae(r,"htmx:beforeRequest",H)){re(s);m();return e}var T=tn(r);var q=nn(r);ie(["loadstart","loadend","progress","abort"],function(t){ie([g,g.upload],function(e){e.addEventListener(t,function(e){ae(r,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ae(r,"htmx:beforeSend",H);const J=E?null:wn(g,r,w);g.send(J);return e}function Mn(e,t){const n=t.xhr;let r=null;let o=null;if(H(n,/HX-Push:/i)){r=n.getResponseHeader("HX-Push");o="push"}else if(H(n,/HX-Push-Url:/i)){r=n.getResponseHeader("HX-Push-Url");o="push"}else if(H(n,/HX-Replace-Url:/i)){r=n.getResponseHeader("HX-Replace-Url");o="replace"}if(r){if(r==="false"){return{}}else{return{type:o,path:r}}}const i=t.pathInfo.finalRequestPath;const s=t.pathInfo.responsePath;const l=ne(e,"hx-push-url");const c=ne(e,"hx-replace-url");const u=oe(e).boosted;let f=null;let a=null;if(l){f="push";a=l}else if(c){f="replace";a=c}else if(u){f="push";a=s||i}if(a){if(a==="false"){return{}}if(a==="true"){a=s||i}if(t.pathInfo.anchor&&a.indexOf("#")===-1){a=a+"#"+t.pathInfo.anchor}return{type:f,path:a}}else{return{}}}function Fn(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function Xn(e){for(var t=0;t<Q.config.responseHandling.length;t++){var n=Q.config.responseHandling[t];if(Fn(n,e.status)){return n}}return{swap:false}}function Bn(e){if(e){const t=f("title");if(t){t.textContent=e}else{window.document.title=e}}}function Un(e,t){if(t==="this"){return e}const n=ce(ue(e,t));if(n==null){fe(e,"htmx:targetError",{target:t});throw new Error(`Invalid re-target ${t}`)}return n}function jn(t,e){const n=e.xhr;let r=e.target;const o=e.etc;const i=e.select;if(!ae(t,"htmx:beforeOnLoad",e))return;if(H(n,/HX-Trigger:/i)){Je(n,"HX-Trigger",t)}if(H(n,/HX-Location:/i)){Gt();let e=n.getResponseHeader("HX-Location");var s;if(e.indexOf("{")===0){s=v(e);e=s.path;delete s.path}Ln("get",e,s).then(function(){Wt(e)});return}const l=H(n,/HX-Refresh:/i)&&n.getResponseHeader("HX-Refresh")==="true";if(H(n,/HX-Redirect:/i)){e.keepIndicators=true;Q.location.href=n.getResponseHeader("HX-Redirect");l&&Q.location.reload();return}if(l){e.keepIndicators=true;Q.location.reload();return}const c=Mn(t,e);const u=Xn(n);const f=u.swap;let a=!!u.error;let h=Q.config.ignoreTitle||u.ignoreTitle;let d=u.select;if(u.target){e.target=Un(t,u.target)}var p=o.swapOverride;if(p==null&&u.swapOverride){p=u.swapOverride}if(H(n,/HX-Retarget:/i)){e.target=Un(t,n.getResponseHeader("HX-Retarget"))}if(H(n,/HX-Reswap:/i)){p=n.getResponseHeader("HX-Reswap")}var g=n.response;var m=le({shouldSwap:f,serverResponse:g,isError:a,ignoreTitle:h,selectOverride:d,swapOverride:p},e);if(u.event&&!ae(r,u.event,m))return;if(!ae(r,"htmx:beforeSwap",m))return;r=m.target;g=m.serverResponse;a=m.isError;h=m.ignoreTitle;d=m.selectOverride;p=m.swapOverride;e.target=r;e.failed=a;e.successful=!a;if(m.shouldSwap){if(n.status===286){lt(t)}jt(t,function(e){g=e.transformResponse(g,n,t)});if(c.type){Gt()}var y=bn(t,p);if(!y.hasOwnProperty("ignoreTitle")){y.ignoreTitle=h}r.classList.add(Q.config.swappingClass);if(i){d=i}if(H(n,/HX-Reselect:/i)){d=n.getResponseHeader("HX-Reselect")}const x=ne(t,"hx-select-oob");const b=ne(t,"hx-select");$e(r,g,y,{select:d==="unset"?null:d||b,selectOOB:x,eventInfo:e,anchor:e.pathInfo.anchor,contextElement:t,afterSwapCallback:function(){if(H(n,/HX-Trigger-After-Swap:/i)){let e=t;if(!se(t)){e=te().body}Je(n,"HX-Trigger-After-Swap",e)}},afterSettleCallback:function(){if(H(n,/HX-Trigger-After-Settle:/i)){let e=t;if(!se(t)){e=te().body}Je(n,"HX-Trigger-After-Settle",e)}},beforeSwapCallback:function(){if(c.type){ae(te().body,"htmx:beforeHistoryUpdate",le({history:c},e));if(c.type==="push"){Wt(c.path);ae(te().body,"htmx:pushedIntoHistory",{path:c.path})}else{Zt(c.path);ae(te().body,"htmx:replacedInHistory",{path:c.path})}}}})}if(a){fe(t,"htmx:responseError",le({error:"Response Status Error Code "+n.status+" from "+e.pathInfo.requestPath},e))}}const Vn={};function _n(){return{init:function(e){return null},getSelectors:function(){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,n,r){return false},encodeParameters:function(e,t,n){return null}}}function zn(e,t){if(t.init){t.init(n)}Vn[e]=le(_n(),t)}function $n(e){delete Vn[e]}function Jn(e,n,r){if(n==undefined){n=[]}if(e==undefined){return n}if(r==undefined){r=[]}const t=a(e,"hx-ext");if(t){ie(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){r.push(e.slice(7));return}if(r.indexOf(e)<0){const t=Vn[e];if(t&&n.indexOf(t)<0){n.push(t)}}})}return Jn(ce(u(e)),n,r)}var Kn=false;te().addEventListener("DOMContentLoaded",function(){Kn=true});function Gn(e){if(Kn||te().readyState==="complete"){e()}else{te().addEventListener("DOMContentLoaded",e)}}function Wn(){if(Q.config.includeIndicatorStyles!==false){const e=Q.config.inlineStyleNonce?` nonce="${Q.config.inlineStyleNonce}"`:"";te().head.insertAdjacentHTML("beforeend","<style"+e+"> ."+Q.config.indicatorClass+"{opacity:0} ."+Q.config.requestClass+" ."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ."+Q.config.requestClass+"."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} </style>")}}function Zn(){const e=te().querySelector('meta[name="htmx-config"]');if(e){return v(e.content)}else{return null}}function Yn(){const e=Zn();if(e){Q.config=le(Q.config,e)}}Gn(function(){Yn();Wn();let e=te().body;Ft(e);const t=te().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){const t=e.target;const n=oe(t);if(n&&n.xhr){n.xhr.abort()}});const n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){en();ie(t,function(e){ae(e,"htmx:restored",{document:te(),triggerEvent:ae})})}else{if(n){n(e)}}};b().setTimeout(function(){ae(e,"htmx:load",{});e=null},0)});return Q}();
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@ export type ScaffoldFrontendProps = {
4
4
  templatesDirectory: string;
5
5
  projectAssetsDirectory: string;
6
6
  isSingleFrontend: boolean;
7
+ frontends: Frontend[];
7
8
  };
8
9
  export type Frontend = (typeof availableFrontends)[number];
9
10
  export type FrontendLabels = Record<Frontend, string>;
@@ -0,0 +1 @@
1
+ export declare const formatNavLink: (frontend: string) => `<a href="/${string}">${string}</a>`;
@@ -0,0 +1,6 @@
1
+ export const formatNavLink = (frontend) => {
2
+ const displayText = frontend === 'html' || frontend === 'htmx'
3
+ ? frontend.toUpperCase()
4
+ : frontend.charAt(0).toUpperCase() + frontend.slice(1);
5
+ return `<a href="/${frontend}">${displayText}</a>`;
6
+ };
@@ -1,41 +1,47 @@
1
1
  import { argv, exit } from 'node:process';
2
2
  import { parseArgs } from 'node:util';
3
3
  import { DEFAULT_ARG_LENGTH } from '../constants';
4
- import { availableAuthProviders, availableCodeQualityTools, availableDatabaseEngines, availableDatabaseHosts, availableDirectoryConfigurations, availableORMs, availableFrontends } from '../data';
5
- import { isAuthProvider, isCodeQualityTool, isDatabaseEngine, isDatabaseHost, isDirectoryConfig, isFrontend, isORM } from '../typeGuards';
4
+ import { availableAuthProviders, availableDatabaseEngines, availableDatabaseHosts, availableDirectoryConfigurations, availableORMs } from '../data';
5
+ import { isAuthProvider, isDatabaseEngine, isDatabaseHost, isDirectoryConfig, isORM } from '../typeGuards';
6
6
  export const parseCommandLineOptions = () => {
7
7
  const { values, positionals } = parseArgs({
8
8
  allowNegative: true,
9
9
  allowPositionals: true,
10
10
  args: argv.slice(DEFAULT_ARG_LENGTH),
11
11
  options: {
12
- angular: { type: 'string' },
12
+ angular: { type: 'boolean' },
13
+ 'angular-dir': { type: 'string' },
13
14
  assets: { type: 'string' },
14
15
  auth: { type: 'string' },
16
+ biome: { type: 'boolean' },
15
17
  build: { type: 'string' },
16
18
  database: { type: 'string' },
17
19
  debug: { default: false, short: 'd', type: 'boolean' },
18
20
  directory: { type: 'string' },
19
21
  engine: { type: 'string' },
20
- frontend: { multiple: true, type: 'string' },
22
+ 'eslint+prettier': { type: 'boolean' },
21
23
  git: { type: 'boolean' },
22
24
  help: { default: false, short: 'h', type: 'boolean' },
23
25
  host: { type: 'string' },
24
- html: { type: 'string' },
25
- 'html-script': { type: 'boolean' },
26
- htmx: { type: 'string' },
26
+ html: { type: 'boolean' },
27
+ 'html-dir': { type: 'string' },
28
+ 'html-scripts': { type: 'boolean' },
29
+ htmx: { type: 'boolean' },
30
+ 'htmx-dir': { type: 'string' },
31
+ install: { type: 'boolean' },
27
32
  lts: { default: false, type: 'boolean' },
28
- npm: { type: 'boolean' },
29
33
  orm: { type: 'string' },
30
34
  plugin: { multiple: true, type: 'string' },
31
- quality: { type: 'string' },
32
- react: { type: 'string' },
35
+ react: { type: 'boolean' },
36
+ 'react-dir': { type: 'string' },
33
37
  skip: { type: 'boolean' },
34
- svelte: { type: 'string' },
38
+ svelte: { type: 'boolean' },
39
+ 'svelte-dir': { type: 'string' },
35
40
  tailwind: { type: 'boolean' },
36
41
  'tailwind-input': { type: 'string' },
37
42
  'tailwind-output': { type: 'string' },
38
- vue: { type: 'string' }
43
+ vue: { type: 'boolean' },
44
+ 'vue-dir': { type: 'string' }
39
45
  }
40
46
  });
41
47
  const errors = [];
@@ -81,11 +87,17 @@ export const parseCommandLineOptions = () => {
81
87
  else if (values.skip) {
82
88
  orm = 'none';
83
89
  }
84
- const codeQualityTool = isCodeQualityTool(values.quality)
85
- ? values.quality
86
- : undefined;
87
- if (values.quality !== undefined && codeQualityTool === undefined) {
88
- errors.push(`Invalid code quality tool: "${values.quality}". Expected: [ ${availableCodeQualityTools.join(', ')} ]`);
90
+ const useESLintPrettier = values['eslint+prettier'];
91
+ const useBiome = values.biome;
92
+ let codeQualityTool;
93
+ if (useESLintPrettier) {
94
+ codeQualityTool = 'eslint+prettier';
95
+ }
96
+ else if (useBiome) {
97
+ codeQualityTool = 'biome';
98
+ }
99
+ else {
100
+ codeQualityTool = undefined;
89
101
  }
90
102
  const directoryConfig = values.directory !== undefined && isDirectoryConfig(values.directory)
91
103
  ? values.directory
@@ -93,11 +105,6 @@ export const parseCommandLineOptions = () => {
93
105
  if (values.directory !== undefined && directoryConfig === undefined) {
94
106
  errors.push(`Invalid directory configuration: "${values.directory}". Expected: [ ${availableDirectoryConfigurations.join(', ')} ]`);
95
107
  }
96
- for (const f of values.frontend || []) {
97
- if (isFrontend(f))
98
- continue;
99
- errors.push(`Invalid frontend: "${f}". Expected: [ ${availableFrontends.join(', ')} ]`);
100
- }
101
108
  if (errors.length > 0) {
102
109
  console.error(errors.join('\n'));
103
110
  exit(1);
@@ -113,17 +120,37 @@ export const parseCommandLineOptions = () => {
113
120
  console.warn('Warning: Setting a database directory without a database engine has no effect.');
114
121
  databaseDirectory = undefined;
115
122
  }
116
- const frontendsWithDirectory = availableFrontends.filter((f) => values[f] !== undefined);
123
+ const selectedFrontends = [];
124
+ // if (values.angular) selectedFrontends.push('angular')
125
+ if (values.html)
126
+ selectedFrontends.push('html');
127
+ if (values.htmx)
128
+ selectedFrontends.push('htmx');
129
+ if (values.react)
130
+ selectedFrontends.push('react');
131
+ if (values.svelte)
132
+ selectedFrontends.push('svelte');
133
+ if (values.vue)
134
+ selectedFrontends.push('vue');
117
135
  const frontendDirectories = {};
118
- for (const frontend of frontendsWithDirectory) {
119
- frontendDirectories[frontend] = values[frontend];
136
+ // if (values['angular-dir'] !== undefined) {
137
+ // frontendDirectories.angular = values['angular-dir']
138
+ // }
139
+ if (values['html-dir'] !== undefined) {
140
+ frontendDirectories.html = values['html-dir'];
120
141
  }
121
- const originalFrontends = values.frontend;
122
- const collector = new Set(originalFrontends ?? []);
123
- for (const frontend of frontendsWithDirectory) {
124
- collector.add(frontend);
142
+ if (values['htmx-dir'] !== undefined) {
143
+ frontendDirectories.htmx = values['htmx-dir'];
144
+ }
145
+ if (values['react-dir'] !== undefined) {
146
+ frontendDirectories.react = values['react-dir'];
147
+ }
148
+ if (values['svelte-dir'] !== undefined) {
149
+ frontendDirectories.svelte = values['svelte-dir'];
150
+ }
151
+ if (values['vue-dir'] !== undefined) {
152
+ frontendDirectories.vue = values['vue-dir'];
125
153
  }
126
- values.frontend = collector.size > 0 ? Array.from(collector) : undefined;
127
154
  if (values.plugin === undefined && values.skip) {
128
155
  values.plugin = ['none'];
129
156
  }
@@ -131,10 +158,7 @@ export const parseCommandLineOptions = () => {
131
158
  const hasTailwindFiles = values['tailwind-input'] !== undefined ||
132
159
  values['tailwind-output'] !== undefined;
133
160
  let tailwind = hasTailwindFiles
134
- ? {
135
- input: values['tailwind-input'],
136
- output: values['tailwind-output']
137
- }
161
+ ? { input: values['tailwind-input'], output: values['tailwind-output'] }
138
162
  : undefined;
139
163
  const useTailwind = values.tailwind ?? (hasTailwindFiles ? true : undefined);
140
164
  if (useTailwind === false && hasTailwindFiles) {
@@ -151,14 +175,14 @@ export const parseCommandLineOptions = () => {
151
175
  databaseHost,
152
176
  directoryConfig,
153
177
  frontendDirectories,
154
- frontends: values.frontend?.filter(isFrontend),
178
+ frontends: selectedFrontends.length ? selectedFrontends : undefined,
155
179
  initializeGitNow: values.git,
156
- installDependenciesNow: values.npm,
180
+ installDependenciesNow: values.install,
157
181
  orm,
158
182
  plugins,
159
183
  projectName,
160
184
  tailwind,
161
- useHTMLScripts: values['html-script'],
185
+ useHTMLScripts: values['html-scripts'],
162
186
  useTailwind
163
187
  };
164
188
  return {
package/package.json CHANGED
@@ -47,5 +47,5 @@
47
47
  "typecheck": "bun run tsc --noEmit"
48
48
  },
49
49
  "type": "module",
50
- "version": "0.3.3"
50
+ "version": "0.3.5"
51
51
  }
@@ -1,23 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export const Dropdown = () => {
4
- const [isOpen, setIsOpen] = useState(false);
5
-
6
- return (
7
- <details
8
- onPointerEnter={() => setIsOpen(true)}
9
- onPointerLeave={() => setIsOpen(false)}
10
- open={isOpen}
11
- >
12
- <summary>Pages</summary>
13
- <nav>
14
- <a href="/html">HTML</a>
15
- <a href="/react">React</a>
16
- <a href="/htmx">HTMX</a>
17
- <a href="/svelte">Svelte</a>
18
- <a href="/vue">Vue</a>
19
- <a href="/angular">Angular</a>
20
- </nav>
21
- </details>
22
- );
23
- };