From af087c1bbf0f5352d2ac413ee18537e191b74336 Mon Sep 17 00:00:00 2001 From: Juri Leino Date: Sat, 2 Aug 2025 10:34:36 +0200 Subject: [PATCH 01/11] style: improve page layout and smallter tweaks - footer sticks to the bottom of the window - improve module info display - info icon size and alignment - module info boottom and right margin - module description foont size - improve function info display - remove duplicate function name --- src/main/frontend/sass/_doc.scss | 18 +++-- src/main/frontend/sass/_layout.scss | 2 +- src/main/xar-resources/browse.html | 75 ++++++++++---------- src/main/xar-resources/error-page.html | 8 ++- src/main/xar-resources/index.html | 96 +++++++++++++------------- src/main/xar-resources/modules/app.xql | 69 +++++++++--------- src/main/xar-resources/view.html | 14 ++-- 7 files changed, 143 insertions(+), 139 deletions(-) diff --git a/src/main/frontend/sass/_doc.scss b/src/main/frontend/sass/_doc.scss index 1bfadb4..278fee8 100644 --- a/src/main/frontend/sass/_doc.scss +++ b/src/main/frontend/sass/_doc.scss @@ -30,6 +30,7 @@ select[name='module'] { .module .module-head { background-color: var(--lightgrey); border-radius: .25rem; + padding-block-end: 1rem; } .module .module-head-inner { @@ -39,17 +40,16 @@ select[name='module'] { .module-info-icon { display: block; - text-align: center; + text-align: right; margin-top: 1em; } .module-info-icon .glyphicon-info-sign { - font-size: 3rem; + font-size: 2.5rem; } -.module-head p { - font-size: 1.33em; - line-height: 1.5em; +.module-description { + margin-inline-end: 1rem; } .module-head td { @@ -62,17 +62,15 @@ select[name='module'] { } .module-head h3 { - margin-block-start: 1rem; + margin-block-start: 1.25rem; } .module-head h4 { word-wrap: break-word; } -.function-head h4 { - font-size: 1.25em; - margin-block-end: .8rem; - margin-inline-start: 1rem; +.function-head { + margin-block-start: 2rem; } .module .extended { diff --git a/src/main/frontend/sass/_layout.scss b/src/main/frontend/sass/_layout.scss index de0924f..71eae5a 100644 --- a/src/main/frontend/sass/_layout.scss +++ b/src/main/frontend/sass/_layout.scss @@ -17,7 +17,7 @@ #main { margin-block-start: 1.75rem; - min-height: 40vh; + min-height: calc(100vh - 18.5rem); } .navbar-dark { diff --git a/src/main/xar-resources/browse.html b/src/main/xar-resources/browse.html index 3f2a509..3124d41 100644 --- a/src/main/xar-resources/browse.html +++ b/src/main/xar-resources/browse.html @@ -1,41 +1,44 @@
-

XQuery Function Documentation

-

- - Back to Search Page -

-
-
-
- -
-
- -
-
- +
+

XQuery Function Documentation

+

+ + Back to Search Page +

+ +
+
+ +
+
+ +
+
+ +
+ + +
+ + + + + + + + + + +
NamespaceLocation
- - -
- - - - - - - - - - -
NamespaceLocation
-
+
+
\ No newline at end of file diff --git a/src/main/xar-resources/error-page.html b/src/main/xar-resources/error-page.html index d218798..c92e728 100644 --- a/src/main/xar-resources/error-page.html +++ b/src/main/xar-resources/error-page.html @@ -1,6 +1,8 @@
-

An error has occurred

-

An error has been generated by the application.

-

+    
+

An error has occurred

+

An error has been generated by the application.

+

+    
\ No newline at end of file diff --git a/src/main/xar-resources/index.html b/src/main/xar-resources/index.html index 850c73d..9f0c683 100644 --- a/src/main/xar-resources/index.html +++ b/src/main/xar-resources/index.html @@ -1,56 +1,58 @@
-

XQuery Function Documentation

-
-
- -
- - - - +
+

XQuery Function Documentation

+ +
+ +
+ + + + + - +
-
-
- -
- -
-
-
-
- - Browse +
+ +
+ +
-
- - - +
+ +
+ + + +
+ +
+ The function documentation has not been generated yet. Click on the Generate button + above to index all XQuery modules. This should take less than a minute.
- -
- The function documentation has not been generated yet. Click on the Generate button - above to index all XQuery modules. This should take less than a minute. -
-
- The function documentation has not been generated yet. Please contact the database administrator in order to have the function documentation generated. - -
-
-
-
-
+
+ The function documentation has not been generated yet. Please contact the database administrator in order to have the function documentation generated. + +
+
+
+
+
+
-
+
\ No newline at end of file diff --git a/src/main/xar-resources/modules/app.xql b/src/main/xar-resources/modules/app.xql index 15c12c1..a4e74cf 100644 --- a/src/main/xar-resources/modules/app.xql +++ b/src/main/xar-resources/modules/app.xql @@ -115,41 +115,39 @@ function app:print-module( return
-
-
- -
-

{ $uri }

- { - if (empty($location)) then ( - ) else if (starts-with($location, '/db')) then ( -

{$location}

+
+ +
+

{ $uri }

+ { + if (empty($location)) then ( + ) else if (starts-with($location, '/db')) then ( +

{$location}

+ ) else ( +

{$location}

+ ) + } +

{ $parsed }

+ { + let $metadata := $module/xqdoc:module/xqdoc:comment/(xqdoc:author|xqdoc:version|xqdoc:since) + return + if (empty($metadata)) then ( ) else ( -

{$location}

+ + { + for $meta in $metadata + return + + + + + } +
{local-name($meta)}{$meta/string()}
) - } -

{ $parsed }

- { - let $metadata := $module/xqdoc:module/xqdoc:comment/(xqdoc:author|xqdoc:version|xqdoc:since) - return - if (empty($metadata)) then ( - ) else ( - - { - for $meta in $metadata - return - - - - - } -
{local-name($meta)}{$meta/string()}
- ) - } -
+ }
@@ -209,10 +207,9 @@ function app:print-function( let $extDocs := app:get-extended-doc($function)[1] return
-
-

{$function-name/node()}

+
{ $function/xqdoc:signature/node() }
-
+

{ $parsed }

{ diff --git a/src/main/xar-resources/view.html b/src/main/xar-resources/view.html index a038963..a15966d 100644 --- a/src/main/xar-resources/view.html +++ b/src/main/xar-resources/view.html @@ -1,9 +1,11 @@
-

XQuery Function Documentation

-

- - Search and Browse -

-
+
\ No newline at end of file From 8010e6240c6a498121570a9e61dcbe81bb350142 Mon Sep 17 00:00:00 2001 From: Juri Leino Date: Sat, 2 Aug 2025 10:48:19 +0200 Subject: [PATCH 02/11] feat: tighten controller security, error handling - rewrite scan.xql to generate.xqm - adhere to naming conventions - explicitly allow routes and deny evertying else - route modules/reindex.xql moved to /regenerate - add routes fro markdown and static resources - call to /regenerate is a GET request --- .../{controller.xql => controller.xq} | 55 +++--- .../modules/{app.xql => app.xqm} | 0 src/main/xar-resources/modules/generate.xqm | 160 ++++++++++++++++++ src/main/xar-resources/modules/regenerate.xq | 30 ++++ src/main/xar-resources/modules/reindex.xql | 18 -- src/main/xar-resources/modules/scan.xql | 155 ----------------- .../modules/{view.xql => view.xq} | 2 +- .../xar-resources/resources/scripts/query.js | 3 +- 8 files changed, 222 insertions(+), 201 deletions(-) rename src/main/xar-resources/{controller.xql => controller.xq} (70%) rename src/main/xar-resources/modules/{app.xql => app.xqm} (100%) create mode 100644 src/main/xar-resources/modules/generate.xqm create mode 100644 src/main/xar-resources/modules/regenerate.xq delete mode 100644 src/main/xar-resources/modules/reindex.xql delete mode 100644 src/main/xar-resources/modules/scan.xql rename src/main/xar-resources/modules/{view.xql => view.xq} (99%) diff --git a/src/main/xar-resources/controller.xql b/src/main/xar-resources/controller.xq similarity index 70% rename from src/main/xar-resources/controller.xql rename to src/main/xar-resources/controller.xq index 173cfd9..b470103 100644 --- a/src/main/xar-resources/controller.xql +++ b/src/main/xar-resources/controller.xq @@ -10,17 +10,36 @@ declare variable $exist:controller external; declare variable $exist:path external; declare variable $exist:resource external; -if ($exist:path eq '') then +if ($exist:path eq '') then ( -else if ($exist:path eq "/") then +) else if ($exist:path eq "/") then ( (: forward root path to index.xql :) - -else if ($exist:resource eq "login") then +) else if (ends-with($exist:resource, ".html")) then ( + let $loggedIn := login:set-user("org.exist.login", (), true()) + return + (: the html page is run through view.xql to expand templates :) + + + + + + + + +) else if (ends-with($exist:resource, ".md")) then ( + + + +) else if (matches($exist:path, "/resources/(css|fonts|images|scripts|svg|css)/.+")) then ( + + + +) else if ($exist:resource eq "login") then ( let $loggedIn := login:set-user("org.exist.login", (), true()) return try { @@ -33,27 +52,13 @@ else if ($exist:resource eq "login") then response:set-status-code(401), {$err:description} } - -else if (ends-with($exist:resource, ".html")) then - let $loggedIn := login:set-user("org.exist.login", (), true()) +) else if ($exist:path = "/regenerate") then ( + let $loggedIn := login:set-user("org.exist.login", (), false()) return - (: the html page is run through view.xql to expand templates :) - - - - - - + - -else if ($exist:resource = "reindex.xql") then - - {login:set-user("org.exist.login", (), false())} - - -else - (: everything else is passed through :) - - - \ No newline at end of file +) else ( + response:set-status-code(404), + Not Found +) diff --git a/src/main/xar-resources/modules/app.xql b/src/main/xar-resources/modules/app.xqm similarity index 100% rename from src/main/xar-resources/modules/app.xql rename to src/main/xar-resources/modules/app.xqm diff --git a/src/main/xar-resources/modules/generate.xqm b/src/main/xar-resources/modules/generate.xqm new file mode 100644 index 0000000..03468a0 --- /dev/null +++ b/src/main/xar-resources/modules/generate.xqm @@ -0,0 +1,160 @@ +xquery version "3.1"; + + +(:~ + : This module scans the database instance for library modules and + : generates XQDoc for each one that is found. + : This collection of XQDoc definitions is then used to render + : the function documentation. + :) +module namespace generate="http://exist-db.org/apps/fundocs/generate"; + + +import module namespace xmldb="http://exist-db.org/xquery/xmldb"; +import module namespace inspect="http://exist-db.org/xquery/inspection" at "java:org.exist.xquery.functions.inspect.InspectionModule"; +import module namespace config="http://exist-db.org/xquery/apps/config" at "config.xqm"; +import module namespace dbutil="http://exist-db.org/xquery/dbutil" at "dbutil.xqm"; + + +declare namespace xqdoc="http://www.xqdoc.org/1.0"; + + +declare variable $generate:doc-collection := $config:app-root || "/data"; + + +declare function generate:fundocs() { + ( + generate:load-mapped-modules(), + generate:load-internal-modules(), + generate:load-stored-modules() + ) + => filter(generate:has-definition#1) + => for-each(generate:generate-and-store-xqdoc#1) +}; + +declare %private function generate:load-mapped-modules() as array(*)* { + for $path in util:mapped-modules() + let $uri := xs:anyURI($path) + return generate:safe-inspect($uri, inspect:inspect-module-uri#1) +}; + +declare %private function generate:load-internal-modules() as array(*)* { + for $path in util:registered-modules() + let $uri := xs:anyURI($path) + return generate:safe-inspect($uri, inspect:inspect-module-uri#1) +}; + +declare %private function generate:load-stored-modules() as array(*)* { + for $uri in dbutil:find-by-mimetype(xs:anyURI("/db"), "application/xquery") + return generate:safe-inspect($uri, inspect:inspect-module#1) +}; + +declare %private function generate:safe-inspect ($moduleUri as xs:anyURI, $inspect as function(xs:anyURI) as element(module)?) as array(*)? { + try { + [$moduleUri, $inspect($moduleUri)] + } catch * { + (: + : Expected to fail if XQuery file is not a library module + : Will also guard against malformed and missing modules + :) + util:log("WARN", ( + "Could not compile function documentation for: ", + $moduleUri, " (", $err:code, ")", $err:description + )) + } +}; + +declare %private function generate:has-definition($module-info as array(*)) as xs:boolean { + exists($module-info?2) +}; + +declare %private function generate:generate-and-store-xqdoc ($module-info as array(*)) { + let $filename := concat(util:hash($module-info?1, "md5"), ".xml"), + $xqdoc := generate:module-to-xqdoc($module-info?2) + return + xmldb:store($generate:doc-collection, $filename, $xqdoc) + => xs:anyURI() + => sm:chmod("rw-rw-r--") +}; + +declare %private function generate:module-to-xqdoc($module as element(module)) as element(xqdoc:xqdoc) { + + + {current-dateTime()} + {$module/@location/string()} + + + {$module/@uri/string()} + {$module/@prefix/string()} + + {$module/description/string()} + { + if (empty($module/version)) then () + else + {$module/version/string()} + } + { + if (empty($module/author)) then () + else + {$module/author/string()} + } + + + + { + for $function in $module/function + return generate:function-to-xqdoc($function) + } + + +}; + +declare %private function generate:function-to-xqdoc($function as element(function)) as element(xqdoc:function) { + + {$function/@name/string()} + {generate:signature($function)} + + {$function/description/string()} + { + for $argument in $function/argument + return + { + "$" || $argument/@var || generate:cardinality($argument/@cardinality) || + " " || $argument/text() + } + } + + { + $function/returns/@type/string() || generate:cardinality($function/returns/@cardinality) + }{ + if (empty($function/returns/text())) then () + else " : " || $function/returns/text() + } + + { + if (empty($function/deprecated)) then () + else + {$function/deprecated/string()} + } + + +}; + +declare %private function generate:cardinality($cardinality as xs:string) as xs:string? { + switch ($cardinality) + case "zero or one" return "?" + case "zero or more" return "*" + case "one or more" return "+" + default return () +}; + +declare %private function generate:signature($function as element(function)) as xs:string { + let $arguments := + for $argument in $function/argument + return + "$" || $argument/@var || " as " || $argument/@type || generate:cardinality($argument/@cardinality) + + return + $function/@name/string() || "(" || string-join($arguments, ", ") || ")" || + " as " || $function/returns/@type || generate:cardinality($function/returns/@cardinality) +}; diff --git a/src/main/xar-resources/modules/regenerate.xq b/src/main/xar-resources/modules/regenerate.xq new file mode 100644 index 0000000..ab62aea --- /dev/null +++ b/src/main/xar-resources/modules/regenerate.xq @@ -0,0 +1,30 @@ +xquery version "3.1"; + +import module namespace sm="http://exist-db.org/xquery/securitymanager"; +import module namespace generate="http://exist-db.org/apps/fundocs/generate" at "generate.xqm"; + +declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization"; + +declare option output:method "json"; +declare option output:media-type "application/json"; + +try { + let $user := sm:id()/sm:id/(sm:effective|sm:real)[1]/sm:username + return if (sm:is-dba($user)) then ( + let $result := generate:fundocs() + return + + Scan completed! {$result} + + ) else ( + response:set-status-code(403), + + You have to be a member of the dba group. Please log in using the dashboard and retry. + + ) +} catch * { + response:set-status-code(500), + + {$err:description} + +} diff --git a/src/main/xar-resources/modules/reindex.xql b/src/main/xar-resources/modules/reindex.xql deleted file mode 100644 index 7a34a16..0000000 --- a/src/main/xar-resources/modules/reindex.xql +++ /dev/null @@ -1,18 +0,0 @@ -xquery version "3.1"; - -import module namespace docs="http://exist-db.org/xquery/docs" at "scan.xql"; -import module namespace config="http://exist-db.org/xquery/apps/config" at "config.xqm"; -import module namespace sm="http://exist-db.org/xquery/securitymanager"; - -declare option exist:serialize "method=json media-type=application/javascript"; - -let $isDba := sm:is-dba(sm:id()/sm:id/(sm:effective|sm:real)[1]/sm:username) -return - if ($isDba) then - - Scan completed! {docs:load-fundocs($config:app-root)} - - else - - You have to be a member of the dba group. Please log in using the dashboard and retry. - \ No newline at end of file diff --git a/src/main/xar-resources/modules/scan.xql b/src/main/xar-resources/modules/scan.xql deleted file mode 100644 index 4e9332b..0000000 --- a/src/main/xar-resources/modules/scan.xql +++ /dev/null @@ -1,155 +0,0 @@ -xquery version "3.1"; - -module namespace docs="http://exist-db.org/xquery/docs"; - -import module namespace xmldb="http://exist-db.org/xquery/xmldb"; -import module namespace dbutil="http://exist-db.org/xquery/dbutil" at "dbutil.xqm"; -import module namespace inspect="http://exist-db.org/xquery/inspection" at "java:org.exist.xquery.functions.inspect.InspectionModule"; - -declare namespace xqdoc="http://www.xqdoc.org/1.0"; - -declare %private function docs:create-collection($parent as xs:string, $child as xs:string) as empty-sequence() { - let $null := xmldb:create-collection($parent, $child) - return () -}; - -declare %private function docs:load-external($uri as xs:anyURI, $store as function(xs:string, element()) as empty-sequence()) { - let $meta := inspect:inspect-module-uri($uri) - return - if ($meta) then - let $xml := docs:generate-xqdoc($meta) - let $moduleURI := $xml//xqdoc:module/xqdoc:uri - return - $store($uri, $xml) - else - () -}; - -declare %private function docs:load-stored($path as xs:anyURI, $store as function(xs:string, element()) as empty-sequence()) { - let $meta := inspect:inspect-module($path) - return - if ($meta) then - let $xml := docs:generate-xqdoc($meta) - let $moduleURI := $xml//xqdoc:module/xqdoc:uri - return - $store($path, $xml) - else - () -}; - -declare %private function docs:load-external-modules($store as function(xs:string, element()) as empty-sequence()) { - for $uri in util:mapped-modules() - return - docs:load-external(xs:anyURI($uri), $store), - for $path in dbutil:find-by-mimetype(xs:anyURI("/db"), "application/xquery") - return - try { - docs:load-stored($path, $store) - } catch * { - (: Expected to fail if XQuery file is not a library module :) - () - } -}; - -declare %private function docs:load-internal-modules($store as function(xs:string, element()) as empty-sequence()) { - for $moduleURI in util:registered-modules() - let $meta := inspect:inspect-module-uri(xs:anyURI($moduleURI)) - return - if ($meta) then - let $xml := docs:generate-xqdoc($meta) - return - $store($moduleURI, $xml) - else - util:log("WARN", "Module not found: " || $moduleURI) -}; - -declare function docs:load-fundocs($target as xs:string) { - let $dataColl := xmldb:create-collection($target, "data") - let $store := function($moduleURI as xs:string, $data as element()) { - let $name := util:hash($moduleURI, "md5") || ".xml" - return - ( - xmldb:store($dataColl, $name, $data), - sm:chmod(xs:anyURI($dataColl || "/" || $name), "rw-rw-r--") - )[2] - } - return ( - docs:load-internal-modules($store), - docs:load-external-modules($store) - ) -}; - -declare function docs:generate-xqdoc($module as element(module)) { - - - {current-dateTime()} - {$module/@location/string()} - - - {$module/@uri/string()} - {$module/@prefix/string()} - - {$module/description/string()} - { - if ($module/version) then - {$module/version/string()} - else - () - } - { - if ($module/author) then - {$module/author/string()} - else - () - } - - - - { - for $func in $module/function - return - - {$func/@name/string()} - {docs:generate-signature($func)} - - {$func/description/string()} - { - for $param in $func/argument - return - ${$param/@var/string()}{docs:cardinality($param/@cardinality)}{" "}{$param/text()} - } - - {$func/returns/@type/string()}{docs:cardinality($func/returns/@cardinality)}{if(empty($func/returns/text())) then "" else " : " || $func/returns/text()} - - - { - if ($func/deprecated) then - {$func/deprecated/string()} - else - () - } - - - } - - -}; - -declare function docs:cardinality($cardinality as xs:string) { - switch ($cardinality) - case "zero or one" return "?" - case "zero or more" return "*" - case "one or more" return "+" - default return () -}; - -declare function docs:generate-signature($func as element(function)) { - $func/@name/string() || "(" || - string-join( - for $param in $func/argument - return - "$" || $param/@var/string() || " as " || $param/@type/string() || docs:cardinality($param/@cardinality), - ", " - ) || - ")" || " as " || $func/returns/@type/string() || docs:cardinality($func/returns/@cardinality) -}; \ No newline at end of file diff --git a/src/main/xar-resources/modules/view.xql b/src/main/xar-resources/modules/view.xq similarity index 99% rename from src/main/xar-resources/modules/view.xql rename to src/main/xar-resources/modules/view.xq index 9516dec..f1a8047 100644 --- a/src/main/xar-resources/modules/view.xql +++ b/src/main/xar-resources/modules/view.xq @@ -6,7 +6,7 @@ import module namespace lib="http://exist-db.org/xquery/html-templating/lib"; (: The following modules provide functions which will be called by the templating :) import module namespace config="http://exist-db.org/xquery/apps/config" at "config.xqm"; -import module namespace app="http://exist-db.org/xquery/app" at "app.xql"; +import module namespace app="http://exist-db.org/xquery/app" at "app.xqm"; declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization"; diff --git a/src/main/xar-resources/resources/scripts/query.js b/src/main/xar-resources/resources/scripts/query.js index 9bb5361..af45921 100644 --- a/src/main/xar-resources/resources/scripts/query.js +++ b/src/main/xar-resources/resources/scripts/query.js @@ -54,8 +54,7 @@ document.addEventListener("DOMContentLoaded", function () { if (messages) messages.innerHTML = ""; if (loadIndicator) loadIndicator.style.display = "block"; - fetch("modules/reindex.xql", { - method: "POST", + fetch("regenerate", { headers: { Accept: "application/json" }, }) .then((response) => response.json()) From ff5ee898b150be1fb1ecb623a36a92305f48c58e Mon Sep 17 00:00:00 2001 From: Juri Leino Date: Sat, 2 Aug 2025 11:27:19 +0200 Subject: [PATCH 03/11] test: adapt tests to changed app - use new route `/exist/apps/fundocs/generate` - do not test for removed headline in function info - test for changed module name --- .../cypress/integration/fundoc_spec.cy.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/cypress/integration/fundoc_spec.cy.js b/src/test/cypress/integration/fundoc_spec.cy.js index c83c4d5..b9dc477 100644 --- a/src/test/cypress/integration/fundoc_spec.cy.js +++ b/src/test/cypress/integration/fundoc_spec.cy.js @@ -3,16 +3,23 @@ context('Function Documentation', () => { before (() => { - // Creat Index before running test + // Generate function documentation before running tests + cy.timeout(10000); cy.request({ - url: 'http://127.0.0.1:8080/exist/rest/db/apps/fundocs/modules/reindex.xql', + url: 'http://127.0.0.1:8080/exist/apps/fundocs/regenerate', auth: { user: 'admin', password: '' } }) - .its('body') - .should('equal', '{ "status" : "ok", "message" : "Scan completed! " }') + .then((response) => { + expect(response).to.have.property('status') + expect(response.status).to.equal(200) + expect(response.body).to.have.property('status') + expect(response.body.status).to.equal('ok') + }) + // .its('body') + // .should('equal', "{ status: 'ok', message: 'Scan completed! ' }") }) beforeEach(() => { @@ -34,7 +41,7 @@ context('Function Documentation', () => { it('should find article with extended markdown contents and code highlighting', () => { cy.get('#query-field') .type('file:sync{enter}') - cy.get('.function-head > h4') + cy.get('.function-head') .should('exist') .click() cy.url() @@ -64,7 +71,7 @@ context('Function Documentation', () => { .click() // check module from fundocs itself cy.get('#modules') - .contains('http://exist-db.org/xquery/docs') + .contains('http://exist-db.org/apps/fundocs/generate') .click() cy.get('.module') .should('exist') From 05d22d695d0f51a369137c7fcdd99bdc4bb9312b Mon Sep 17 00:00:00 2001 From: Juri Leino Date: Sun, 3 Aug 2025 12:01:19 +0200 Subject: [PATCH 04/11] fix: links in topnavigation bar --- src/main/xar-resources/templates/page.html | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/xar-resources/templates/page.html b/src/main/xar-resources/templates/page.html index bab7463..4196a9c 100644 --- a/src/main/xar-resources/templates/page.html +++ b/src/main/xar-resources/templates/page.html @@ -1,4 +1,4 @@ - + App Title @@ -28,51 +28,51 @@ Home