create-evikit 2.2.3 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-evikit.js +6 -17
- package/index.css +22 -1
- package/index.html +1 -0
- package/index.js +1 -0
- package/package.json +2 -2
- package/po/create-evikit.pot +15 -7
- package/po/uk.po +19 -9
- package/src/app.js +2 -0
- package/src/routes/api/server.js +22 -4
- package/src/routes/api/types.js +6 -1
- package/src/routes/error.js +16 -0
- package/src/routes/page.js +128 -63
- package/static/favicon.svg +15 -0
- package/static/preact-ssr-framework.png +0 -0
- package/static/preact-ssr-framework.xcf +0 -0
package/bin/create-evikit.js
CHANGED
|
@@ -23,10 +23,12 @@ for (const file of [...template, "eslint.config.js", "tsconfig.json"]) {
|
|
|
23
23
|
mkdirSync(dirname(path), { recursive: true });
|
|
24
24
|
writeFileSync(
|
|
25
25
|
path,
|
|
26
|
-
readFileSync(join(ownRoot, file), "utf8")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
readFileSync(join(ownRoot, file), "utf8")
|
|
27
|
+
.replace(
|
|
28
|
+
/<link\s+href="[^"]+"(\s+media="[^"]+")?\s+rel="[a-z]+"\s+\/>\s+/g,
|
|
29
|
+
"",
|
|
30
|
+
)
|
|
31
|
+
.replace(', "uk"', ""),
|
|
30
32
|
);
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -47,19 +49,6 @@ package-lock.json
|
|
|
47
49
|
|
|
48
50
|
// ---
|
|
49
51
|
|
|
50
|
-
writeFileSync(
|
|
51
|
-
join(target, "src/lib/index.js"),
|
|
52
|
-
`
|
|
53
|
-
import acceptLanguage from "accept-language";
|
|
54
|
-
|
|
55
|
-
export function acceptLanguages() {
|
|
56
|
-
acceptLanguage.languages(["en"]);
|
|
57
|
-
}
|
|
58
|
-
`.trimStart(),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// ---
|
|
62
|
-
|
|
63
52
|
writeFileSync(
|
|
64
53
|
join(target, "src/routes/api/types.js"),
|
|
65
54
|
`
|
package/index.css
CHANGED
|
@@ -5,8 +5,19 @@
|
|
|
5
5
|
text-wrap: balance;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
.badge-info.badge-soft,
|
|
8
9
|
p a:not([class]) {
|
|
9
10
|
color: #28f;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@media (prefers-color-scheme: dark) {
|
|
14
|
+
.badge-info.badge-soft,
|
|
15
|
+
p a:not([class]) {
|
|
16
|
+
color: #61afef;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
p a:not([class]) {
|
|
10
21
|
text-decoration: underline;
|
|
11
22
|
}
|
|
12
23
|
|
|
@@ -32,7 +43,7 @@ details {
|
|
|
32
43
|
nav a {
|
|
33
44
|
background-color: currentColor;
|
|
34
45
|
color: #fff;
|
|
35
|
-
border-radius:
|
|
46
|
+
border-radius: 0.75rem;
|
|
36
47
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
|
|
37
48
|
display: inline-block;
|
|
38
49
|
margin-bottom: 1em;
|
|
@@ -53,3 +64,13 @@ nav a[href*="issues"] {
|
|
|
53
64
|
background-color: #7d8f;
|
|
54
65
|
color: #fff;
|
|
55
66
|
}
|
|
67
|
+
|
|
68
|
+
@media (prefers-color-scheme: dark) {
|
|
69
|
+
nav a[href*="documentation"] {
|
|
70
|
+
background-color: #c678dd;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
nav a[href*="issues"] {
|
|
74
|
+
background-color: #61afef;
|
|
75
|
+
}
|
|
76
|
+
}
|
package/index.html
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
|
+
<link href="/static/favicon.svg" rel="icon" />
|
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
7
|
<meta name="color-scheme" content="dark light" />
|
|
7
8
|
<link href="/index.css" rel="stylesheet" />
|
package/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { api } from "./src/api.js";
|
|
|
7
7
|
import { prerender } from "./src/app.js";
|
|
8
8
|
|
|
9
9
|
const app = express();
|
|
10
|
+
app.use("/static", express.static("./static"));
|
|
10
11
|
app.use(api().onRequest);
|
|
11
12
|
app.use("/openapi", swaggerUi.serve, swaggerUi.setup(api().openapi));
|
|
12
13
|
app.use("/", express.static("./dist", { index: false, redirect: false }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-evikit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "bin/create-evikit.js",
|
|
6
6
|
"scripts": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@speed-highlight/core": "^1.2.17",
|
|
38
38
|
"accept-language": "^3.0.20",
|
|
39
39
|
"classnames": "^2.5.1",
|
|
40
|
-
"evikit": "^2.
|
|
40
|
+
"evikit": "^2.3.2",
|
|
41
41
|
"express": "^5.2.1",
|
|
42
42
|
"file-type": "^22.0.1",
|
|
43
43
|
"hoofd": "^1.7.3",
|
package/po/create-evikit.pot
CHANGED
|
@@ -8,7 +8,7 @@ msgid ""
|
|
|
8
8
|
msgstr ""
|
|
9
9
|
"Project-Id-Version: create-evikit\n"
|
|
10
10
|
"Report-Msgid-Bugs-To: \n"
|
|
11
|
-
"POT-Creation-Date: 2026-07-
|
|
11
|
+
"POT-Creation-Date: 2026-07-08 12:51+0300\n"
|
|
12
12
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
13
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
14
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
@@ -21,12 +21,6 @@ msgstr ""
|
|
|
21
21
|
msgid "EviKit: Preact SSR framework for small web apps"
|
|
22
22
|
msgstr ""
|
|
23
23
|
|
|
24
|
-
msgid "Preact SSR framework."
|
|
25
|
-
msgstr ""
|
|
26
|
-
|
|
27
|
-
msgid "For small web apps hosted on a <b>VPS</b> or in local network."
|
|
28
|
-
msgstr ""
|
|
29
|
-
|
|
30
24
|
msgid ""
|
|
31
25
|
"Standard modern <b>JavaScript</b>, no JSX or TS. <b>Forms</b> accessible "
|
|
32
26
|
"when JS doesn't load. Automatic <b>Swagger</b> specification. First-class "
|
|
@@ -34,6 +28,12 @@ msgid ""
|
|
|
34
28
|
"for UI <b>translation</b> through .po files."
|
|
35
29
|
msgstr ""
|
|
36
30
|
|
|
31
|
+
msgid "Preact SSR framework."
|
|
32
|
+
msgstr ""
|
|
33
|
+
|
|
34
|
+
msgid "For small web apps hosted on a <b>VPS</b> or in local network."
|
|
35
|
+
msgstr ""
|
|
36
|
+
|
|
37
37
|
msgid "Documentation"
|
|
38
38
|
msgstr ""
|
|
39
39
|
|
|
@@ -45,6 +45,11 @@ msgid ""
|
|
|
45
45
|
"are deleted every 10 minutes."
|
|
46
46
|
msgstr ""
|
|
47
47
|
|
|
48
|
+
msgid ""
|
|
49
|
+
"<a href=\"{openapi}\" target=\"_top\">OpenAPI demo</a> (Swagger) is "
|
|
50
|
+
"generated from API route schema."
|
|
51
|
+
msgstr ""
|
|
52
|
+
|
|
48
53
|
msgid "Quick start"
|
|
49
54
|
msgstr ""
|
|
50
55
|
|
|
@@ -55,6 +60,9 @@ msgid ""
|
|
|
55
60
|
"using all the primary features of the EviKit framework."
|
|
56
61
|
msgstr ""
|
|
57
62
|
|
|
63
|
+
msgid "Favicon by <a href=\"{artist}\">Solar Icons</a>"
|
|
64
|
+
msgstr ""
|
|
65
|
+
|
|
58
66
|
msgid ""
|
|
59
67
|
"<a href=\"{evikit}\"><b>Source code</b> at Codeberg</a> under <b>MIT</b> "
|
|
60
68
|
"license"
|
package/po/uk.po
CHANGED
|
@@ -7,8 +7,8 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: create-evikit\n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date: 2026-07-
|
|
11
|
-
"PO-Revision-Date: 2026-07-
|
|
10
|
+
"POT-Creation-Date: 2026-07-08 12:51+0300\n"
|
|
11
|
+
"PO-Revision-Date: 2026-07-08 03:11+0300\n"
|
|
12
12
|
"Last-Translator: EMAIL@ADDRESS\n"
|
|
13
13
|
"Language-Team: Ukrainian\n"
|
|
14
14
|
"Language: uk\n"
|
|
@@ -22,13 +22,6 @@ msgstr ""
|
|
|
22
22
|
msgid "EviKit: Preact SSR framework for small web apps"
|
|
23
23
|
msgstr "EviKit: Preact SSR-фреймворк для невеликих вебзастосунків"
|
|
24
24
|
|
|
25
|
-
msgid "Preact SSR framework."
|
|
26
|
-
msgstr "Preact SSR-фреймворк."
|
|
27
|
-
|
|
28
|
-
msgid "For small web apps hosted on a <b>VPS</b> or in local network."
|
|
29
|
-
msgstr ""
|
|
30
|
-
"Для невеликих вебзастосунків, розміщених на <b>VPS</b> чи в локальній мережі."
|
|
31
|
-
|
|
32
25
|
msgid ""
|
|
33
26
|
"Standard modern <b>JavaScript</b>, no JSX or TS. <b>Forms</b> accessible "
|
|
34
27
|
"when JS doesn't load. Automatic <b>Swagger</b> specification. First-class "
|
|
@@ -41,6 +34,13 @@ msgstr ""
|
|
|
41
34
|
"коробки. Вбудована <b>ORM</b> на основі <b>node:sqlite</b>. Підтримується "
|
|
42
35
|
"<b>переклад</b> інтерфейсу через .po-файли."
|
|
43
36
|
|
|
37
|
+
msgid "Preact SSR framework."
|
|
38
|
+
msgstr "Preact SSR-фреймворк."
|
|
39
|
+
|
|
40
|
+
msgid "For small web apps hosted on a <b>VPS</b> or in local network."
|
|
41
|
+
msgstr ""
|
|
42
|
+
"Для невеликих вебзастосунків, розміщених на <b>VPS</b> чи в локальній мережі."
|
|
43
|
+
|
|
44
44
|
msgid "Documentation"
|
|
45
45
|
msgstr "Документація"
|
|
46
46
|
|
|
@@ -54,6 +54,13 @@ msgstr ""
|
|
|
54
54
|
"Перегляньте <a href=\"{todo}\">живе демо</a> коду зразків, наведених унизу. "
|
|
55
55
|
"Завдання видаляються кожні 10 хвилин."
|
|
56
56
|
|
|
57
|
+
msgid ""
|
|
58
|
+
"<a href=\"{openapi}\" target=\"_top\">OpenAPI demo</a> (Swagger) is "
|
|
59
|
+
"generated from API route schema."
|
|
60
|
+
msgstr ""
|
|
61
|
+
"<a href=\"{openapi}\" target=\"_top\">Демо OpenAPI</a> (Swagger) генерується "
|
|
62
|
+
"зі схеми API-шляхів."
|
|
63
|
+
|
|
57
64
|
msgid "Quick start"
|
|
58
65
|
msgstr "Швидкий початок"
|
|
59
66
|
|
|
@@ -68,6 +75,9 @@ msgstr ""
|
|
|
68
75
|
"покрокові інструкції з <b><a href=\"{documentation}\">написання застосунка</"
|
|
69
76
|
"a></b>, що використовує всі основні функції фреймворка EviKit."
|
|
70
77
|
|
|
78
|
+
msgid "Favicon by <a href=\"{artist}\">Solar Icons</a>"
|
|
79
|
+
msgstr "Піктограма за авторством <a href=\"{artist}\">Solar Icons</a>"
|
|
80
|
+
|
|
71
81
|
msgid ""
|
|
72
82
|
"<a href=\"{evikit}\"><b>Source code</b> at Codeberg</a> under <b>MIT</b> "
|
|
73
83
|
"license"
|
package/src/app.js
CHANGED
|
@@ -3,6 +3,7 @@ import { h } from "preact";
|
|
|
3
3
|
import { Route } from "preact-iso";
|
|
4
4
|
|
|
5
5
|
import { acceptLanguages } from "./lib/index.js";
|
|
6
|
+
import { ErrorPage } from "./routes/error.js";
|
|
6
7
|
import { HomePage } from "./routes/page.js";
|
|
7
8
|
import { TodoPage } from "./routes/todo/page.js";
|
|
8
9
|
|
|
@@ -18,6 +19,7 @@ export function App(props) {
|
|
|
18
19
|
props,
|
|
19
20
|
h(Route, { component: HomePage, path: "/" }),
|
|
20
21
|
h(Route, { component: TodoPage, path: "/todo" }),
|
|
22
|
+
h(Route, { component: ErrorPage, default: true }),
|
|
21
23
|
);
|
|
22
24
|
}
|
|
23
25
|
|
package/src/routes/api/server.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { endpoint, json } from "evikit/server";
|
|
2
|
+
import { ok } from "node:assert/strict";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { object, parse, record, string } from "valibot";
|
|
3
5
|
|
|
4
6
|
import { HomeGet } from "./types.js";
|
|
5
7
|
|
|
@@ -8,6 +10,8 @@ export const template = [
|
|
|
8
10
|
"src/routes/api/todo/types.js",
|
|
9
11
|
"src/routes/api/todo/server.js",
|
|
10
12
|
"src/routes/todo/page.js",
|
|
13
|
+
"src/routes/error.js",
|
|
14
|
+
"src/lib/index.js",
|
|
11
15
|
"src/api.js",
|
|
12
16
|
"src/app.js",
|
|
13
17
|
"index.js",
|
|
@@ -17,13 +21,27 @@ export const template = [
|
|
|
17
21
|
];
|
|
18
22
|
|
|
19
23
|
export const HomeServer = {
|
|
20
|
-
get: endpoint(HomeGet, async () =>
|
|
21
|
-
|
|
24
|
+
get: endpoint(HomeGet, async () => {
|
|
25
|
+
const latest = Object.entries(
|
|
26
|
+
parse(
|
|
27
|
+
object({ time: record(string(), string()) }),
|
|
28
|
+
await (await fetch("https://registry.npmjs.org/evikit")).json(),
|
|
29
|
+
).time,
|
|
30
|
+
)
|
|
31
|
+
.filter(
|
|
32
|
+
([, publishedAt]) =>
|
|
33
|
+
// Give me 30 minutes for hotfix.
|
|
34
|
+
new Date(publishedAt).getTime() < Date.now() - 30 * 60e3,
|
|
35
|
+
)
|
|
36
|
+
.at(-1);
|
|
37
|
+
ok(latest);
|
|
38
|
+
return json({
|
|
22
39
|
File: Object.fromEntries(
|
|
23
40
|
await Promise.all(
|
|
24
41
|
template.map(async (path) => [path, await readFile(path, "utf8")]),
|
|
25
42
|
),
|
|
26
43
|
),
|
|
27
|
-
|
|
28
|
-
|
|
44
|
+
npm: { publishedAt: latest[1], version: latest[0] },
|
|
45
|
+
});
|
|
46
|
+
}),
|
|
29
47
|
};
|
package/src/routes/api/types.js
CHANGED
|
@@ -2,5 +2,10 @@ import { object, record, string } from "valibot";
|
|
|
2
2
|
|
|
3
3
|
export const HomeGet = {
|
|
4
4
|
input: object({ lang: string() }),
|
|
5
|
-
outputs: {
|
|
5
|
+
outputs: {
|
|
6
|
+
200: object({
|
|
7
|
+
File: record(string(), string()),
|
|
8
|
+
npm: object({ publishedAt: string(), version: string() }),
|
|
9
|
+
}),
|
|
10
|
+
},
|
|
6
11
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { a, h1, main, p, useStatus } from "evikit";
|
|
2
|
+
import { useMeta, useTitle } from "hoofd/preact";
|
|
3
|
+
|
|
4
|
+
/** @param {{ children?: import("preact").ComponentChildren, error?: unknown }} props */
|
|
5
|
+
export function ErrorPage({ children, error }) {
|
|
6
|
+
const status = useStatus(error);
|
|
7
|
+
useMeta({ content: "noindex", name: "robots" });
|
|
8
|
+
useTitle(status);
|
|
9
|
+
|
|
10
|
+
return main(
|
|
11
|
+
{ class: "m-8" },
|
|
12
|
+
p({ class: "mb-4" }, a({ class: "link", href: "/" }, "EviKit")),
|
|
13
|
+
h1({ class: "text-2xl" }, status),
|
|
14
|
+
children,
|
|
15
|
+
);
|
|
16
|
+
}
|
package/src/routes/page.js
CHANGED
|
@@ -1,31 +1,79 @@
|
|
|
1
1
|
import { highlightAll } from "@speed-highlight/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
a,
|
|
4
|
+
b,
|
|
5
|
+
blockquote,
|
|
6
|
+
br,
|
|
7
|
+
code,
|
|
8
|
+
details,
|
|
9
|
+
div,
|
|
10
|
+
figcaption,
|
|
11
|
+
figure,
|
|
12
|
+
footer,
|
|
13
|
+
h1,
|
|
14
|
+
hr,
|
|
15
|
+
main,
|
|
16
|
+
nav,
|
|
17
|
+
p,
|
|
18
|
+
pre,
|
|
19
|
+
span,
|
|
20
|
+
summary,
|
|
21
|
+
useApi,
|
|
22
|
+
useI18n,
|
|
23
|
+
} from "evikit";
|
|
3
24
|
import { useMeta, useTitle } from "hoofd/preact";
|
|
4
25
|
import { h } from "preact";
|
|
5
26
|
import { useEffect } from "preact/hooks";
|
|
6
27
|
|
|
7
28
|
import { HomeGet } from "./api/types.js";
|
|
8
29
|
|
|
30
|
+
const darkBg = "dark:bg-[#161b22]";
|
|
31
|
+
const blockquoteStyle = `bg-gray-50 ${darkBg} border border-gray-100 dark:border-black mx-auto p-4 rounded-xl md:text-center`;
|
|
32
|
+
|
|
9
33
|
export function HomePage() {
|
|
10
34
|
const i18n = useI18n();
|
|
11
35
|
const { data } = useApi(HomeGet);
|
|
12
36
|
useTitle(i18n.gettext("EviKit: Preact SSR framework for small web apps"));
|
|
37
|
+
const description = i18n.gettext(
|
|
38
|
+
"Standard modern <b>JavaScript</b>, no JSX or TS. <b>Forms</b> accessible when JS doesn't load. Automatic <b>Swagger</b> specification. First-class API input/output <b>validation</b>. Built-in <b>node:sqlite ORM</b>. Support for UI <b>translation</b> through .po files.",
|
|
39
|
+
);
|
|
13
40
|
useMeta({
|
|
14
|
-
content:
|
|
15
|
-
"Standard modern JavaScript, no JSX or TS. Forms accessible when JS doesn't load. Automatic Swagger specification. First-class API input/output validation. Built-in node:sqlite ORM. Support for UI translation through .po files.",
|
|
41
|
+
content: description.replace(/<[^>]+>/g, ""),
|
|
16
42
|
name: "description",
|
|
17
43
|
});
|
|
44
|
+
useMeta({ content: "/static/preact-ssr-framework.png", name: "og:image" });
|
|
45
|
+
useMeta({ content: "summary_large_image", name: "twitter:card" });
|
|
18
46
|
useEffect(() => {
|
|
19
47
|
highlightAll();
|
|
20
48
|
}, [JSON.stringify(data?.File)]);
|
|
21
49
|
|
|
22
50
|
return main(
|
|
23
51
|
{ class: "max-w-5xl mx-auto p-8" },
|
|
52
|
+
p(
|
|
53
|
+
{ class: "mb-4 sm:text-center" },
|
|
54
|
+
a(
|
|
55
|
+
{
|
|
56
|
+
class: "badge badge-info badge-soft",
|
|
57
|
+
href: "https://npmx.dev/package/evikit",
|
|
58
|
+
},
|
|
59
|
+
"npm i evikit",
|
|
60
|
+
data
|
|
61
|
+
? [
|
|
62
|
+
"@^" + data.npm.version + " ",
|
|
63
|
+
span(
|
|
64
|
+
{ class: "opacity-50" },
|
|
65
|
+
`# ${data.npm.publishedAt.replace(/T.*/, "")}`,
|
|
66
|
+
),
|
|
67
|
+
]
|
|
68
|
+
: undefined,
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
|
|
24
72
|
h1(
|
|
25
|
-
{ class: "mb-4 text-xl lg:text-3xl
|
|
73
|
+
{ class: "mb-4 text-xl lg:text-3xl sm:text-center" },
|
|
26
74
|
"EviKit: ",
|
|
27
75
|
b(null, i18n.gettext("Preact SSR framework.")),
|
|
28
|
-
|
|
76
|
+
br(null),
|
|
29
77
|
span({
|
|
30
78
|
class: "mt-0 text-base lg:text-sm",
|
|
31
79
|
...sanitize(
|
|
@@ -35,18 +83,9 @@ export function HomePage() {
|
|
|
35
83
|
),
|
|
36
84
|
}),
|
|
37
85
|
),
|
|
86
|
+
p({ class: "lg:text-center", ...sanitize(description) }),
|
|
38
87
|
|
|
39
|
-
|
|
40
|
-
class: "lg:text-center",
|
|
41
|
-
...sanitize(
|
|
42
|
-
i18n.gettext(
|
|
43
|
-
"Standard modern <b>JavaScript</b>, no JSX or TS. <b>Forms</b> accessible when JS doesn't load. Automatic <b>Swagger</b> specification. First-class API input/output <b>validation</b>. Built-in <b>node:sqlite ORM</b>. Support for UI <b>translation</b> through .po files.",
|
|
44
|
-
),
|
|
45
|
-
),
|
|
46
|
-
}),
|
|
47
|
-
|
|
48
|
-
h(
|
|
49
|
-
"nav",
|
|
88
|
+
nav(
|
|
50
89
|
{ class: "gap-4 grid grid-cols-2 max-w-lg mx-auto mt-6 text-center" },
|
|
51
90
|
|
|
52
91
|
a(
|
|
@@ -61,32 +100,39 @@ export function HomePage() {
|
|
|
61
100
|
}),
|
|
62
101
|
),
|
|
63
102
|
|
|
64
|
-
p(
|
|
65
|
-
class: "mb-6 lg:text-center",
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
103
|
+
p(
|
|
104
|
+
{ class: "mb-6 lg:text-center", id: "demo" },
|
|
105
|
+
span({
|
|
106
|
+
...sanitize(
|
|
107
|
+
i18n
|
|
108
|
+
.gettext(
|
|
109
|
+
'See <a href="{todo}">live demo</a> of the example code from below. Todos are deleted every 10 minutes.',
|
|
110
|
+
)
|
|
111
|
+
.replace("{todo}", "/todo"),
|
|
112
|
+
),
|
|
113
|
+
}),
|
|
114
|
+
" ",
|
|
115
|
+
span({
|
|
116
|
+
...sanitize(
|
|
117
|
+
i18n
|
|
118
|
+
.gettext(
|
|
119
|
+
'<a href="{openapi}" target="_top">OpenAPI demo</a> (Swagger) is generated from API route schema.',
|
|
120
|
+
)
|
|
121
|
+
.replace(
|
|
122
|
+
"{openapi}",
|
|
123
|
+
/localhost/.test(location.href) ? "/openapi.json" : "/openapi",
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
class: "lg:block",
|
|
127
|
+
}),
|
|
128
|
+
),
|
|
74
129
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
class: "max-w-xs mx-auto mb-8 p-4 rounded-lg text-center",
|
|
79
|
-
style: {
|
|
80
|
-
background: "rgba(0, 0, 0, 0.025)",
|
|
81
|
-
border: "1px solid rgba(0, 0, 0, 0.05)",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
130
|
+
blockquote(
|
|
131
|
+
{ class: `${blockquoteStyle} max-w-sm mb-8` },
|
|
84
132
|
p(null, b(null, i18n.gettext("Quick start"))),
|
|
85
|
-
|
|
86
|
-
"pre",
|
|
133
|
+
pre(
|
|
87
134
|
null,
|
|
88
|
-
|
|
89
|
-
"code",
|
|
135
|
+
code(
|
|
90
136
|
{ class: "!bg-transparent shj-lang-bash" },
|
|
91
137
|
`
|
|
92
138
|
npm init evikit my-app
|
|
@@ -127,6 +173,20 @@ npm run dev
|
|
|
127
173
|
b(null, "todo/page.js"),
|
|
128
174
|
),
|
|
129
175
|
|
|
176
|
+
h(
|
|
177
|
+
Code,
|
|
178
|
+
{ file: data?.File["src/routes/error.js"], lang: "js" },
|
|
179
|
+
span(null, "src/routes/"),
|
|
180
|
+
b(null, "error.js"),
|
|
181
|
+
),
|
|
182
|
+
|
|
183
|
+
h(
|
|
184
|
+
Code,
|
|
185
|
+
{ file: data?.File["src/lib/index.js"], lang: "js" },
|
|
186
|
+
span(null, "src/lib/"),
|
|
187
|
+
b(null, "index.js"),
|
|
188
|
+
),
|
|
189
|
+
|
|
130
190
|
h(
|
|
131
191
|
Code,
|
|
132
192
|
{ file: data?.File["src/api.js"], lang: "js", tag: "details" },
|
|
@@ -167,16 +227,10 @@ npm run dev
|
|
|
167
227
|
),
|
|
168
228
|
),
|
|
169
229
|
|
|
170
|
-
|
|
230
|
+
hr({ class: "opacity-0" }),
|
|
171
231
|
h(
|
|
172
232
|
"blockquote",
|
|
173
|
-
{
|
|
174
|
-
class: "max-w-2xl mx-auto my-8 p-4 rounded-lg text-center",
|
|
175
|
-
style: {
|
|
176
|
-
background: "rgba(0, 0, 0, 0.025)",
|
|
177
|
-
border: "1px solid rgba(0, 0, 0, 0.05)",
|
|
178
|
-
},
|
|
179
|
-
},
|
|
233
|
+
{ class: `${blockquoteStyle} max-w-2xl my-8` },
|
|
180
234
|
p({
|
|
181
235
|
...sanitize(
|
|
182
236
|
i18n
|
|
@@ -191,15 +245,25 @@ npm run dev
|
|
|
191
245
|
}),
|
|
192
246
|
),
|
|
193
247
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"
|
|
197
|
-
{ class: "text-center" },
|
|
248
|
+
hr({ class: "opacity-0" }),
|
|
249
|
+
footer(
|
|
250
|
+
{ class: "md:text-center" },
|
|
198
251
|
p(
|
|
199
252
|
{ class: "mb-4" },
|
|
200
253
|
"Copyright © 2026 ",
|
|
201
254
|
a({ href: "https://libre.net.ua" }, "Denys Nykula"),
|
|
202
255
|
),
|
|
256
|
+
p({
|
|
257
|
+
class: "mb-4",
|
|
258
|
+
...sanitize(
|
|
259
|
+
i18n
|
|
260
|
+
.gettext('Favicon by <a href="{artist}">Solar Icons</a>')
|
|
261
|
+
.replace(
|
|
262
|
+
"{artist}",
|
|
263
|
+
"https://www.svgrepo.com/author/Solar%20Icons/",
|
|
264
|
+
),
|
|
265
|
+
),
|
|
266
|
+
}),
|
|
203
267
|
p({
|
|
204
268
|
class: "mb-4",
|
|
205
269
|
...sanitize(
|
|
@@ -223,27 +287,27 @@ npm run dev
|
|
|
223
287
|
|
|
224
288
|
/** @param {{ children?: any, lang: string, file: string | undefined, tag?: string }} props */
|
|
225
289
|
function Code({ children, file, lang, tag }) {
|
|
226
|
-
const inner =
|
|
227
|
-
"pre",
|
|
290
|
+
const inner = pre(
|
|
228
291
|
{ class: "mt-4 overflow-auto text-sm" },
|
|
229
|
-
|
|
230
|
-
"code",
|
|
292
|
+
code(
|
|
231
293
|
{ class: `w-full shj-lang-${lang}` },
|
|
232
294
|
file
|
|
233
295
|
?.replace(/.*EviKit.*\r?\n/g, "")
|
|
234
|
-
.replace(/\{[
|
|
235
|
-
|
|
236
|
-
|
|
296
|
+
.replace(/\{[0-9a-z,\s]*?\} from/gi, (match) =>
|
|
297
|
+
match.replace(/\s+/g, " "),
|
|
298
|
+
)
|
|
299
|
+
.replace(/.*HomeServer.*\r?\n/g, "")
|
|
300
|
+
.replace(/.*HomePage.*\r?\n/g, "")
|
|
237
301
|
.replace(
|
|
238
|
-
/<link\s+href="
|
|
302
|
+
/<link\s+href="[^"]+"(\s+media="[^"]+")?\s+rel="[a-z]+"\s+\/>\s+/g,
|
|
239
303
|
"",
|
|
240
304
|
),
|
|
241
305
|
),
|
|
242
306
|
);
|
|
243
|
-
const style =
|
|
307
|
+
const style = `${darkBg} mb-4 mx-0 overflow-hidden p-4 rounded-xl`;
|
|
244
308
|
return tag == "details"
|
|
245
|
-
?
|
|
246
|
-
: figure({ class: style },
|
|
309
|
+
? details({ class: style }, summary(null, children), inner)
|
|
310
|
+
: figure({ class: style }, figcaption(null, children), inner);
|
|
247
311
|
}
|
|
248
312
|
|
|
249
313
|
/** @param {string} html */
|
|
@@ -251,7 +315,8 @@ function sanitize(html) {
|
|
|
251
315
|
return {
|
|
252
316
|
dangerouslySetInnerHTML: {
|
|
253
317
|
__html: html.replace(/<([^>]*)>/g, (match) =>
|
|
254
|
-
/^<\/?[a-z]+>$/.test(match) ||
|
|
318
|
+
/^<\/?[a-z]+>$/.test(match) ||
|
|
319
|
+
/^<a href="[0-9A-Za-z:/.#%]*"( target="_top")?>$/.test(match)
|
|
255
320
|
? match
|
|
256
321
|
: "",
|
|
257
322
|
),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<style>
|
|
4
|
+
#app { fill: #84f }
|
|
5
|
+
#api { fill: #7d8 }
|
|
6
|
+
@media (prefers-color-scheme: dark) {
|
|
7
|
+
#app { fill: #c678dd }
|
|
8
|
+
#api { fill: #61afef }
|
|
9
|
+
}
|
|
10
|
+
</style>
|
|
11
|
+
<rect fill="#fff" width="18" height="6" x="3" y="4" />
|
|
12
|
+
<rect fill="#fff" width="18" height="6" x="3" y="14" />
|
|
13
|
+
<path id="app" fill-rule="evenodd" clip-rule="evenodd" d="M2.58579 3.58579C2 4.17157 2 5.11438 2 7C2 8.88562 2 9.82843 2.58579 10.4142C3.17157 11 4.11438 11 6 11H18C19.8856 11 20.8284 11 21.4142 10.4142C22 9.82843 22 8.88562 22 7C22 5.11438 22 4.17157 21.4142 3.58579C20.8284 3 19.8856 3 18 3H6C4.11438 3 3.17157 3 2.58579 3.58579ZM9 8.75C8.58579 8.75 8.25 8.41421 8.25 8V6C8.25 5.58579 8.58579 5.25 9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V8C9.75 8.41421 9.41421 8.75 9 8.75ZM13.5 6.25C13.0858 6.25 12.75 6.58579 12.75 7C12.75 7.41421 13.0858 7.75 13.5 7.75H18C18.4142 7.75 18.75 7.41421 18.75 7C18.75 6.58579 18.4142 6.25 18 6.25H13.5ZM6 8.75C5.58579 8.75 5.25 8.41421 5.25 8L5.25 6C5.25 5.58579 5.58579 5.25 6 5.25C6.41421 5.25 6.75 5.58579 6.75 6V8C6.75 8.41421 6.41421 8.75 6 8.75Z"/>
|
|
14
|
+
<path id="api" fill-rule="evenodd" clip-rule="evenodd" d="M2.58579 13.5858C2 14.1716 2 15.1144 2 17C2 18.8856 2 19.8284 2.58579 20.4142C3.17157 21 4.11438 21 6 21H18C19.8856 21 20.8284 21 21.4142 20.4142C22 19.8284 22 18.8856 22 17C22 15.1144 22 14.1716 21.4142 13.5858C20.8284 13 19.8856 13 18 13H6C4.11438 13 3.17157 13 2.58579 13.5858ZM12.75 17C12.75 16.5858 13.0858 16.25 13.5 16.25H18C18.4142 16.25 18.75 16.5858 18.75 17C18.75 17.4142 18.4142 17.75 18 17.75H13.5C13.0858 17.75 12.75 17.4142 12.75 17ZM5.25 18C5.25 18.4142 5.58579 18.75 6 18.75C6.41421 18.75 6.75 18.4142 6.75 18V16C6.75 15.5858 6.41421 15.25 6 15.25C5.58579 15.25 5.25 15.5858 5.25 16L5.25 18ZM9 18.75C8.58579 18.75 8.25 18.4142 8.25 18V16C8.25 15.5858 8.58579 15.25 9 15.25C9.41421 15.25 9.75 15.5858 9.75 16V18C9.75 18.4142 9.41421 18.75 9 18.75Z"/>
|
|
15
|
+
</svg>
|
|
Binary file
|
|
Binary file
|