diff --git a/.build.ps1 b/.build.ps1 index df0148d..ce77b14 100644 --- a/.build.ps1 +++ b/.build.ps1 @@ -1,17 +1,16 @@ '# required powershell modules' -Install-Module -Name PSSQLite -MinimumVersion 1.1.0 -MaximumVersion 1.99.99 -Verbose # MIT -Install-Module -Name Pode -MinimumVersion 2.11.0 -MaximumVersion 2.99.99 -Verbose # MIT +# Install-Module -Name PSSQLite -MinimumVersion 1.1.0 -MaximumVersion 1.99.99 -Verbose # MIT +Install-Module -Name Pode -MinimumVersion 2.11.1 -MaximumVersion 2.99.99 -Verbose # MIT '# development powershell modules' Install-Module -Name Pester -MinimumVersion 5.6.1 -MaximumVersion 5.99.99 -Verbose # Apache 2.0 Install-Module -Name PSScriptAnalyzer -MinimumVersion 1.23.0 -MaximumVersion 1.99.99 -Verbose # MIT '# required npm packages' -npm install htmx.org@next htmx-ext-client-side-templates htmx-ext-debug htmx-ext-json-enc mustache -npm install tailwindcss @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio +npm install htmx.org@next htmx-ext-client-side-templates htmx-ext-debug htmx-ext-json-enc mustache tailwindcss @tailwindcss/cli '# development npm packages' -npm install -D eslint globals htmx-ext-debug @eslint/js prettier prettier-plugin-tailwindcss prettier-plugin-sql +npm install -D eslint globals @eslint/js prettier prettier-plugin-tailwindcss prettier-plugin-sql '# update packages' npm update @@ -34,8 +33,8 @@ Copy-Item -Path './node_modules/mustache/mustache.min.js' -Destination './public '# initialize database' $db = './podex.db' if ((Test-Path -Path $db)) { - $confirm = Read-Host "Do you want to reinitialize the database? (y/N)" - if ($confirm -eq "y") { + $confirm = Read-Host 'Do you want to reinitialize the database? (y/N)' + if ($confirm -eq 'y') { Invoke-SqliteQuery -DataSource $db -Query (Get-Content -Path './api/debug/init.sql' -Raw) -Verbose } } diff --git a/.gitignore b/.gitignore index 6b98a49..86741ae 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,4 @@ package-lock.json .vscode/ podex.db public/css/output.css -public/js/client-side-templates.js -public/js/htmx.min.js -public/js/json-enc.js -public/js/mustache.min.js +public/js/ diff --git a/.prettierignore b/.prettierignore index 100bf72..f5b7eaf 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,7 +9,4 @@ package-lock.json **/.vscode podex.db public/css/output.css -public/js/client-side-templates.js -public/js/htmx.min.js -public/js/json-enc.js -public/js/mustache.min.js +public/js/** diff --git a/README.md b/README.md index d4d1f96..5a83a60 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ ### Table of Contents -- [Introduction](#introduction) -- [Features](#features) -- [Technical Stack](#technical-stack) -- [Setup](#setup) -- [Running the Project](#running-the-project) -- [Contributing](#contributing) -- [License](#license) +- [Introduction](#introduction) +- [Features](#features) +- [Technical Stack](#technical-stack) +- [Setup](#setup) +- [Running the Project](#running-the-project) +- [Contributing](#contributing) +- [License](#license) ### Introduction @@ -16,8 +16,8 @@ Podex is a framework for building full-stack web applications using PowerShell/P ### Technical Stack -- **Backend**: [PowerShell Core](https://github.com/PowerShell/PowerShell), [Pode](https://github.com/Badgerati/Pode), [SQLite](https://www.sqlite.org/index.html) -- **Frontend**: [htmx](https://htmx.org/), [Mustache](https://mustache.github.io/), [Tailwind CSS](https://tailwindcss.com/) +- **Backend**: [PowerShell Core](https://github.com/PowerShell/PowerShell), [Pode](https://github.com/Badgerati/Pode), [SQLite](https://www.sqlite.org/index.html) +- **Frontend**: [htmx](https://htmx.org/), [Mustache](https://mustache.github.io/), [Tailwind CSS](https://tailwindcss.com/) ### Setup diff --git a/errors/404.html.pode b/errors/404.html.pode index 540f971..38dd97b 100644 --- a/errors/404.html.pode +++ b/errors/404.html.pode @@ -17,7 +17,7 @@ $(Use-PodePartialView -Path 'partials/header' -Data @{ PageName = '404 Not Found' } ;) -
+

404 Not Found

Render Time: $([DateTime]::Now.ToString('yyyy-MM-dd HH:mm:ss') ;)

diff --git a/errors/default.html.pode b/errors/default.html.pode index bf0a36a..e115ec7 100644 --- a/errors/default.html.pode +++ b/errors/default.html.pode @@ -17,7 +17,7 @@ $(Use-PodePartialView -Path 'partials/header' -Data @{ PageName = '$($data.status.code ;) Error' } ;) -
+

$($data.status.code ;) Error

Description: $($data.status.description ;)

diff --git a/eslint.config.mjs b/eslint.config.mjs index 46e7455..89204a4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,7 +18,7 @@ export default [ sourceType: 'module', }, }, - ignores: ['public/js/*', 'node_modules/*'], + ignores: ['public/js/*', 'node_modules/*', 'logs/*', '.vscode/*'], rules: {}, }, pluginJs.configs.recommended, diff --git a/package.json b/package.json index ba5d599..2a85a4a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "format": "prettier --write .", "lint": "eslint .", - "css": "tailwindcss -c ./tailwind.config.mjs -i ./public/css/tailwind.css -o ./public/css/output.css #--minify", + "css": "npx @tailwindcss/cli -i ./public/css/tailwind.css -o ./public/css/output.css", "analyze": "pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -Command \"Import-Module PSScriptAnalyzer ; Invoke-ScriptAnalyzer -Path ./\"", "test": "pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -Command \"Import-Module Pester ; Invoke-Pester -Path ./tests/*.ps1\"", "build": "pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass \"./.build.ps1\"", @@ -24,21 +24,22 @@ }, "dependencies": { "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/cli": "^4.0.0", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", "htmx-ext-client-side-templates": "^2.0.0", + "htmx-ext-debug": "^2.0.0", "htmx-ext-json-enc": "^2.0.1", - "htmx.org": "^2.0.3", + "htmx.org": "^2.0.4", "mustache": "^4.2.0", - "tailwindcss": "^3.4.14" + "tailwindcss": "^4.0.0" }, "devDependencies": { - "@eslint/js": "^9.12.0", - "eslint": "^9.12.0", - "globals": "^15.11.0", - "htmx-ext-debug": "^2.0.0", - "prettier": "^3.3.3", + "@eslint/js": "^9.19.0", + "eslint": "^9.19.0", + "globals": "^15.14.0", + "prettier": "^3.4.2", "prettier-plugin-sql": "^0.18.1", - "prettier-plugin-tailwindcss": "^0.6.8" + "prettier-plugin-tailwindcss": "^0.6.11" } } diff --git a/podex.ps1 b/podex.ps1 index 03b13d6..5cab7e2 100644 --- a/podex.ps1 +++ b/podex.ps1 @@ -4,17 +4,17 @@ Import-Module -Name 'Pode' -MaximumVersion 2.99.99 -Force function Write-FormattedLog { param([string]$tag, [string]$log, [switch]$save) switch ($tag) { - 'debug' { $icon = "🐞" } - 'database' { $icon = "💾" } - 'api' { $icon = "🔗" } - 'informational' { $icon = "â„šī¸" } - 'verbose' { $icon = "🔍" } - 'warning' { $icon = "âš ī¸" } - 'error' { $icon = "❌" } - default { $icon = "✅" } + 'debug' { $icon = '🐞' } + 'database' { $icon = '💾' } + 'api' { $icon = '🔗' } + 'informational' { $icon = 'â„šī¸' } + 'verbose' { $icon = '🔍' } + 'warning' { $icon = 'âš ī¸' } + 'error' { $icon = '❌' } + default { $icon = '✅' } } - $timestamp = Get-Date -Format "yyyyMMddHHmmss" - $prefix = "{0} {1} {2} " -f $timestamp, $tag.PadRight(11), $icon + $timestamp = Get-Date -Format 'yyyyMMddHHmmss' + $prefix = '{0} {1} {2} ' -f $timestamp, $tag.PadRight(11), $icon Write-PodeHost $prefix -NoNewLine $maxLineLength = [int]($Host.UI.RawUI.WindowSize.Width - $prefix.Length - 1) $currentPosition = 0 @@ -44,6 +44,7 @@ function Remove-UnsafeCharacter { return $inputString } +# Start-PodeServer -Name 'Podex' -ConfigFile '.\podex.psd1' -Threads 5 -ScriptBlock { Start-PodeServer -Name 'Podex' -Threads 5 -ScriptBlock { # get config @@ -78,13 +79,13 @@ Start-PodeServer -Name 'Podex' -Threads 5 -ScriptBlock { # file-based api routes (json or html) foreach ($file in (Get-ChildItem -Path './api' -Filter *.ps1 -Recurse -File)) { - $method = (Get-Culture).TextInfo.ToTitleCase($file.Name) -replace "\.ps1$", '' - $relativePath = $file.FullName -replace [regex]::Escape($PWD.Path + "\"), "" -replace "\\", "/" - $apiPath = "/" + ($relativePath -replace "\.ps1$", "") + $method = (Get-Culture).TextInfo.ToTitleCase($file.Name) -replace '\.ps1$', '' + $relativePath = $file.FullName -replace [regex]::Escape($PWD.Path + '\'), '' -replace '\\', '/' + $apiPath = '/' + ($relativePath -replace '\.ps1$', '') if ($method -in @('Get', 'Post', 'Put', 'Delete')) { - $apiPath = $apiPath -replace "/$($method)", "" - } elseif ($cfg.Podex.Debug -and $relativePath -match "/debug/") { - $apiPath = $apiPath -replace "/debug", "" + $apiPath = $apiPath -replace "/$($method)", '' + } elseif ($cfg.Podex.Debug -and $relativePath -match '/debug/') { + $apiPath = $apiPath -replace '/debug', '' $method = 'Get' } else { $method = 'Get' diff --git a/public/css/tailwind.css b/public/css/tailwind.css index 083db0b..8597816 100644 --- a/public/css/tailwind.css +++ b/public/css/tailwind.css @@ -1,6 +1,38 @@ -@import 'tailwindcss/base'; -@import 'tailwindcss/components'; -@import 'tailwindcss/utilities'; +@import 'tailwindcss'; + +@custom-variant dark (&:is(.dark *)); + +@theme { + --color-primary-50: #ecfdf5; + --color-primary-100: #d1fae5; + --color-primary-200: #a7f3d0; + --color-primary-300: #6ee7b7; + --color-primary-400: #34d399; + --color-primary-500: #10b981; + --color-primary-600: #059669; + --color-primary-700: #047857; + --color-primary-800: #065f46; + --color-primary-900: #064e3b; + --color-primary-950: #022c22; +} + +/* + The default border color has changed to `currentColor` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. +*/ +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } +} @layer base { :root { diff --git a/public/js/htmx.js b/public/js/htmx.js index f949da0..370cc0f 100644 --- a/public/js/htmx.js +++ b/public/js/htmx.js @@ -277,7 +277,7 @@ var htmx = (function() { parseInterval: null, /** @type {typeof internalEval} */ _: null, - version: '2.0.3' + version: '2.0.4' } // Tsc madness part 2 htmx.onLoad = onLoadHelper @@ -693,6 +693,7 @@ var htmx = (function() { * @property {XMLHttpRequest} [xhr] * @property {(() => void)[]} [queuedRequests] * @property {boolean} [abortable] + * @property {boolean} [firstInitCompleted] * * Event data * @property {HtmxTriggerSpecification} [triggerSpec] @@ -754,17 +755,14 @@ var htmx = (function() { } /** + * Checks whether the element is in the document (includes shadow roots). + * This function this is a slight misnomer; it will return true even for elements in the head. + * * @param {Node} elt * @returns {boolean} */ function bodyContains(elt) { - // IE Fix - const rootNode = elt.getRootNode && elt.getRootNode() - if (rootNode && rootNode instanceof window.ShadowRoot) { - return getDocument().body.contains(rootNode.host) - } else { - return getDocument().body.contains(elt) - } + return elt.getRootNode({ composed: true }) === document } /** @@ -1128,34 +1126,77 @@ var htmx = (function() { * @returns {(Node|Window)[]} */ function querySelectorAllExt(elt, selector, global) { - elt = resolveTarget(elt) - if (selector.indexOf('closest ') === 0) { - return [closest(asElement(elt), normalizeSelector(selector.substr(8)))] - } else if (selector.indexOf('find ') === 0) { - return [find(asParentNode(elt), normalizeSelector(selector.substr(5)))] - } else if (selector === 'next') { - return [asElement(elt).nextElementSibling] - } else if (selector.indexOf('next ') === 0) { - return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)), !!global)] - } else if (selector === 'previous') { - return [asElement(elt).previousElementSibling] - } else if (selector.indexOf('previous ') === 0) { - return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)), !!global)] - } else if (selector === 'document') { - return [document] - } else if (selector === 'window') { - return [window] - } else if (selector === 'body') { - return [document.body] - } else if (selector === 'root') { - return [getRootNode(elt, !!global)] - } else if (selector === 'host') { - return [(/** @type ShadowRoot */(elt.getRootNode())).host] - } else if (selector.indexOf('global ') === 0) { + if (selector.indexOf('global ') === 0) { return querySelectorAllExt(elt, selector.slice(7), true) - } else { - return toArray(asParentNode(getRootNode(elt, !!global)).querySelectorAll(normalizeSelector(selector))) } + + elt = resolveTarget(elt) + + const parts = [] + { + let chevronsCount = 0 + let offset = 0 + for (let i = 0; i < selector.length; i++) { + const char = selector[i] + if (char === ',' && chevronsCount === 0) { + parts.push(selector.substring(offset, i)) + offset = i + 1 + continue + } + if (char === '<') { + chevronsCount++ + } else if (char === '/' && i < selector.length - 1 && selector[i + 1] === '>') { + chevronsCount-- + } + } + if (offset < selector.length) { + parts.push(selector.substring(offset)) + } + } + + const result = [] + const unprocessedParts = [] + while (parts.length > 0) { + const selector = normalizeSelector(parts.shift()) + let item + if (selector.indexOf('closest ') === 0) { + item = closest(asElement(elt), normalizeSelector(selector.substr(8))) + } else if (selector.indexOf('find ') === 0) { + item = find(asParentNode(elt), normalizeSelector(selector.substr(5))) + } else if (selector === 'next' || selector === 'nextElementSibling') { + item = asElement(elt).nextElementSibling + } else if (selector.indexOf('next ') === 0) { + item = scanForwardQuery(elt, normalizeSelector(selector.substr(5)), !!global) + } else if (selector === 'previous' || selector === 'previousElementSibling') { + item = asElement(elt).previousElementSibling + } else if (selector.indexOf('previous ') === 0) { + item = scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)), !!global) + } else if (selector === 'document') { + item = document + } else if (selector === 'window') { + item = window + } else if (selector === 'body') { + item = document.body + } else if (selector === 'root') { + item = getRootNode(elt, !!global) + } else if (selector === 'host') { + item = (/** @type ShadowRoot */(elt.getRootNode())).host + } else { + unprocessedParts.push(selector) + } + + if (item) { + result.push(item) + } + } + + if (unprocessedParts.length > 0) { + const standardSelector = unprocessedParts.join(',') + const rootNode = asParentNode(getRootNode(elt, !!global)) + result.push(...toArray(rootNode.querySelectorAll(standardSelector))) + } + + return result } /** @@ -1418,8 +1459,8 @@ var htmx = (function() { if (oobValue === 'true') { // do nothing } else if (oobValue.indexOf(':') > 0) { - swapStyle = oobValue.substr(0, oobValue.indexOf(':')) - selector = oobValue.substr(oobValue.indexOf(':') + 1, oobValue.length) + swapStyle = oobValue.substring(0, oobValue.indexOf(':')) + selector = oobValue.substring(oobValue.indexOf(':') + 1) } else { swapStyle = oobValue } @@ -1628,7 +1669,7 @@ var htmx = (function() { }) } deInitOnHandlers(element) - forEach(Object.keys(internalData), function(key) { delete internalData[key] }) + forEach(Object.keys(internalData), function(key) { if (key !== 'firstInitCompleted') delete internalData[key] }) } /** @@ -1889,7 +1930,7 @@ var htmx = (function() { // oob swaps findAndSwapOobElements(fragment, settleInfo, rootNode) forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) { - if (findAndSwapOobElements(template.content, settleInfo, rootNode)) { + if (template.content && findAndSwapOobElements(template.content, settleInfo, rootNode)) { // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap template.remove() } @@ -2028,7 +2069,7 @@ var htmx = (function() { while (SYMBOL_CONT.exec(str.charAt(position + 1))) { position++ } - tokens.push(str.substr(startPosition, position - startPosition + 1)) + tokens.push(str.substring(startPosition, position + 1)) } else if (STRINGISH_START.indexOf(str.charAt(position)) !== -1) { const startChar = str.charAt(position) var startPosition = position @@ -2039,7 +2080,7 @@ var htmx = (function() { } position++ } - tokens.push(str.substr(startPosition, position - startPosition + 1)) + tokens.push(str.substring(startPosition, position + 1)) } else { const symbol = str.charAt(position) tokens.push(symbol) @@ -2323,6 +2364,11 @@ var htmx = (function() { const rawAttribute = getRawAttribute(elt, 'method') verb = (/** @type HttpVerb */(rawAttribute ? rawAttribute.toLowerCase() : 'get')) path = getRawAttribute(elt, 'action') + if (path == null || path === '') { + // if there is no action attribute on the form set path to current href before the + // following logic to properly clear parameters on a GET (not on a POST!) + path = getDocument().location.href + } if (verb === 'get' && path.includes('?')) { path = path.replace(/\?[^#]+/, '') } @@ -2354,7 +2400,8 @@ var htmx = (function() { if (elt.tagName === 'FORM') { return true } - if (matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) { + if (matches(elt, 'input[type="submit"], button') && + (matches(elt, '[form]') || closest(elt, 'form') !== null)) { return true } if (elt instanceof HTMLAnchorElement && elt.href && @@ -2559,6 +2606,7 @@ var htmx = (function() { const load = function() { if (!nodeData.loaded) { nodeData.loaded = true + triggerEvent(elt, 'htmx:trigger') handler(elt) } } @@ -2634,7 +2682,7 @@ var htmx = (function() { }, observerOptions) observer.observe(asElement(elt)) addEventListener(asElement(elt), handler, nodeData, triggerSpec) - } else if (triggerSpec.trigger === 'load') { + } else if (!nodeData.firstInitCompleted && triggerSpec.trigger === 'load') { if (!maybeFilterEvent(triggerSpec, elt, makeEvent('load', { elt }))) { loadImmediately(asElement(elt), handler, nodeData, triggerSpec.delay) } @@ -2841,11 +2889,12 @@ var htmx = (function() { return } const nodeData = getInternalData(elt) - if (nodeData.initHash !== attributeHash(elt)) { + const attrHash = attributeHash(elt) + if (nodeData.initHash !== attrHash) { // clean up any previously processed info deInitNode(elt) - nodeData.initHash = attributeHash(elt) + nodeData.initHash = attrHash triggerEvent(elt, 'htmx:beforeProcessNode') @@ -2870,6 +2919,7 @@ var htmx = (function() { initButtonTracking(elt) } + nodeData.firstInitCompleted = true triggerEvent(elt, 'htmx:afterProcessNode') } } @@ -3581,7 +3631,7 @@ var htmx = (function() { } else if (paramsValue === '*') { return inputValues } else if (paramsValue.indexOf('not ') === 0) { - forEach(paramsValue.substr(4).split(','), function(name) { + forEach(paramsValue.slice(4).split(','), function(name) { name = name.trim() inputValues.delete(name) }) @@ -3631,15 +3681,15 @@ var htmx = (function() { for (let i = 0; i < split.length; i++) { const value = split[i] if (value.indexOf('swap:') === 0) { - swapSpec.swapDelay = parseInterval(value.substr(5)) + swapSpec.swapDelay = parseInterval(value.slice(5)) } else if (value.indexOf('settle:') === 0) { - swapSpec.settleDelay = parseInterval(value.substr(7)) + swapSpec.settleDelay = parseInterval(value.slice(7)) } else if (value.indexOf('transition:') === 0) { - swapSpec.transition = value.substr(11) === 'true' + swapSpec.transition = value.slice(11) === 'true' } else if (value.indexOf('ignoreTitle:') === 0) { - swapSpec.ignoreTitle = value.substr(12) === 'true' + swapSpec.ignoreTitle = value.slice(12) === 'true' } else if (value.indexOf('scroll:') === 0) { - const scrollSpec = value.substr(7) + const scrollSpec = value.slice(7) var splitSpec = scrollSpec.split(':') const scrollVal = splitSpec.pop() var selectorVal = splitSpec.length > 0 ? splitSpec.join(':') : null @@ -3647,14 +3697,14 @@ var htmx = (function() { swapSpec.scroll = scrollVal swapSpec.scrollTarget = selectorVal } else if (value.indexOf('show:') === 0) { - const showSpec = value.substr(5) + const showSpec = value.slice(5) var splitSpec = showSpec.split(':') const showVal = splitSpec.pop() var selectorVal = splitSpec.length > 0 ? splitSpec.join(':') : null swapSpec.show = showVal swapSpec.showTarget = selectorVal } else if (value.indexOf('focus-scroll:') === 0) { - const focusScrollVal = value.substr('focus-scroll:'.length) + const focusScrollVal = value.slice('focus-scroll:'.length) swapSpec.focusScroll = focusScrollVal == 'true' } else if (i == 0) { swapSpec.swapStyle = value @@ -3776,10 +3826,10 @@ var htmx = (function() { return null } if (str.indexOf('javascript:') === 0) { - str = str.substr(11) + str = str.slice(11) evaluateValue = true } else if (str.indexOf('js:') === 0) { - str = str.substr(3) + str = str.slice(3) evaluateValue = true } if (str.indexOf('{') !== 0) { @@ -3905,9 +3955,9 @@ var htmx = (function() { }) } else { let resolvedTarget = resolveTarget(context.target) - // If target is supplied but can't resolve OR both target and source can't be resolved + // If target is supplied but can't resolve OR source is supplied but both target and source can't be resolved // then use DUMMY_ELT to abort the request with htmx:targetError to avoid it replacing body by mistake - if ((context.target && !resolvedTarget) || (!resolvedTarget && !resolveTarget(context.source))) { + if ((context.target && !resolvedTarget) || (context.source && !resolvedTarget && !resolveTarget(context.source))) { resolvedTarget = DUMMY_ELT } return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event, @@ -4039,7 +4089,15 @@ var htmx = (function() { get: function(target, name) { if (typeof name === 'symbol') { // Forward symbol calls to the FormData itself directly - return Reflect.get(target, name) + const result = Reflect.get(target, name) + // Wrap in function with apply to correctly bind the FormData context, as a direct call would result in an illegal invocation error + if (typeof result === 'function') { + return function() { + return result.apply(formData, arguments) + } + } else { + return result + } } if (name === 'toJSON') { // Support JSON.stringify call on proxy @@ -4873,7 +4931,7 @@ var htmx = (function() { * @see https://htmx.org/api/#defineExtension * * @param {string} name the extension name - * @param {HtmxExtension} extension the extension definition + * @param {Partial} extension the extension definition */ function defineExtension(name, extension) { if (extension.init) { diff --git a/tailwind.config.mjs b/tailwind.config.mjs deleted file mode 100644 index df662c6..0000000 --- a/tailwind.config.mjs +++ /dev/null @@ -1,27 +0,0 @@ -/** @type {import('tailwindcss').Config} */ - -export default { - darkMode: 'class', - content: ['./views/**/*.*', './errors/**/*.*'], - theme: { - extend: { - colors: { - // tailwind emerald - primary: { - 50: '#ecfdf5', - 100: '#d1fae5', - 200: '#a7f3d0', - 300: '#6ee7b7', - 400: '#34d399', - 500: '#10b981', - 600: '#059669', - 700: '#047857', - 800: '#065f46', - 900: '#064e3b', - 950: '#022c22', - }, - }, - }, - }, - plugins: [], -}; diff --git a/views/components/about.pode b/views/components/about.pode index 8ab453d..69e2e43 100644 --- a/views/components/about.pode +++ b/views/components/about.pode @@ -1,9 +1,9 @@ -
-
+
+

Podex - PowerShell/Pode + htmx Framework for Building Web Applications

-
-
+
+

Podex - PowerShell/Pode + htmx Framework for Building Web Applications

Table of Contents

    diff --git a/views/components/crudmgr-new.pode b/views/components/crudmgr-new.pode index 096b184..fc9bc81 100644 --- a/views/components/crudmgr-new.pode +++ b/views/components/crudmgr-new.pode @@ -1,4 +1,4 @@ -
    +

    Add New Item

    diff --git a/views/components/crudmgr.pode b/views/components/crudmgr.pode index 95e371b..b278a20 100644 --- a/views/components/crudmgr.pode +++ b/views/components/crudmgr.pode @@ -1,12 +1,12 @@ -
    -
    +
    +

    Item Management

    -
    +
    -
    +
    -