@sealcode/sealgen 0.19.18 → 0.19.20

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -4,6 +4,10 @@ export default class TableController extends Controller {
4
4
  tableTarget: HTMLTableElement;
5
5
  templateTarget: HTMLTemplateElement;
6
6
  getTbody(): HTMLTableSectionElement;
7
+ getRowIndex(tr: HTMLTableRowElement): number;
7
8
  addRow(): void;
8
- swapRows(e: MouseEvent): void;
9
+ swapRows(index_a: number, index_b: number): void;
10
+ moveUp(e: MouseEvent): void;
11
+ moveDown(e: MouseEvent): void;
12
+ reindex(): void;
9
13
  }
package/@types/index.d.ts CHANGED
@@ -45,3 +45,4 @@ export interface HTMLArgs {
45
45
  hideNavigation?: boolean;
46
46
  }
47
47
  export type HTMLFunc = (args: HTMLArgs) => Readable | Promise<Readable>;
48
+ export type BreadcrumbLabel = string | ((ctx: Context) => Promise<string>);
@@ -5,8 +5,8 @@ import { walkDir } from "./utils/walk.js";
5
5
  import { importPath } from "./utils/import-path.js";
6
6
  import { assertType, predicates } from "@sealcode/ts-predicates";
7
7
  import { unescape_url_params } from "./utils/escape-url-params.js";
8
- import { formatWithPrettier } from "./utils/prettier.js";
9
8
  import { Templates } from "./templates/templates.js";
9
+ import { formatWithPrettier } from "./utils/prettier.js";
10
10
  const target_locreq = _locreq(process.cwd());
11
11
  async function extractActionName(full_file_path) {
12
12
  const file_content = await fs.readFile(full_file_path, "utf-8");
@@ -49,6 +49,12 @@ function sortRoutes(urls) {
49
49
  return encoded1 < encoded2 ? -1 : 1;
50
50
  });
51
51
  }
52
+ const getPathToBuiltFile = (filePath) => {
53
+ const rootPath = target_locreq.resolve("");
54
+ const relativePath = relative(rootPath, filePath);
55
+ const distPath = relativePath.replace(/^src[\\/]/, "dist/");
56
+ return distPath.replace(/\.(ts|tsx)$/, ".js");
57
+ };
52
58
  async function generateRoutes() {
53
59
  const files = await Promise.all(
54
60
  (await walkDir(target_locreq.resolve("src/back/routes"))).filter(
@@ -58,6 +64,7 @@ async function generateRoutes() {
58
64
  ).map(async (fullpath) => ({
59
65
  fullpath,
60
66
  actionName: await extractActionName(fullpath),
67
+ module_path: getPathToBuiltFile(fullpath),
61
68
  // trailing slash is important here, as it enables to use the entire path while building relative URLs. For example, while visiting /users/123, the path ./add-photo leads to /users/add-photo. While visiting /users/123/ (note the trailing slash), the path ./add-photo leads to /users/123/add-photo
62
69
  url: unescape_url_params(
63
70
  "/" + relative(
@@ -71,7 +78,7 @@ async function generateRoutes() {
71
78
  }))
72
79
  );
73
80
  const url_tree = { children: {} };
74
- sortRoutes(files).forEach(({ actionName, url }) => {
81
+ sortRoutes(files).forEach(({ actionName, url, module_path }) => {
75
82
  const elements = url.split("/").filter((e) => e != "");
76
83
  let pointer = url_tree;
77
84
  for (const [index, element] of elements.entries()) {
@@ -81,6 +88,7 @@ async function generateRoutes() {
81
88
  pointer = pointer.children[element];
82
89
  if (index == elements.length - 1) {
83
90
  pointer.actionName = actionName;
91
+ pointer.module_path = module_path;
84
92
  }
85
93
  }
86
94
  });
@@ -88,6 +96,7 @@ async function generateRoutes() {
88
96
 
89
97
  export type URLTree = {
90
98
  actionName?: string;
99
+ module_path?: string;
91
100
  children: { [key: string]: URLTree };
92
101
  };
93
102
  export const url_tree = ${JSON.stringify(url_tree)};
@@ -41,13 +41,13 @@ const defaults = {
41
41
  async function formTemplate(action_name, _params = defaults) {
42
42
  const params = { ...defaults, ..._params };
43
43
  const content = `import type { Context } from "koa";
44
- import type { FormData } from "@sealcode/sealgen";
44
+ import type { FormData, BreadcrumbLabel } from "@sealcode/sealgen";
45
45
  import { Form, Controls } from "@sealcode/sealgen";
46
46
  import html from "src/back/html.js";
47
-
48
47
  ${params.postimport}
49
48
 
50
49
  export const actionName = "${action_name}";
50
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
51
51
 
52
52
  const getFields = ${params.get_fields};
53
53
 
@@ -48,12 +48,14 @@ import type { FieldNames } from "sealious";
48
48
  import { TempstreamJSX } from "tempstream";
49
49
  import type ${collectionClassName} from "src/back/collections/${collection_name}.js";
50
50
  import { EditJDDField } from "@sealcode/jdd-editor";
51
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
51
52
  import html from "src/back/html.js";
52
53
  import { registry } from "src/back/jdd-components/registry.js";
53
54
  import { makeJDDContext } from "src/back/jdd-context.js";
54
55
  import { defaultHead } from "src/back/defaultHead.js";
55
56
 
56
57
  export const actionName = "${actionName}";
58
+ export const breadcrumbLabel: BreadcrumbLabel = "${actionName}";
57
59
 
58
60
  export default new (class JDDCreatePreviewPage extends EditJDDField<${collectionClassName}> {
59
61
  getCollection(ctx: Context) {
@@ -5,10 +5,12 @@ async function LongRunningProcessTemplate(action_name, newfilefullpath) {
5
5
  return `import { Context } from "koa";
6
6
  import { TempstreamJSX } from "tempstream";
7
7
  import { Page } from "@sealcode/sealgen";
8
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
8
9
  import html from "${rel("src/back/html.js")}";
9
10
  import { LongRunningProcess } from "sealious";
10
11
 
11
12
  export const actionName = "${action_name}";
13
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
12
14
 
13
15
  export default new (class PatroniteAutoImportStatusPage extends Page {
14
16
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -4,9 +4,11 @@ async function multiformTemplate(action_name, newfilefullpath) {
4
4
  return `import { Context } from "koa";
5
5
  import html from "${rel("src/back/html.js")}";
6
6
  import { Controls, Fields, Form, FormMessage, Multiform } from "@sealcode/sealgen";
7
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
7
8
  import { FlatTemplatable } from "tempstream";
8
9
 
9
10
  export const actionName = "${action_name}";
11
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
10
12
 
11
13
  const fields1 = {
12
14
  email: new Fields.TextBasedSimpleField(true),
@@ -2,9 +2,11 @@ async function pageTemplate(action_name) {
2
2
  return `import type { Context } from "koa";
3
3
  import { TempstreamJSX } from "tempstream";
4
4
  import { Page } from "@sealcode/sealgen";
5
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
5
6
  import html from "src/back/html.js";
6
7
 
7
8
  export const actionName = "${action_name}";
9
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
8
10
 
9
11
  export default new (class ${action_name}Page extends Page {
10
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  async function postTemplate(action_name) {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  async function redirectTemplate(action_name) {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -91,7 +91,7 @@ import type { FlatTemplatable, Templatable } from "tempstream";
91
91
  import { TempstreamJSX, tempstream } from "tempstream";
92
92
  import { ${uppercase_collection} } from "src/back/collections/collections.js";
93
93
  import html from "src/back/html.js";
94
- import type { ListFilterRender, FormDisplayInfo } from "@sealcode/sealgen";
94
+ import type { ListFilterRender, FormDisplayInfo, BreadcrumbLabel } from "@sealcode/sealgen";
95
95
  import {
96
96
  SealiousItemListPage,
97
97
  BaseListPageFields,
@@ -103,6 +103,7 @@ ${hooks.post_import_js}
103
103
  ${field_import_string}
104
104
 
105
105
  export const actionName = "${action_name}";
106
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
106
107
 
107
108
  const filterFields:{
108
109
  field: keyof (typeof ${uppercase_collection})["fields"];
@@ -4,6 +4,7 @@ function itemDeleteTemplate(delete_action_name, collection_name, list_action_nam
4
4
  const result = `import type { Context } from "koa";
5
5
  import { Mountable } from "@sealcode/sealgen";
6
6
  import type Router from "@koa/router";
7
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
7
8
 
8
9
  import { ${toPascalCase(collection_name)} } from "${importPath(
9
10
  "src/back/collections/collections.js"
@@ -14,6 +15,7 @@ import { ${list_action_name}URL } from "${importPath(
14
15
  )}";
15
16
 
16
17
  export const actionName = "${delete_action_name}";
18
+ export const breadcrumbLabel: BreadcrumbLabel = "${delete_action_name}";
17
19
 
18
20
  export default new (class ${delete_action_name}Redirect extends Mountable {
19
21
  canAccess = async (ctx: Context) => {
@@ -5,9 +5,11 @@ async function statefulPageTemplate(action_name, newfilefullpath) {
5
5
  return formatWithPrettier(`import { TempstreamJSX, Templatable } from "tempstream";
6
6
  import { Context } from "koa";
7
7
  import { StatefulPage, StatefulPageActionArgument } from "@sealcode/sealgen";
8
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
8
9
  import html from "${rel("src/back/html.js")}";
9
10
 
10
11
  export const actionName = "${action_name}";
12
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
11
13
 
12
14
  const actions = {
13
15
  add: ({ state, inputs }: StatefulPageActionArgument<State>) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealcode/sealgen",
3
- "version": "0.19.18",
3
+ "version": "0.19.20",
4
4
  "description": "Module to automate adding routes and collections to a sealious application",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -0,0 +1,44 @@
1
+ import { Locator, Page } from "playwright";
2
+ import { RealAppTest } from "./test_utils/test-on-real-app.js";
3
+ import { getBrowser } from "./utils/browser-creator.js";
4
+
5
+ describe("add crud test", () => {
6
+ let page: Page;
7
+ before(async () => {
8
+ const browser = await getBrowser();
9
+ const context = await browser.newContext();
10
+ page = await context.newPage();
11
+ });
12
+
13
+ after(async () => {
14
+ await page.close();
15
+ });
16
+
17
+ it("Generate simple sealious crud", async function () {
18
+ const test_app = await RealAppTest.init();
19
+
20
+ const path = "/sample";
21
+ await test_app.runSealgenCommand(`add-collection --collection_name=sealcode-test`);
22
+ await test_app.runSealgenCommand(`add-crud --url=${path} --collection=sealcode-test`);
23
+ await test_app.start();
24
+
25
+ await page.goto(test_app.fullURL(`sample/create/`));
26
+
27
+ const contentInput: Locator = page.getByLabel("Content");
28
+
29
+ await contentInput.focus();
30
+ await contentInput.fill("Hello");
31
+
32
+ await page.locator("[type=submit]").click();
33
+ await page.locator('[href="/sample/"]').click();
34
+
35
+ const dataFieldNameAttr: string | null = await page
36
+ .getByText("Hello")
37
+ .getAttribute("data-field-name");
38
+
39
+ if (dataFieldNameAttr !== "content") {
40
+ throw new Error("Generated td element does not have correct data-field-name attribute");
41
+ }
42
+ await test_app.close();
43
+ }).timeout(100 * 1000);
44
+ });
package/src/add-crud.ts CHANGED
@@ -71,7 +71,7 @@ export async function addCRUD(
71
71
  {displayFields.map(({ field, format }) => {
72
72
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
73
73
  const value = item.get(field as any);
74
- return <td>{format ? format(value, item) : value}</td>;
74
+ return <td data-field-name={field}>{format ? format(value, item) : value}</td>;
75
75
  })}
76
76
  <td><div class="sealious-list__actions">
77
77
  <a href={${edit_action_name}URL(item.id)}>Edit</a>
@@ -5,8 +5,8 @@ import { walkDir } from "./utils/walk.js";
5
5
  import { importPath } from "./utils/import-path.js";
6
6
  import { assertType, predicates } from "@sealcode/ts-predicates";
7
7
  import { unescape_url_params } from "./utils/escape-url-params.js";
8
- import { formatWithPrettier } from "./utils/prettier.js";
9
8
  import { Templates } from "./templates/templates.js";
9
+ import { formatWithPrettier } from "./utils/prettier.js";
10
10
 
11
11
  const target_locreq = _locreq(process.cwd());
12
12
 
@@ -67,6 +67,13 @@ export function sortRoutes<T extends { url: string }>(urls: T[]): T[] {
67
67
  });
68
68
  }
69
69
 
70
+ const getPathToBuiltFile = (filePath: string) => {
71
+ const rootPath = target_locreq.resolve("");
72
+ const relativePath = relative(rootPath, filePath);
73
+ const distPath = relativePath.replace(/^src[\\/]/, "dist/");
74
+ return distPath.replace(/\.(ts|tsx)$/, ".js");
75
+ };
76
+
70
77
  export async function generateRoutes(): Promise<void> {
71
78
  const files = await Promise.all(
72
79
  (await walkDir(target_locreq.resolve("src/back/routes")))
@@ -79,6 +86,7 @@ export async function generateRoutes(): Promise<void> {
79
86
  .map(async (fullpath) => ({
80
87
  fullpath,
81
88
  actionName: await extractActionName(fullpath),
89
+ module_path: getPathToBuiltFile(fullpath),
82
90
  // trailing slash is important here, as it enables to use the entire path while building relative URLs. For example, while visiting /users/123, the path ./add-photo leads to /users/add-photo. While visiting /users/123/ (note the trailing slash), the path ./add-photo leads to /users/123/add-photo
83
91
  url: unescape_url_params(
84
92
  "/" +
@@ -98,11 +106,12 @@ export async function generateRoutes(): Promise<void> {
98
106
 
99
107
  type URLTree = {
100
108
  actionName?: string;
109
+ module_path?: string;
101
110
  children: { [key: string]: URLTree };
102
111
  };
103
112
 
104
113
  const url_tree: URLTree = { children: {} };
105
- sortRoutes(files).forEach(({ actionName, url }) => {
114
+ sortRoutes(files).forEach(({ actionName, url, module_path }) => {
106
115
  const elements = url.split("/").filter((e) => e != "");
107
116
  let pointer: URLTree = url_tree;
108
117
  for (const [index, element] of elements.entries()) {
@@ -113,6 +122,7 @@ export async function generateRoutes(): Promise<void> {
113
122
  if (index == elements.length - 1) {
114
123
  (pointer as unknown as { actionName: string }).actionName =
115
124
  actionName;
125
+ pointer.module_path = module_path;
116
126
  }
117
127
  }
118
128
  });
@@ -121,6 +131,7 @@ export async function generateRoutes(): Promise<void> {
121
131
 
122
132
  export type URLTree = {
123
133
  actionName?: string;
134
+ module_path?: string;
124
135
  children: { [key: string]: URLTree };
125
136
  };
126
137
  export const url_tree = ${JSON.stringify(url_tree)};
package/src/index.ts CHANGED
@@ -52,3 +52,5 @@ export interface HTMLArgs {
52
52
  }
53
53
 
54
54
  export type HTMLFunc = (args: HTMLArgs) => Readable | Promise<Readable>;
55
+
56
+ export type BreadcrumbLabel = string | ((ctx: Context) => Promise<string>);
@@ -46,13 +46,13 @@ export async function formTemplate(
46
46
  ): Promise<string> {
47
47
  const params = { ...defaults, ..._params };
48
48
  const content = `import type { Context } from "koa";
49
- import type { FormData } from "@sealcode/sealgen";
49
+ import type { FormData, BreadcrumbLabel } from "@sealcode/sealgen";
50
50
  import { Form, Controls } from "@sealcode/sealgen";
51
51
  import html from "src/back/html.js";
52
-
53
52
  ${params.postimport}
54
53
 
55
54
  export const actionName = "${action_name}";
55
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
56
56
 
57
57
  const getFields = ${params.get_fields};
58
58
 
@@ -58,12 +58,14 @@ import type { FieldNames } from "sealious";
58
58
  import { TempstreamJSX } from "tempstream";
59
59
  import type ${collectionClassName} from "src/back/collections/${collection_name}.js";
60
60
  import { EditJDDField } from "@sealcode/jdd-editor";
61
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
61
62
  import html from "src/back/html.js";
62
63
  import { registry } from "src/back/jdd-components/registry.js";
63
64
  import { makeJDDContext } from "src/back/jdd-context.js";
64
65
  import { defaultHead } from "src/back/defaultHead.js";
65
66
 
66
67
  export const actionName = "${actionName}";
68
+ export const breadcrumbLabel: BreadcrumbLabel = "${actionName}";
67
69
 
68
70
  export default new (class JDDCreatePreviewPage extends EditJDDField<${collectionClassName}> {
69
71
  getCollection(ctx: Context) {
@@ -9,10 +9,12 @@ export async function LongRunningProcessTemplate(
9
9
  return `import { Context } from "koa";
10
10
  import { TempstreamJSX } from "tempstream";
11
11
  import { Page } from "@sealcode/sealgen";
12
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
12
13
  import html from "${rel("src/back/html.js")}";
13
14
  import { LongRunningProcess } from "sealious";
14
15
 
15
16
  export const actionName = "${action_name}";
17
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
16
18
 
17
19
  export default new (class PatroniteAutoImportStatusPage extends Page {
18
20
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -8,9 +8,11 @@ export async function multiformTemplate(
8
8
  return `import { Context } from "koa";
9
9
  import html from "${rel("src/back/html.js")}";
10
10
  import { Controls, Fields, Form, FormMessage, Multiform } from "@sealcode/sealgen";
11
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
11
12
  import { FlatTemplatable } from "tempstream";
12
13
 
13
14
  export const actionName = "${action_name}";
15
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
14
16
 
15
17
  const fields1 = {
16
18
  email: new Fields.TextBasedSimpleField(true),
@@ -2,9 +2,11 @@ export async function pageTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { TempstreamJSX } from "tempstream";
4
4
  import { Page } from "@sealcode/sealgen";
5
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
5
6
  import html from "src/back/html.js";
6
7
 
7
8
  export const actionName = "${action_name}";
9
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
8
10
 
9
11
  export default new (class ${action_name}Page extends Page {
10
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  export async function postTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  export async function redirectTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -118,7 +118,7 @@ import type { FlatTemplatable, Templatable } from "tempstream";
118
118
  import { TempstreamJSX, tempstream } from "tempstream";
119
119
  import { ${uppercase_collection} } from "src/back/collections/collections.js";
120
120
  import html from "src/back/html.js";
121
- import type { ListFilterRender, FormDisplayInfo } from "@sealcode/sealgen";
121
+ import type { ListFilterRender, FormDisplayInfo, BreadcrumbLabel } from "@sealcode/sealgen";
122
122
  import {
123
123
  SealiousItemListPage,
124
124
  BaseListPageFields,
@@ -130,6 +130,7 @@ ${hooks.post_import_js}
130
130
  ${field_import_string}
131
131
 
132
132
  export const actionName = "${action_name}";
133
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
133
134
 
134
135
  const filterFields:{
135
136
  field: keyof (typeof ${uppercase_collection})["fields"];
@@ -10,6 +10,7 @@ export function itemDeleteTemplate(
10
10
  const result = `import type { Context } from "koa";
11
11
  import { Mountable } from "@sealcode/sealgen";
12
12
  import type Router from "@koa/router";
13
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
13
14
 
14
15
  import { ${toPascalCase(collection_name)} } from "${importPath(
15
16
  "src/back/collections/collections.js"
@@ -20,6 +21,7 @@ import { ${list_action_name}URL } from "${importPath(
20
21
  )}";
21
22
 
22
23
  export const actionName = "${delete_action_name}";
24
+ export const breadcrumbLabel: BreadcrumbLabel = "${delete_action_name}";
23
25
 
24
26
  export default new (class ${delete_action_name}Redirect extends Mountable {
25
27
  canAccess = async (ctx: Context) => {
@@ -9,9 +9,11 @@ export async function statefulPageTemplate(
9
9
  return formatWithPrettier(`import { TempstreamJSX, Templatable } from "tempstream";
10
10
  import { Context } from "koa";
11
11
  import { StatefulPage, StatefulPageActionArgument } from "@sealcode/sealgen";
12
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
12
13
  import html from "${rel("src/back/html.js")}";
13
14
 
14
15
  export const actionName = "${action_name}";
16
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
15
17
 
16
18
  const actions = {
17
19
  add: ({ state, inputs }: StatefulPageActionArgument<State>) => {