@sealcode/sealgen 0.16.1-beta3 → 0.16.2
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/@types/src/page/stateful-page.d.ts +11 -11
- package/lib/builder.js +17 -4
- package/lib/get-fonts.js +11 -1
- package/lib/templates/stateful-page.js +3 -3
- package/package.json +1 -1
- package/src/builder.ts +16 -2
- package/src/forms/form.test.ts +2 -2
- package/src/get-fonts.ts +12 -1
- package/src/mount.ts +2 -2
- package/src/page/stateful-page.test.ts +4 -4
- package/src/page/stateful-page.ts +17 -13
- package/src/templates/stateful-page.ts +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="koa__router" />
|
|
2
2
|
import Router from "@koa/router";
|
|
3
|
-
import {
|
|
3
|
+
import { Context } from "koa";
|
|
4
4
|
import { Templatable } from "tempstream";
|
|
5
5
|
import { Mountable } from "./mountable.js";
|
|
6
6
|
export type StatefulPageActionDescription<ActionName> = {
|
|
@@ -21,25 +21,25 @@ export type StateAndMetadata<State, Actions> = {
|
|
|
21
21
|
action_args: null;
|
|
22
22
|
$: Record<string, unknown>;
|
|
23
23
|
};
|
|
24
|
-
export type StatefulPageActionArgument<State extends Record<string, unknown>, Args extends unknown[] = unknown[]> = {
|
|
25
|
-
ctx:
|
|
24
|
+
export type StatefulPageActionArgument<State extends Record<string, unknown>, Args extends unknown[] = unknown[], Page extends StatefulPage<Record<string, unknown>, any> = StatefulPage<any, any>> = {
|
|
25
|
+
ctx: Context;
|
|
26
26
|
state: State;
|
|
27
27
|
inputs: Record<string, string>;
|
|
28
28
|
args: Args;
|
|
29
|
-
page:
|
|
29
|
+
page: Page;
|
|
30
30
|
};
|
|
31
31
|
export type StatefulPageAction<State extends Record<string, unknown>, Args extends unknown[] = unknown[]> = (obj: StatefulPageActionArgument<State, Args>) => State | Promise<State>;
|
|
32
32
|
export type ExtractStatefulPageActionArgs<X> = X extends StatefulPageAction<any, infer Args> ? Args : never;
|
|
33
33
|
export declare abstract class StatefulPage<State extends Record<string, unknown>, Actions extends Record<string, StatefulPageAction<State>>> extends Mountable {
|
|
34
34
|
abstract actions: Actions;
|
|
35
|
-
abstract getInitialState(ctx:
|
|
36
|
-
abstract render(ctx:
|
|
35
|
+
abstract getInitialState(ctx: Context): State | Promise<State>;
|
|
36
|
+
abstract render(ctx: Context, state: State, inputs: Record<string, string>): Templatable | Promise<Templatable>;
|
|
37
37
|
canAccess(): Promise<{
|
|
38
38
|
readonly canAccess: true;
|
|
39
39
|
readonly message: "";
|
|
40
40
|
}>;
|
|
41
41
|
constructor();
|
|
42
|
-
abstract wrapInLayout(ctx:
|
|
42
|
+
abstract wrapInLayout(ctx: Context, content: Templatable, state: State): Templatable;
|
|
43
43
|
wrapInForm(context: Context, state: State, content: Templatable): Promise<Templatable>;
|
|
44
44
|
extractActionAndLabel<ActionName extends keyof Actions>(action_description: StatefulPageActionDescription<ActionName>): {
|
|
45
45
|
action: string;
|
|
@@ -55,9 +55,9 @@ export declare abstract class StatefulPage<State extends Record<string, unknown>
|
|
|
55
55
|
} | ActionName, ...args: ExtractStatefulPageActionArgs<Actions[ActionName]>): string;
|
|
56
56
|
rerender(): string;
|
|
57
57
|
preprocessState(values: State): Promise<State>;
|
|
58
|
-
preprocessOverrides(_context:
|
|
59
|
-
serializeState(_context:
|
|
60
|
-
deserializeState(_context:
|
|
61
|
-
extractState(ctx:
|
|
58
|
+
preprocessOverrides(_context: Context, _state: State, values: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
59
|
+
serializeState(_context: Context, state: State): Promise<string>;
|
|
60
|
+
deserializeState(_context: Context, s: string): Promise<State>;
|
|
61
|
+
extractState(ctx: Context): Promise<StateAndMetadata<State, Actions>>;
|
|
62
62
|
mount(router: Router, path: string): void;
|
|
63
63
|
}
|
package/lib/builder.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { build } from "esbuild";
|
|
2
2
|
import glob from "tiny-glob";
|
|
3
|
+
import { promises as fs } from "fs";
|
|
3
4
|
import { embeddable_file_extensions } from "./embeddable-file-extensions.js";
|
|
4
5
|
import { load_assets_plugin } from "./esbuild-plugins/load-assets.js";
|
|
5
6
|
import { rewrite_asset_imports_plugin } from "./esbuild-plugins/rewrite-asset-imports.js";
|
|
@@ -8,6 +9,7 @@ import { generateComponents } from "./generate-components.js";
|
|
|
8
9
|
import { generateRoutes } from "./generate-routes.js";
|
|
9
10
|
import { generateStimulusControllers } from "./generate-stimulus.js";
|
|
10
11
|
import { FONTS_CONFIG_PATH, getFonts } from "./get-fonts.js";
|
|
12
|
+
import { relative } from "node:path";
|
|
11
13
|
class Builder {
|
|
12
14
|
constructor(project_dir) {
|
|
13
15
|
this.project_dir = project_dir;
|
|
@@ -61,7 +63,7 @@ class BackendTSBuilder extends Builder {
|
|
|
61
63
|
","
|
|
62
64
|
)}}`
|
|
63
65
|
)).filter((path) => !path.includes(".#"));
|
|
64
|
-
await build({
|
|
66
|
+
const { metafile } = await build({
|
|
65
67
|
entryPoints,
|
|
66
68
|
sourcemap: true,
|
|
67
69
|
bundle: false,
|
|
@@ -75,8 +77,13 @@ class BackendTSBuilder extends Builder {
|
|
|
75
77
|
loader: Object.fromEntries(
|
|
76
78
|
embeddable_file_extensions.map((ext) => ["." + ext, "js"])
|
|
77
79
|
),
|
|
78
|
-
plugins: [rewrite_asset_imports_plugin, load_assets_plugin]
|
|
80
|
+
plugins: [rewrite_asset_imports_plugin, load_assets_plugin],
|
|
81
|
+
metafile: true
|
|
79
82
|
});
|
|
83
|
+
await fs.writeFile(
|
|
84
|
+
relative(this.project_dir, "dist") + "/back.meta.json",
|
|
85
|
+
JSON.stringify(metafile)
|
|
86
|
+
);
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
class FrontendTSBuilder extends Builder {
|
|
@@ -91,14 +98,20 @@ class FrontendTSBuilder extends Builder {
|
|
|
91
98
|
}
|
|
92
99
|
async _build() {
|
|
93
100
|
await generateStimulusControllers();
|
|
94
|
-
await build({
|
|
101
|
+
const { metafile } = await build({
|
|
95
102
|
entryPoints: ["./src/front/index.ts"],
|
|
96
103
|
sourcemap: true,
|
|
97
104
|
outfile: "./public/dist/bundle.js",
|
|
98
105
|
logLevel: "info",
|
|
99
106
|
bundle: true,
|
|
100
|
-
minify: true
|
|
107
|
+
minify: true,
|
|
108
|
+
metafile: true,
|
|
109
|
+
treeShaking: true
|
|
101
110
|
});
|
|
111
|
+
await fs.writeFile(
|
|
112
|
+
relative(this.project_dir, "dist") + "/front.meta.json",
|
|
113
|
+
JSON.stringify(metafile)
|
|
114
|
+
);
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
117
|
export {
|
package/lib/get-fonts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
1
|
+
import { readFile, writeFile } from "fs/promises";
|
|
2
2
|
import { constructURL, download } from "google-fonts-helper";
|
|
3
|
+
import { resolve } from "node:path";
|
|
3
4
|
import { target_locreq } from "./target-locreq.js";
|
|
4
5
|
const FONTS_CONFIG_PATH = "src/fonts.json";
|
|
5
6
|
async function getFonts(_ = {}) {
|
|
@@ -18,6 +19,15 @@ async function getFonts(_ = {}) {
|
|
|
18
19
|
fontsPath: "/dist/fonts"
|
|
19
20
|
});
|
|
20
21
|
await downloader.execute();
|
|
22
|
+
const fonts_css_file_path = resolve(
|
|
23
|
+
target_locreq.resolve("public/dist/fonts"),
|
|
24
|
+
"fonts.css"
|
|
25
|
+
);
|
|
26
|
+
const content = await readFile(fonts_css_file_path, "utf-8");
|
|
27
|
+
await writeFile(
|
|
28
|
+
fonts_css_file_path,
|
|
29
|
+
content.replace(/@font-face {/g, "@font-face {\nfont-display: swap;")
|
|
30
|
+
);
|
|
21
31
|
console.log("Downloaded new fonts pack");
|
|
22
32
|
}
|
|
23
33
|
export {
|
|
@@ -2,7 +2,7 @@ import { curryImportPath } from "../utils/import-path.js";
|
|
|
2
2
|
async function statefulPageTemplate(action_name, newfilefullpath) {
|
|
3
3
|
const rel = curryImportPath(newfilefullpath);
|
|
4
4
|
return `import { TempstreamJSX, Templatable } from "tempstream";
|
|
5
|
-
import {
|
|
5
|
+
import { Context } from "koa";
|
|
6
6
|
import { StatefulPage } from "@sealcode/sealgen";
|
|
7
7
|
import html from "${rel("src/back/html.js")}";
|
|
8
8
|
|
|
@@ -34,11 +34,11 @@ export default new (class ${action_name}Page extends StatefulPage<State, typeof
|
|
|
34
34
|
return { elements: ["one", "two", "three"] };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
wrapInLayout(ctx:
|
|
37
|
+
wrapInLayout(ctx: Context, content: Templatable): Templatable {
|
|
38
38
|
return html(ctx, "${action_name}", content);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
render(ctx:
|
|
41
|
+
render(ctx: Context, state: State, inputs: Record<string, string>) {
|
|
42
42
|
return (
|
|
43
43
|
<div>
|
|
44
44
|
<ul>
|
package/package.json
CHANGED
package/src/builder.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { build } from "esbuild";
|
|
2
2
|
import glob from "tiny-glob";
|
|
3
|
+
import { promises as fs } from "fs";
|
|
3
4
|
import { embeddable_file_extensions } from "./embeddable-file-extensions.js";
|
|
4
5
|
import { load_assets_plugin } from "./esbuild-plugins/load-assets.js";
|
|
5
6
|
import { rewrite_asset_imports_plugin } from "./esbuild-plugins/rewrite-asset-imports.js";
|
|
@@ -8,6 +9,7 @@ import { generateComponents } from "./generate-components.js";
|
|
|
8
9
|
import { generateRoutes } from "./generate-routes.js";
|
|
9
10
|
import { generateStimulusControllers } from "./generate-stimulus.js";
|
|
10
11
|
import { FONTS_CONFIG_PATH, getFonts } from "./get-fonts.js";
|
|
12
|
+
import { relative } from "node:path";
|
|
11
13
|
|
|
12
14
|
export abstract class Builder {
|
|
13
15
|
abstract ownsFile(file_path: string): boolean;
|
|
@@ -84,7 +86,7 @@ export class BackendTSBuilder extends Builder {
|
|
|
84
86
|
)}}`
|
|
85
87
|
)
|
|
86
88
|
).filter((path) => !path.includes(".#"));
|
|
87
|
-
await build({
|
|
89
|
+
const { metafile } = await build({
|
|
88
90
|
entryPoints,
|
|
89
91
|
sourcemap: true,
|
|
90
92
|
bundle: false,
|
|
@@ -99,7 +101,13 @@ export class BackendTSBuilder extends Builder {
|
|
|
99
101
|
embeddable_file_extensions.map((ext) => ["." + ext, "js"])
|
|
100
102
|
),
|
|
101
103
|
plugins: [rewrite_asset_imports_plugin, load_assets_plugin],
|
|
104
|
+
metafile: true,
|
|
102
105
|
});
|
|
106
|
+
|
|
107
|
+
await fs.writeFile(
|
|
108
|
+
relative(this.project_dir, "dist") + "/" + "back" + ".meta.json",
|
|
109
|
+
JSON.stringify(metafile)
|
|
110
|
+
);
|
|
103
111
|
}
|
|
104
112
|
}
|
|
105
113
|
|
|
@@ -120,13 +128,19 @@ export class FrontendTSBuilder extends Builder {
|
|
|
120
128
|
|
|
121
129
|
async _build(): Promise<void> {
|
|
122
130
|
await generateStimulusControllers();
|
|
123
|
-
await build({
|
|
131
|
+
const { metafile } = await build({
|
|
124
132
|
entryPoints: ["./src/front/index.ts"],
|
|
125
133
|
sourcemap: true,
|
|
126
134
|
outfile: "./public/dist/bundle.js",
|
|
127
135
|
logLevel: "info",
|
|
128
136
|
bundle: true,
|
|
129
137
|
minify: true,
|
|
138
|
+
metafile: true,
|
|
139
|
+
treeShaking: true,
|
|
130
140
|
});
|
|
141
|
+
await fs.writeFile(
|
|
142
|
+
relative(this.project_dir, "dist") + "/" + "front" + ".meta.json",
|
|
143
|
+
JSON.stringify(metafile)
|
|
144
|
+
);
|
|
131
145
|
}
|
|
132
146
|
}
|
package/src/forms/form.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Router from "@koa/router";
|
|
2
2
|
import axios from "axios";
|
|
3
|
-
import Koa, {
|
|
3
|
+
import Koa, { Context } from "koa";
|
|
4
4
|
import { Page } from "playwright";
|
|
5
5
|
import { Controls, Fields, Form, Mountable } from "../index.js";
|
|
6
6
|
import { mount } from "../mount.js";
|
|
@@ -205,7 +205,7 @@ describe("form test", () => {
|
|
|
205
205
|
const app = new Koa();
|
|
206
206
|
const router = new Router();
|
|
207
207
|
|
|
208
|
-
router.use(async (ctx:
|
|
208
|
+
router.use(async (ctx: Context, next: any) => {
|
|
209
209
|
ctx.$context = {
|
|
210
210
|
user_id: "miguel",
|
|
211
211
|
} as any;
|
package/src/get-fonts.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
1
|
+
import { readFile, writeFile } from "fs/promises";
|
|
2
2
|
import { constructURL, download } from "google-fonts-helper";
|
|
3
|
+
import { resolve } from "node:path";
|
|
3
4
|
import { target_locreq } from "./target-locreq.js";
|
|
4
5
|
|
|
5
6
|
export const FONTS_CONFIG_PATH = "src/fonts.json";
|
|
@@ -23,5 +24,15 @@ export async function getFonts(_: Record<string, string | boolean> = {}) {
|
|
|
23
24
|
fontsPath: "/dist/fonts",
|
|
24
25
|
});
|
|
25
26
|
await downloader.execute();
|
|
27
|
+
const fonts_css_file_path = resolve(
|
|
28
|
+
target_locreq.resolve("public/dist/fonts"),
|
|
29
|
+
"fonts.css"
|
|
30
|
+
);
|
|
31
|
+
const content = await readFile(fonts_css_file_path, "utf-8");
|
|
32
|
+
await writeFile(
|
|
33
|
+
fonts_css_file_path,
|
|
34
|
+
content.replace(/@font-face {/g, "@font-face {\nfont-display: swap;")
|
|
35
|
+
);
|
|
36
|
+
|
|
26
37
|
console.log("Downloaded new fonts pack");
|
|
27
38
|
}
|
package/src/mount.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import Router from "@koa/router";
|
|
3
3
|
import { FileManager } from "@sealcode/file-manager";
|
|
4
|
-
import Koa, {
|
|
4
|
+
import Koa, { Context } from "koa";
|
|
5
5
|
import { Middlewares as SealiousMiddlewares } from "sealious";
|
|
6
6
|
import { Mountable } from "./page/mountable.js";
|
|
7
7
|
|
|
@@ -26,7 +26,7 @@ export function mount(
|
|
|
26
26
|
const raw_url = typeof url === "string" ? url : url.rawURL;
|
|
27
27
|
const args = use_dummy_app
|
|
28
28
|
? [
|
|
29
|
-
async (ctx:
|
|
29
|
+
async (ctx: Context, next: any) => {
|
|
30
30
|
// this dummy is a temporary solution it's ilustrating the ways that
|
|
31
31
|
// sealgen is coupled with sealious. Definitely room for improvement in this
|
|
32
32
|
// regard
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { Templatable, tempstream } from "tempstream";
|
|
3
3
|
import {
|
|
4
4
|
ExtractStatefulPageActionArgs,
|
|
@@ -22,15 +22,15 @@ describe("stateful page", () => {
|
|
|
22
22
|
new (class extends StatefulPage<TestState, typeof actions> {
|
|
23
23
|
actions = actions;
|
|
24
24
|
|
|
25
|
-
getInitialState(_ctx:
|
|
25
|
+
getInitialState(_ctx: Context) {
|
|
26
26
|
return {};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
wrapInLayout(_ctx:
|
|
29
|
+
wrapInLayout(_ctx: Context, content: Templatable, _state: TestState): Templatable {
|
|
30
30
|
return content;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
render(_ctx:
|
|
33
|
+
render(_ctx: Context, state: TestState) {
|
|
34
34
|
return tempstream`<div>${this.makeActionButton(state, "some_action", 2)}</div>`;
|
|
35
35
|
}
|
|
36
36
|
})();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Router from "@koa/router";
|
|
3
3
|
import { predicates, hasShape, is } from "@sealcode/ts-predicates";
|
|
4
4
|
import deepmerge, { ArrayMergeOptions } from "deepmerge";
|
|
5
|
-
import {
|
|
5
|
+
import { Context } from "koa";
|
|
6
6
|
import { Templatable, tempstream } from "tempstream";
|
|
7
7
|
import { from_base64, to_base64 } from "../utils/base64.js";
|
|
8
8
|
import { Mountable } from "./mountable.js";
|
|
@@ -34,13 +34,17 @@ export type StateAndMetadata<State, Actions> =
|
|
|
34
34
|
|
|
35
35
|
export type StatefulPageActionArgument<
|
|
36
36
|
State extends Record<string, unknown>,
|
|
37
|
-
Args extends unknown[] = unknown[]
|
|
37
|
+
Args extends unknown[] = unknown[],
|
|
38
|
+
Page extends StatefulPage<Record<string, unknown>, any> = StatefulPage<
|
|
39
|
+
any,
|
|
40
|
+
any
|
|
41
|
+
>
|
|
38
42
|
> = {
|
|
39
|
-
ctx:
|
|
43
|
+
ctx: Context;
|
|
40
44
|
state: State;
|
|
41
45
|
inputs: Record<string, string>;
|
|
42
46
|
args: Args;
|
|
43
|
-
page:
|
|
47
|
+
page: Page;
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
export type StatefulPageAction<
|
|
@@ -60,9 +64,9 @@ export abstract class StatefulPage<
|
|
|
60
64
|
Actions extends Record<string, StatefulPageAction<State>>
|
|
61
65
|
> extends Mountable {
|
|
62
66
|
abstract actions: Actions;
|
|
63
|
-
abstract getInitialState(ctx:
|
|
67
|
+
abstract getInitialState(ctx: Context): State | Promise<State>;
|
|
64
68
|
abstract render(
|
|
65
|
-
ctx:
|
|
69
|
+
ctx: Context,
|
|
66
70
|
state: State,
|
|
67
71
|
inputs: Record<string, string>
|
|
68
72
|
): Templatable | Promise<Templatable>;
|
|
@@ -75,14 +79,14 @@ export abstract class StatefulPage<
|
|
|
75
79
|
super();
|
|
76
80
|
const original_render = this.render.bind(this) as typeof this.render;
|
|
77
81
|
this.render = async (
|
|
78
|
-
ctx:
|
|
82
|
+
ctx: Context,
|
|
79
83
|
state: State,
|
|
80
84
|
inputs: Record<string, string>
|
|
81
85
|
) => {
|
|
82
86
|
return this.wrapInLayout(
|
|
83
87
|
ctx,
|
|
84
88
|
await this.wrapInForm(
|
|
85
|
-
ctx
|
|
89
|
+
ctx,
|
|
86
90
|
state,
|
|
87
91
|
await original_render(ctx, state, inputs)
|
|
88
92
|
),
|
|
@@ -92,7 +96,7 @@ export abstract class StatefulPage<
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
abstract wrapInLayout(
|
|
95
|
-
ctx:
|
|
99
|
+
ctx: Context,
|
|
96
100
|
content: Templatable,
|
|
97
101
|
state: State
|
|
98
102
|
): Templatable;
|
|
@@ -189,24 +193,24 @@ export abstract class StatefulPage<
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
async preprocessOverrides(
|
|
192
|
-
_context:
|
|
196
|
+
_context: Context,
|
|
193
197
|
_state: State,
|
|
194
198
|
values: Record<string, unknown>
|
|
195
199
|
): Promise<Record<string, unknown>> {
|
|
196
200
|
return values;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
async serializeState(_context:
|
|
203
|
+
async serializeState(_context: Context, state: State): Promise<string> {
|
|
200
204
|
return JSON.stringify(state);
|
|
201
205
|
}
|
|
202
206
|
|
|
203
|
-
async deserializeState(_context:
|
|
207
|
+
async deserializeState(_context: Context, s: string): Promise<State> {
|
|
204
208
|
const deserialized = JSON.parse(s) as unknown;
|
|
205
209
|
return deserialized as State;
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
async extractState(
|
|
209
|
-
ctx:
|
|
213
|
+
ctx: Context
|
|
210
214
|
): Promise<StateAndMetadata<State, Actions>> {
|
|
211
215
|
if (
|
|
212
216
|
!hasShape(
|
|
@@ -6,7 +6,7 @@ export async function statefulPageTemplate(
|
|
|
6
6
|
): Promise<string> {
|
|
7
7
|
const rel = curryImportPath(newfilefullpath);
|
|
8
8
|
return `import { TempstreamJSX, Templatable } from "tempstream";
|
|
9
|
-
import {
|
|
9
|
+
import { Context } from "koa";
|
|
10
10
|
import { StatefulPage } from "@sealcode/sealgen";
|
|
11
11
|
import html from "${rel("src/back/html.js")}";
|
|
12
12
|
|
|
@@ -38,11 +38,11 @@ export default new (class ${action_name}Page extends StatefulPage<State, typeof
|
|
|
38
38
|
return { elements: ["one", "two", "three"] };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
wrapInLayout(ctx:
|
|
41
|
+
wrapInLayout(ctx: Context, content: Templatable): Templatable {
|
|
42
42
|
return html(ctx, "${action_name}", content);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
render(ctx:
|
|
45
|
+
render(ctx: Context, state: State, inputs: Record<string, string>) {
|
|
46
46
|
return (
|
|
47
47
|
<div>
|
|
48
48
|
<ul>
|