diff --git a/assets/css/tailwind.config.js b/assets/css/tailwind.config.js
index 575381c..07d3a7b 100644
--- a/assets/css/tailwind.config.js
+++ b/assets/css/tailwind.config.js
@@ -1,14 +1,36 @@
const {join} = require("node:path");
/** @type {import('tailwindcss').Config} */
const root = join(__dirname, "../../");
-const content = join(root, "**/*.go");
+const contentGo = join(root, "**/*.go");
+const contentJs = join(root, "**/pages/**/*.js");
-console.log(content)
module.exports = {
- content: [content],
+ content: [contentGo, contentJs],
theme: {
- extend: {},
+ extend: {
+ colors: {
+ background: 'hsl(224, 71.4%, 4.1%)',
+ foreground: 'hsl(0, 0%, 89%)',
+ card: 'hsl(224, 71.4%, 4.1%)',
+ cardForeground: 'hsl(0, 0%, 89%)',
+ popover: 'hsl(224, 71.4%, 4.1%)',
+ popoverForeground: 'hsl(0, 0%, 89%)',
+ primary: 'hsl(0, 0%, 89%)',
+ primaryForeground: 'hsl(220.9, 39.3%, 11%)',
+ secondary: 'hsl(215, 27.9%, 16.9%)',
+ secondaryForeground: 'hsl(0, 0%, 89%)',
+ muted: 'hsl(215, 27.9%, 16.9%)',
+ mutedForeground: 'hsl(217.9, 10.6%, 64.9%)',
+ accent: 'hsl(215, 27.9%, 16.9%)',
+ accentForeground: 'hsl(0, 0%, 89%)',
+ destructive: 'hsl(0, 62.8%, 30.6%)',
+ destructiveForeground: 'hsl(0, 0%, 89%)',
+ border: 'hsl(215, 27.9%, 16.9%)',
+ input: 'hsl(215, 27.9%, 16.9%)',
+ ring: 'hsl(216, 12.2%, 83.9%)',
+ },
+ },
},
plugins: [],
};
diff --git a/assets/js/htmx.ts b/assets/js/htmx.ts
new file mode 100644
index 0000000..e69de29
diff --git a/assets/js/extensions/debug.ts b/assets/js/htmxextensions/debug.ts
similarity index 100%
rename from assets/js/extensions/debug.ts
rename to assets/js/htmxextensions/debug.ts
diff --git a/assets/js/extensions/mutation-error.ts b/assets/js/htmxextensions/mutation-error.ts
similarity index 100%
rename from assets/js/extensions/mutation-error.ts
rename to assets/js/htmxextensions/mutation-error.ts
diff --git a/assets/js/extensions/pathdeps.ts b/assets/js/htmxextensions/pathdeps.ts
similarity index 100%
rename from assets/js/extensions/pathdeps.ts
rename to assets/js/htmxextensions/pathdeps.ts
diff --git a/assets/js/extensions/response-targets.ts b/assets/js/htmxextensions/response-targets.ts
similarity index 100%
rename from assets/js/extensions/response-targets.ts
rename to assets/js/htmxextensions/response-targets.ts
diff --git a/assets/js/extensions/trigger-children.ts b/assets/js/htmxextensions/trigger-children.ts
similarity index 100%
rename from assets/js/extensions/trigger-children.ts
rename to assets/js/htmxextensions/trigger-children.ts
diff --git a/assets/js/mhtml.ts b/assets/js/mhtml.ts
index 51bea3a..2debef2 100644
--- a/assets/js/mhtml.ts
+++ b/assets/js/mhtml.ts
@@ -1,9 +1,9 @@
import htmx from "htmx.org";
-import "./extensions/pathdeps";
-import "./extensions/trigger-children";
-import "./extensions/debug";
-import "./extensions/response-targets";
-import "./extensions/mutation-error";
+import "./htmxextensions/pathdeps";
+import "./htmxextensions/trigger-children";
+import "./htmxextensions/debug";
+import "./htmxextensions/response-targets";
+import "./htmxextensions/mutation-error";
function watchUrl(callback: (oldUrl: string, newUrl: string) => void) {
let lastUrl = window.location.href;
diff --git a/assets/js/package-lock.json b/assets/js/package-lock.json
index ffeb3b8..80d97ab 100644
--- a/assets/js/package-lock.json
+++ b/assets/js/package-lock.json
@@ -15,6 +15,7 @@
"@swc/core": "^1.7.26",
"@types/node": "^22.5.4",
"prettier": "^3.3.3",
+ "shiki": "^1.17.6",
"tailwindcss": "^3.4.11",
"tsup": "^8.2.4",
"typescript": "^5.6.2"
@@ -734,6 +735,56 @@
"win32"
]
},
+ "node_modules/@shikijs/core": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.17.6.tgz",
+ "integrity": "sha512-9ztslig6/YmCg/XwESAXbKjAjOhaq6HVced9NY6qcbDz1X5g/S90Wco2vMjBNX/6V71ASkzri76JewSGPa7kiQ==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/engine-javascript": "1.17.6",
+ "@shikijs/engine-oniguruma": "1.17.6",
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.2"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.17.6.tgz",
+ "integrity": "sha512-5EEZj8tVcierNxm4V0UMS2PVoflb0UJPalWWV8l9rRg+oOfnr5VivqBJbkyq5grltVPvByIXvVbY8GSM/356jQ==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/types": "1.17.6",
+ "oniguruma-to-js": "0.4.3"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.17.6.tgz",
+ "integrity": "sha512-NLfWDMXFYe0nDHFbEoyZdz89aIIey3bTfF3zLYSUNTXks5s4uinZVmuPOFf1HfTeGqIn8uErJSBc3VnpJO7Alw==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.17.6.tgz",
+ "integrity": "sha512-ndTFa2TJi2w51ddKQDn3Jy8f6K4E5Q2x3dA3Hmsd3+YmxDQ10UWHjcw7VbVbKzv3VcUvYPLy+z9neqytSzUMUg==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz",
+ "integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==",
+ "dev": true
+ },
"node_modules/@swc/core": {
"version": "1.7.26",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.26.tgz",
@@ -953,6 +1004,24 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
"node_modules/@types/node": {
"version": "22.5.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
@@ -962,6 +1031,18 @@
"undici-types": "~6.19.2"
}
},
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "dev": true
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -1092,6 +1173,36 @@
"node": ">= 6"
}
},
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -1134,6 +1245,16 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -1195,6 +1316,28 @@
}
}
},
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1457,6 +1600,52 @@
"node": ">= 0.4"
}
},
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.2.tgz",
+ "integrity": "sha512-RP5wNpj5nm1Z8cloDv4Sl4RS8jH5HYa0v93YB6Wb4poEzgMo/dAAL0KcT4974dCjcNG5pkLqTImeFHHCwwfY3g==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/htmx.org": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.2.tgz",
@@ -1636,6 +1825,27 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true
},
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
+ "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -1651,6 +1861,95 @@
"node": ">= 8"
}
},
+ "node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
+ "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
+ "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
+ "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -1786,6 +2085,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/oniguruma-to-js": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz",
+ "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==",
+ "dev": true,
+ "dependencies": {
+ "regex": "^4.3.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/package-json-from-dist": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
@@ -2033,6 +2344,16 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/property-information": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+ "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -2083,6 +2404,12 @@
"node": ">=8.10.0"
}
},
+ "node_modules/regex": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz",
+ "integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==",
+ "dev": true
+ },
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -2198,6 +2525,20 @@
"node": ">=8"
}
},
+ "node_modules/shiki": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.17.6.tgz",
+ "integrity": "sha512-RejGugKpDM75vh6YtF9R771acxHRDikC/01kxsUGW+Pnaz3pTY+c8aZB5CnD7p0vuFPs1HaoAIU/4E+NCfS+mQ==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/core": "1.17.6",
+ "@shikijs/engine-javascript": "1.17.6",
+ "@shikijs/engine-oniguruma": "1.17.6",
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -2234,6 +2575,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -2293,6 +2644,20 @@
"node": ">=8"
}
},
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "dev": true,
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
@@ -2529,6 +2894,16 @@
"tree-kill": "cli.js"
}
},
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -2605,12 +2980,108 @@
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true
},
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
@@ -2745,6 +3216,16 @@
"engines": {
"node": ">= 14"
}
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
}
}
}
diff --git a/assets/js/package.json b/assets/js/package.json
index a417950..0f245b6 100644
--- a/assets/js/package.json
+++ b/assets/js/package.json
@@ -3,8 +3,8 @@
"version": "1.0.0",
"main": "mhtml.js",
"scripts": {
- "watch": "tsup ./mhtml.ts --watch --config ./tsup.config.ts --sourcemap inline",
- "build": "tsup ./mhtml.ts --minify --config ./tsup.config.ts",
+ "watch": "tsup --watch --config ./tsup.config.ts --sourcemap inline",
+ "build": "tsup --minify --config ./tsup.config.ts",
"tailwind:watch": "npx tailwindcss -i ./input.css -o ./output.css --watch",
"pretty": "prettier --write ."
},
@@ -18,6 +18,7 @@
"@swc/core": "^1.7.26",
"@types/node": "^22.5.4",
"prettier": "^3.3.3",
+ "shiki": "^1.17.6",
"tailwindcss": "^3.4.11",
"tsup": "^8.2.4",
"typescript": "^5.6.2"
diff --git a/assets/js/scripts/shiki.ts b/assets/js/scripts/shiki.ts
new file mode 100644
index 0000000..972ae72
--- /dev/null
+++ b/assets/js/scripts/shiki.ts
@@ -0,0 +1,5 @@
+import htmx from "htmx.org";
+
+htmx.findAll("body");
+
+alert("here");
diff --git a/assets/js/tsup.config.ts b/assets/js/tsup.config.ts
index 610f00e..b2f3ec2 100644
--- a/assets/js/tsup.config.ts
+++ b/assets/js/tsup.config.ts
@@ -2,7 +2,7 @@ import { defineConfig } from "tsup";
export default defineConfig({
format: ["esm"],
- entry: ["./src/mhtml.ts"],
+ entry: ["mhtml.ts", "./scripts/*.ts"],
outDir: "./../dist",
dts: false,
shims: true,
@@ -20,5 +20,4 @@ export default defineConfig({
bundle: true,
// https://github.com/egoist/tsup/issues/619
noExternal: [/(.*)/],
- splitting: false,
});
diff --git a/h/livereload.go b/h/livereload.go
index 0dc910d..f30375b 100644
--- a/h/livereload.go
+++ b/h/livereload.go
@@ -25,7 +25,7 @@ func LiveReloadHandler(c *fiber.Ctx) error {
}
func LiveReload() Renderable {
- return Div(Get("/livereload"), Trigger("every 2s"))
+ return Div(Get("/livereload"), Trigger("every 200ms"))
}
func AddLiveReloadHandler(path string, app *fiber.App) {
diff --git a/h/tag.go b/h/tag.go
index 3b36a58..9c0696b 100644
--- a/h/tag.go
+++ b/h/tag.go
@@ -160,7 +160,7 @@ func Text(text string) Renderable {
}
func Pf(format string, args ...interface{}) Renderable {
- return P(fmt.Sprintf(format, args...))
+ return P(Text(fmt.Sprintf(format, args...)))
}
func Target(target string) Renderable {
@@ -230,7 +230,8 @@ func Script(url string) Renderable {
return &Node{
tag: "script",
attributes: map[string]string{
- "src": url,
+ "src": url,
+ "type": "module",
},
children: make([]Renderable, 0),
}
@@ -244,10 +245,26 @@ func Raw(text string) Renderable {
}
}
+func MultiLineQuotes(text string) string {
+ return "`" + text + "`"
+}
+
+func RawF(text string, args any) Renderable {
+ return &Node{
+ tag: "raw",
+ children: make([]Renderable, 0),
+ value: fmt.Sprintf(text, args),
+ }
+}
+
func RawScript(text string) Renderable {
return Raw("")
}
+func Pre(children ...Renderable) Renderable {
+ return Tag("pre", children...)
+}
+
func Div(children ...Renderable) Renderable {
return Tag("div", children...)
}
@@ -347,24 +364,48 @@ func Indicator(tag string) Renderable {
return Attribute("hx-indicator", tag)
}
-func P(text string, children ...Renderable) Renderable {
- return &Node{
- tag: "p",
- children: children,
- text: text,
- }
+func P(children ...Renderable) Renderable {
+ return Tag("p", children...)
+}
+
+func H1(children ...Renderable) Renderable {
+ return Tag("h1", children...)
+}
+
+func H2(children ...Renderable) Renderable {
+ return Tag("h2", children...)
+}
+
+func H3(children ...Renderable) Renderable {
+ return Tag("h3", children...)
+}
+
+func H4(children ...Renderable) Renderable {
+ return Tag("h4", children...)
+}
+
+func H5(children ...Renderable) Renderable {
+ return Tag("h5", children...)
+}
+
+func H6(children ...Renderable) Renderable {
+ return Tag("h6", children...)
+}
+
+func Img(children ...Renderable) Renderable {
+ return Tag("img", children...)
+}
+
+func Src(src string) Renderable {
+ return Attribute("src", src)
}
func Form(children ...Renderable) Renderable {
return Tag("form", children...)
}
-func A(text string, children ...Renderable) Renderable {
- return &Node{
- tag: "a",
- children: children,
- text: text,
- }
+func A(children ...Renderable) Renderable {
+ return Tag("a", children...)
}
func Nav(children ...Renderable) Renderable {
diff --git a/justfile b/justfile
index f6ebf96..7bfeddf 100644
--- a/justfile
+++ b/justfile
@@ -1,5 +1,5 @@
-run-app:
- just run-gen && air & just watch-js & just watch-gen
+dev:
+ just run-gen && just build-css && air & just watch-js & just watch-gen & just watch-css
bundle:
rm -rf dist
diff --git a/news/views.go b/news/views.go
index 34713fe..1cfbf70 100644
--- a/news/views.go
+++ b/news/views.go
@@ -17,7 +17,7 @@ func StoryList() h.Renderable {
time.Sleep(200 * time.Millisecond)
if len(*posts) == 0 {
- return h.P("No results found")
+ return h.Pf("No results found")
}
return h.Fragment(
@@ -31,14 +31,14 @@ func StoryCard(post Post) h.Renderable {
url := fmt.Sprintf("/news/%d", post.Id)
return h.Div(
h.Class("items-center bg-indigo-200 p-4 rounded"),
- h.A(post.Title, h.Href(url)),
+ h.A(h.Text(post.Title), h.Href(url)),
)
}
func StoryFull(id string) h.Renderable {
post, err := Get(id)
if err != nil {
- return h.P(err.Error())
+ return h.Pf(err.Error())
}
return StoryCard(post)
}
diff --git a/pages/index.go b/pages/index.go
index fb2014e..cbb2ebb 100644
--- a/pages/index.go
+++ b/pages/index.go
@@ -1,17 +1,89 @@
package pages
import (
+ "fmt"
"github.com/gofiber/fiber/v2"
"mhtml/h"
+ "os"
+ "time"
)
func IndexPage(c *fiber.Ctx) *h.Page {
return h.NewPage(h.Html(
- h.HxExtension("path-deps, response-targets, mutation-error"),
+ h.Class("bg-background flex flex-col items-center"),
h.Head(
- h.Script("https://cdn.tailwindcss.com"),
- h.Script("/js/dist/mhtml.js"),
+ h.Link("/public/main.css", "stylesheet"),
+ h.Script("/public/mhtml.js"),
+ h.Script("/public/scripts/shiki.js"),
+ ),
+ h.Body(
+ h.Class("flex flex-col gap-3"),
+ // Navbar
+ h.Div(
+ h.Class("flex justify-between items-center w-full p-6"),
+ h.Div(
+ h.Class("text-white text-xl font-bold"),
+ h.Text("MHTML"),
+ ),
+ h.Div(
+ h.Class("flex gap-4"),
+ h.A(h.Href("/pricing"), h.Class("text-white"), h.Text("Pricing")),
+ h.A(h.Href("/docs"), h.Class("text-white"), h.Text("Docs")),
+ h.A(h.Href("/app"), h.Class("text-white"), h.Text("App")),
+ ),
+ ),
+
+ // Hero Section
+ h.Div(
+ h.Class("flex flex-col items-center justify-center gap-6 p-12 bg-background text-center"),
+ h.H1(
+ h.Class("text-white text-4xl sm:text-5xl font-bold max-w-3xl"),
+ h.Text("Go and HTMX: The Simple Stack"),
+ ),
+ h.P(
+ h.Class("text-white text-lg sm:text-xl max-w-2xl"),
+ h.Text("Combine the simplicity of Go with the power of HTMX for dynamic, JavaScript-light web development."),
+ ),
+ h.A(h.Href("/get-started"),
+ h.Class("bg-white text-background px-6 py-3 rounded-md font-semibold mt-4"),
+ h.Text("Join the waitlist"),
+ ),
+ ),
+
+ // Explore Section
+ h.Div(
+ h.Class("w-full max-w-4xl"),
+ CodeExample(),
+ ),
+
+ // Footer Section
+ h.Div(
+ h.Class("flex justify-center items-center py-6"),
+ h.Text(fmt.Sprintf("© %d MHTML", time.Now().Year())),
+ ),
),
- h.Body(),
))
}
+
+func CodeExample() h.Renderable {
+ code, err := os.ReadFile("pages/assets/_example.go")
+ scriptSrc, err := os.ReadFile("pages/assets/shiki.js")
+
+ if err != nil {
+ return h.Pf("Error loading code example")
+ }
+
+ fmt.Printf("%s\n", code)
+
+ script := fmt.Sprintf(string(scriptSrc), string(code))
+
+ return h.Div(
+ h.Class("text-white rounded-lg"),
+ h.Pre(h.Id("foo")),
+ h.RawF(`
+
+ `, script),
+ )
+}
diff --git a/pages/patients.index.go b/pages/patients.index.go
index 94ea9d1..fb3bb2a 100644
--- a/pages/patients.index.go
+++ b/pages/patients.index.go
@@ -14,7 +14,7 @@ func PatientsIndex(ctx *fiber.Ctx) *h.Page {
h.Div(
h.Div(
h.Class("flex justify-between items-center"),
- h.P("Manage Patients", h.Class("text-lg font-bold")),
+ h.P(h.Text("Manage Patients"), h.Class("text-lg font-bold")),
patient.AddPatientButton(),
),
h.View(patient.List, h.ReloadParams{
diff --git a/partials/nav.go b/partials/nav.go
index 15f3b4e..2c0fd79 100644
--- a/partials/nav.go
+++ b/partials/nav.go
@@ -19,7 +19,7 @@ func NavBar() h.Renderable {
h.Boost(),
h.Children(
h.Map(links, func(link Link) h.Renderable {
- return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
+ return h.A(h.Text(link.Name), h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
})...,
))
}
diff --git a/partials/news.go b/partials/news.go
index 27238e8..2dc5a3b 100644
--- a/partials/news.go
+++ b/partials/news.go
@@ -56,10 +56,10 @@ func SheetOpen() h.Renderable {
h.Class(`fixed top-0 right-0 h-full w-96 bg-gray-100 shadow-lg z-50`),
h.Div(
h.Class("p-4 overflow-y-auto h-full w-full flex flex-col gap-4"),
- h.P("My NewsSheet",
+ h.P(h.Text("News Sheet"),
h.Class("text-lg font-bold"),
),
- h.P("This is a sheet",
+ h.P(h.Text("Here are the latest news stories."),
h.Class("text-sm mt-2"),
),
ui.Button(ui.ButtonProps{
diff --git a/partials/patient/patient.go b/partials/patient/patient.go
index 5570f82..547fef9 100644
--- a/partials/patient/patient.go
+++ b/partials/patient/patient.go
@@ -15,14 +15,14 @@ func List(ctx *fiber.Ctx) *h.Partial {
if err != nil {
return h.NewPartial(h.Div(
h.Class("patient-list"),
- h.P("Error loading patients"),
+ h.Pf("Error loading patients"),
))
}
if len(patients) == 0 {
return h.NewPartial(h.Div(
h.Class("patient-list"),
- h.P("No patients found"),
+ h.Pf("No patients found"),
))
}
@@ -50,7 +50,7 @@ func AddPatientSheet(onClosePath string) h.Renderable {
ClassName: "w-[400px] bg-gray-100 p-4",
Root: h.Div(
h.Class("flex flex-col gap-4"),
- h.P("Add Patient", h.Class("text-lg font-bold")),
+ h.P(h.Text("Add Patient"), h.Class("text-lg font-bold")),
addPatientForm(),
),
})
@@ -62,19 +62,19 @@ func ValidateForm(ctx *fiber.Ctx) *h.Partial {
if trigger == "name" {
if strings.ToLower(value) == "sydne" {
- return h.NewPartial(h.P("that name is reserved"))
+ return h.NewPartial(h.Pf("that name is reserved"))
}
}
if trigger == "reason-for-visit" {
if strings.ToLower(value) == "arm hurts" {
- return h.NewPartial(h.P("lol that reason is fake"))
+ return h.NewPartial(h.Pf("lol that reason is fake"))
}
}
if trigger == "location-name" {
if strings.ToLower(value) == "hospital" {
- return h.NewPartial(h.P("that location is reserved"))
+ return h.NewPartial(h.Pf("that location is reserved"))
}
}