@effindomv2/create-fui-as-app 0.1.5 → 0.1.7

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.
Files changed (68) hide show
  1. package/README.md +26 -0
  2. package/dist/src/templates.js +1 -1
  3. package/dist/src/versions.d.ts +1 -1
  4. package/dist/src/versions.js +1 -1
  5. package/dist/tests/scaffold.test.js +5 -2
  6. package/package.json +5 -4
  7. package/templates/hello/README.md +20 -0
  8. package/{build/templates → templates}/hello/package.json +2 -2
  9. package/{build/templates → templates}/hello/src/App.ts +1 -1
  10. package/{build/templates → templates}/hello/src/HelloWorld.ts +32 -21
  11. package/templates/hello/src/fui/Fui.ts +1 -0
  12. package/templates/hello/src/fui/FuiBrowser.ts +1 -0
  13. package/templates/hello/src/fui/FuiExports.ts +1 -0
  14. package/templates/hello/src/fui/FuiPrimitives.ts +1 -0
  15. package/templates/mvc/README.md +27 -0
  16. package/{build/templates → templates}/mvc/harness.ts +9 -9
  17. package/templates/mvc/index.html +11 -0
  18. package/{build/templates → templates}/mvc/package.json +5 -5
  19. package/{build/templates → templates}/mvc/route-shell.html +2 -2
  20. package/{build/templates → templates}/mvc/scripts/prepare-runtime.ts +4 -4
  21. package/{build/templates → templates}/mvc/scripts/smoke.ts +4 -4
  22. package/templates/mvc/src/fui/Fui.ts +1 -0
  23. package/templates/mvc/src/fui/FuiBrowser.ts +1 -0
  24. package/templates/mvc/src/fui/FuiExports.ts +1 -0
  25. package/templates/mvc/src/fui/FuiPrimitives.ts +1 -0
  26. package/templates/mvc/src/routes/HomeApp.ts +16 -0
  27. package/templates/mvc/src/routes/SettingsApp.ts +16 -0
  28. package/{build/templates/mvc/src/routes/mvc/pages → templates/mvc/src/routes}/home/HomeController.ts +8 -10
  29. package/{build/templates/mvc/src/routes/mvc/pages → templates/mvc/src/routes}/home/HomeView.ts +37 -25
  30. package/{build/templates/mvc/src/routes/mvc/pages → templates/mvc/src/routes}/settings/SettingsController.ts +5 -7
  31. package/templates/mvc/src/routes/settings/SettingsView.ts +73 -0
  32. package/templates/mvc/src/routes/shared/design-system/NavBar.ts +18 -0
  33. package/templates/mvc/src/routes/shared/design-system/NavPill.ts +59 -0
  34. package/{build/templates/mvc/src/routes/mvc/shared/design-system/MvcPrimaryButton.ts → templates/mvc/src/routes/shared/design-system/PrimaryButton.ts} +2 -7
  35. package/{build/templates/mvc/src/routes/mvc → templates/mvc/src/routes}/shared/routes.ts +7 -7
  36. package/build/templates/hello/src/fui/Fui.ts +0 -1
  37. package/build/templates/hello/src/fui/FuiBrowser.ts +0 -1
  38. package/build/templates/hello/src/fui/FuiExports.ts +0 -1
  39. package/build/templates/hello/src/fui/FuiPrimitives.ts +0 -1
  40. package/build/templates/mvc/index.html +0 -11
  41. package/build/templates/mvc/src/fui/Fui.ts +0 -1
  42. package/build/templates/mvc/src/fui/FuiBrowser.ts +0 -1
  43. package/build/templates/mvc/src/fui/FuiExports.ts +0 -1
  44. package/build/templates/mvc/src/fui/FuiPrimitives.ts +0 -1
  45. package/build/templates/mvc/src/routes/mvc/pages/settings/SettingsView.ts +0 -61
  46. package/build/templates/mvc/src/routes/mvc/shared/design-system/MvcNavPill.ts +0 -42
  47. package/build/templates/mvc/src/routes/mvc_home.ts +0 -20
  48. package/build/templates/mvc/src/routes/mvc_settings.ts +0 -20
  49. package/dist/scripts/sync-templates.d.ts +0 -1
  50. package/dist/scripts/sync-templates.js +0 -297
  51. /package/{build/templates → templates}/hello/asconfig.json +0 -0
  52. /package/{build/templates → templates}/hello/harness.ts +0 -0
  53. /package/{build/templates → templates}/hello/index.html +0 -0
  54. /package/{build/templates → templates}/hello/scripts/prepare-runtime.ts +0 -0
  55. /package/{build/templates → templates}/hello/scripts/smoke.ts +0 -0
  56. /package/{build/templates → templates}/hello/src/host/generated/HostEvents.ts +0 -0
  57. /package/{build/templates → templates}/hello/src/host/generated/HostServices.ts +0 -0
  58. /package/{build/templates → templates}/hello/src/host/host-events.ts +0 -0
  59. /package/{build/templates → templates}/hello/src/host/host-services.ts +0 -0
  60. /package/{build/templates → templates}/hello/tsconfig.json +0 -0
  61. /package/{build/templates → templates}/mvc/asconfig.json +0 -0
  62. /package/{build/templates → templates}/mvc/src/host/generated/HostEvents.ts +0 -0
  63. /package/{build/templates → templates}/mvc/src/host/generated/HostServices.ts +0 -0
  64. /package/{build/templates → templates}/mvc/src/host/host-events.ts +0 -0
  65. /package/{build/templates → templates}/mvc/src/host/host-services.ts +0 -0
  66. /package/{build/templates/mvc/src/routes/mvc/pages → templates/mvc/src/routes}/home/HomeModel.ts +0 -0
  67. /package/{build/templates/mvc/src/routes/mvc/pages → templates/mvc/src/routes}/settings/SettingsModel.ts +0 -0
  68. /package/{build/templates → templates}/mvc/tsconfig.json +0 -0
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # EffinDom create-fui-as-app
2
+
3
+ This repository contains the standalone scaffolder package for FUI-AS apps:
4
+
5
+ - `@effindomv2/create-fui-as-app` (`v2/create-fui-as-app`)
6
+
7
+ Depends on published `@effindomv2/fui-as` and `@effindomv2/runtime` packages from npm.
8
+
9
+ ## License
10
+
11
+ MIT. See `LICENSE.md`.
12
+
13
+ ## Build and publish
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build
18
+ npm test
19
+ npm run publish:local
20
+ ```
21
+
22
+ For npm publishing:
23
+
24
+ ```bash
25
+ npm run publish:npm
26
+ ```
@@ -2,7 +2,7 @@ import { readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { FUI_AS_VERSION, RUNTIME_VERSION } from "./versions.js";
5
- const TEMPLATE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "build", "templates");
5
+ const TEMPLATE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "templates");
6
6
  function collectTemplateFiles(root, relativePath = "") {
7
7
  const absolutePath = resolve(root, relativePath);
8
8
  const stats = statSync(absolutePath);
@@ -1,2 +1,2 @@
1
- export declare const FUI_AS_VERSION = "0.1.2";
1
+ export declare const FUI_AS_VERSION = "latest";
2
2
  export declare const RUNTIME_VERSION = "0.1.0";
@@ -1,2 +1,2 @@
1
- export const FUI_AS_VERSION = "0.1.2";
1
+ export const FUI_AS_VERSION = "latest";
2
2
  export const RUNTIME_VERSION = "0.1.0";
@@ -21,6 +21,7 @@ test("createProject writes hello-world scaffold including AssemblyScript tsconfi
21
21
  assert.equal(typeof packageJson.scripts.test, "string");
22
22
  assert.equal(typeof packageJson.scripts["generate:host"], "string");
23
23
  assert.equal(readFileSync(join(target, "src", "HelloWorld.ts"), "utf8").includes("Hello world"), true);
24
+ assert.equal(readFileSync(join(target, "README.md"), "utf8").includes("Hello World scaffold guide"), true);
24
25
  assert.equal(readFileSync(join(target, "src", "fui", "Fui.ts"), "utf8").includes("@effindomv2/fui-as/src/Fui"), true);
25
26
  assert.equal(readFileSync(join(target, "src", "host", "host-events.ts"), "utf8").includes("appHostEvents"), true);
26
27
  assert.equal(readFileSync(join(target, "src", "host", "host-services.ts"), "utf8").includes("appHostServices"), true);
@@ -44,13 +45,15 @@ test("createProject writes mvc scaffold when template is mvc", () => {
44
45
  assert.equal(typeof packageJson.scripts["build:wasm:home"], "string");
45
46
  assert.equal(typeof packageJson.scripts["build:wasm:settings"], "string");
46
47
  assert.equal(typeof packageJson.scripts["generate:host"], "string");
47
- assert.equal(readFileSync(join(target, "src", "routes", "mvc", "pages", "home", "HomeController.ts"), "utf8").includes("HomeController"), true);
48
+ assert.equal(readFileSync(join(target, "src", "routes", "home", "HomeController.ts"), "utf8").includes("HomeController"), true);
49
+ assert.equal(readFileSync(join(target, "src", "routes", "HomeApp.ts"), "utf8").includes("createManagedApplication"), true);
50
+ assert.equal(readFileSync(join(target, "README.md"), "utf8").includes("MVC scaffold guide"), true);
48
51
  assert.equal(readFileSync(join(target, "src", "host", "host-events.ts"), "utf8").includes("appHostEvents"), true);
49
52
  assert.equal(readFileSync(join(target, "src", "host", "host-services.ts"), "utf8").includes("appHostServices"), true);
50
53
  assert.equal(readFileSync(join(target, "src", "fui", "Fui.ts"), "utf8").includes("@effindomv2/fui-as/src/Fui"), true);
51
54
  assert.equal(readFileSync(join(target, "src", "host", "generated", "HostEvents.ts"), "utf8").includes("onAppClockTick"), true);
52
55
  assert.equal(readFileSync(join(target, "src", "host", "generated", "HostServices.ts"), "utf8").includes("appClockNowUnixSeconds"), true);
53
- assert.equal(readFileSync(join(target, "route-shell.html"), "utf8").includes("FUI-AS MVC Demo"), true);
56
+ assert.equal(readFileSync(join(target, "route-shell.html"), "utf8").includes("FUI-AS Routed Demo"), true);
54
57
  }
55
58
  finally {
56
59
  rmSync(root, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/create-fui-as-app",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Scaffold a minimal EffinDom v2 FUI-AS app",
@@ -24,13 +24,14 @@
24
24
  "types": "./dist/src/scaffold.d.ts",
25
25
  "files": [
26
26
  "dist",
27
- "build/templates"
27
+ "templates"
28
28
  ],
29
29
  "scripts": {
30
- "sync:templates": "tsx scripts/sync-templates.ts",
31
- "build": "npm run sync:templates && tsc -p tsconfig.json",
30
+ "build": "tsc -p tsconfig.json",
32
31
  "typecheck": "tsc -p tsconfig.json --noEmit",
33
32
  "test": "npm run build && node --test dist/tests/**/*.test.js",
33
+ "publish:local": "bash -c 'if [ -f scripts/publish/local/create-fui-as-app.sh ]; then bash scripts/publish/local/create-fui-as-app.sh; elif [ -f ../../scripts/publish/local/create-fui-as-app.sh ]; then bash ../../scripts/publish/local/create-fui-as-app.sh; else echo \"Missing create-fui-as-app publish:local script\" >&2; exit 1; fi'",
34
+ "publish:npm": "bash -c 'if [ -f scripts/publish/npm/create-fui-as-app.sh ]; then bash scripts/publish/npm/create-fui-as-app.sh; elif [ -f ../../scripts/publish/npm/create-fui-as-app.sh ]; then bash ../../scripts/publish/npm/create-fui-as-app.sh; else echo \"Missing create-fui-as-app publish:npm script\" >&2; exit 1; fi'",
34
35
  "prepack": "npm run build"
35
36
  },
36
37
  "devDependencies": {
@@ -0,0 +1,20 @@
1
+ # Hello World scaffold guide
2
+
3
+ This template is the smallest FUI-AS app shape:
4
+
5
+ - `src/App.ts` is the app entrypoint (`__runApp`) and where you compose your root retained UI tree.
6
+ - `src/HelloWorld.ts` is a simple feature module you can replace with your own screens/components.
7
+ - `harness.ts` boots the browser runtime and mounts the wasm app.
8
+ - `src/host/host-services.ts` and `src/host/host-events.ts` define app-owned JS bridge contracts.
9
+ - `src/host/generated/*` is generated from those host definition files.
10
+
11
+ ## Typical workflow
12
+
13
+ 1. Build UI from `src/App.ts` with controls/nodes from `./fui/Fui`.
14
+ 2. Split real features into more files under `src/` and compose them in `App.ts`.
15
+ 3. If browser-owned capabilities are needed (for example shell APIs), add definitions in `src/host/*.ts` and run `npm run generate:host`.
16
+ 4. Run `npm run dev` while iterating.
17
+
18
+ ## Architecture intent
19
+
20
+ Use this template as a single-app baseline: one wasm, one harness, one mounted root tree. Keep UI composition in AssemblyScript and keep browser glue inside `harness.ts` + host bridge definitions.
@@ -13,7 +13,7 @@
13
13
  "generate:host-events": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-events.ts src/host/host-events.ts appHostEvents src/host/generated/HostEvents.ts ../../fui/FuiPrimitives",
14
14
  "generate:host": "npm run generate:host-services && npm run generate:host-events",
15
15
  "watch": "chokidar \"src/**/*.ts\" \"harness.ts\" \"index.html\" \"asconfig.json\" --ignore \"src/host/generated/**\" -c \"npm run build\"",
16
- "serve": "http-server public -p 8080 -c-1",
16
+ "serve": "serve public --listen 8080",
17
17
  "dev": "npm run build && concurrently -k -n watch,serve \"npm run watch\" \"npm run serve\"",
18
18
  "test": "npm run build && tsx scripts/smoke.ts"
19
19
  },
@@ -26,7 +26,7 @@
26
26
  "chokidar-cli": "^3.0.0",
27
27
  "concurrently": "^9.2.1",
28
28
  "esbuild": "^0.27.7",
29
- "http-server": "^14.1.1",
29
+ "serve": "^14.2.5",
30
30
  "tsx": "^4.20.6"
31
31
  }
32
32
  }
@@ -4,4 +4,4 @@ export * from "./host/generated/HostEvents";
4
4
 
5
5
  import { createHelloWorldPage } from "./HelloWorld";
6
6
 
7
- Application.register((app) => app.page(createHelloWorldPage));
7
+ Application.register((app) => app.themeSystem().page(createHelloWorldPage));
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  AlignItems,
3
+ Action,
3
4
  Button,
4
5
  Column,
5
6
  JustifyContent,
6
- rgb,
7
+ Theme,
8
+ bindTheme,
9
+ activeTheme,
7
10
  SelectionArea,
8
11
  Text,
9
12
  TextAlign,
@@ -32,13 +35,20 @@ class HelloWorld {
32
35
  private readonly hostServiceLabel: Text = new Text("Host service time: -");
33
36
  private readonly hostEventLabel: Text = new Text("Host event tick: -");
34
37
  private readonly clockTickHandler: ClockTickHandler = new ClockTickHandler(this);
38
+ private readonly themeBinding!: Action<Theme>;
39
+ private readonly root!: SelectionArea;
35
40
 
36
41
  constructor() {
37
42
  this.counterLabel.fontSize(20.0);
38
- this.hostServiceLabel.fontSize(14.0).textColor(rgb(191, 219, 254));
39
- this.hostEventLabel.fontSize(14.0).textColor(rgb(134, 239, 172));
43
+ this.hostServiceLabel.fontSize(14.0);
44
+ this.hostEventLabel.fontSize(14.0);
40
45
  this.refreshHostServiceTime();
41
46
  onAppClockTick(this.clockTickHandler);
47
+ this.root = new SelectionArea().fillWidth().fillHeight() as SelectionArea;
48
+ this.themeBinding = bindTheme(this, (page, theme): void => {
49
+ page.applyTheme(theme);
50
+ });
51
+ this.applyTheme(activeTheme.value);
42
52
  }
43
53
 
44
54
  private refreshHostServiceTime(): void {
@@ -50,6 +60,10 @@ class HelloWorld {
50
60
  this.hostEventLabel.text("Host event tick: " + seconds.toString());
51
61
  }
52
62
 
63
+ private applyTheme(theme: Theme): void {
64
+ this.root.bgColor(theme.colors.background);
65
+ }
66
+
53
67
  buildPage(): SelectionArea {
54
68
 
55
69
  const title = new Text("Hello world from FUI-AS")
@@ -79,24 +93,21 @@ class HelloWorld {
79
93
  .textAlign(TextAlign.Center)
80
94
  .width(680.0, Unit.Pixel);
81
95
 
82
- return new SelectionArea()
83
- .fillWidth()
84
- .fillHeight()
85
- .child(
86
- Column(
87
- title,
88
- subtitle,
89
- button,
90
- this.counterLabel,
91
- this.hostServiceLabel,
92
- this.hostEventLabel,
93
- note)
94
- .fillWidth()
95
- .fillHeight()
96
- .padding(24.0, 24.0, 24.0, 24.0)
97
- .justifyContent(JustifyContent.Center)
98
- .alignItems(AlignItems.Center))
99
- .bgColor(rgb(0, 0, 0)) as SelectionArea;
96
+ return this.root.child(
97
+ Column(
98
+ title,
99
+ subtitle,
100
+ button,
101
+ this.counterLabel,
102
+ this.hostServiceLabel,
103
+ this.hostEventLabel,
104
+ note)
105
+ .fillWidth()
106
+ .fillHeight()
107
+ .padding(24.0, 24.0, 24.0, 24.0)
108
+ .justifyContent(JustifyContent.Center)
109
+ .alignItems(AlignItems.Center),
110
+ ) as SelectionArea;
100
111
  }
101
112
  }
102
113
 
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/Fui";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/browser";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/FuiExports";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/FuiPrimitives";
@@ -0,0 +1,27 @@
1
+ # MVC scaffold guide
2
+
3
+ This template scaffolds a multi-route FUI-AS app with explicit page-level MVC.
4
+
5
+ - `src/routes/HomeApp.ts` and `src/routes/SettingsApp.ts` are route app entrypoints.
6
+ - `src/routes/home/*` and `src/routes/settings/*` hold each route's model, view, and controller.
7
+ - `src/routes/shared/*` holds shared route UI primitives (for example nav pills/nav bar) and shared route helpers.
8
+ - `harness.ts` wires routed host boot, route table, and host-services/events registration.
9
+ - `route-shell.html` is the per-route host shell copied for each route path.
10
+ - `src/host/host-services.ts` and `src/host/host-events.ts` are app-owned bridge contracts; `src/host/generated/*` is generated output.
11
+
12
+ ## Routing + deployment model
13
+
14
+ Each route builds to its own wasm (`home.wasm`, `settings.wasm`) and is mounted by routed harness config. This is designed for true MFE slices: each route app can evolve and deploy independently while still sharing the same browser host/runtime contract.
15
+
16
+ The routed harness is optimized for fast navigation:
17
+
18
+ - route shells stay stable while route wasm swaps,
19
+ - warm route transitions can avoid full loading overlays,
20
+ - host bridge wiring is reused consistently across route apps.
21
+
22
+ ## Typical workflow
23
+
24
+ 1. Add/modify page behavior inside `home/` or `settings/` controllers/models/views.
25
+ 2. Add new routes by creating another `*App.ts` entrypoint plus a new route folder and route record in `harness.ts`.
26
+ 3. Keep shared browser bridge capabilities in `src/host/*.ts`, then regenerate with `npm run generate:host`.
27
+ 4. Use `npm run dev` for local routed iteration.
@@ -2,7 +2,7 @@ import { startRoutedHarness, type HarnessExports, type RoutedHarnessRoute } from
2
2
  import { appHostEvents } from './src/host/host-events';
3
3
  import { appHostServices } from './src/host/host-services';
4
4
 
5
- type MvcRouteExports = HarnessExports & {
5
+ type RouteExports = HarnessExports & {
6
6
  __runApp(): void;
7
7
  __disposeApp?(): void;
8
8
  };
@@ -10,19 +10,19 @@ type MvcRouteExports = HarnessExports & {
10
10
  const routePrefix = window.location.pathname.startsWith('/v2/fui-as/demo-mvc/') ? '/v2/fui-as/demo-mvc' : '';
11
11
  const routes: readonly RoutedHarnessRoute[] = [
12
12
  {
13
- routePath: `${routePrefix}/mvc-home/`,
14
- wasmPath: `${routePrefix}/mvc-home.wasm`,
15
- title: 'MVC Home',
13
+ routePath: `${routePrefix}/home/`,
14
+ wasmPath: `${routePrefix}/home.wasm`,
15
+ title: 'Home',
16
16
  },
17
17
  {
18
- routePath: `${routePrefix}/mvc-settings/`,
19
- wasmPath: `${routePrefix}/mvc-settings.wasm`,
20
- title: 'MVC Settings',
18
+ routePath: `${routePrefix}/settings/`,
19
+ wasmPath: `${routePrefix}/settings.wasm`,
20
+ title: 'Settings',
21
21
  },
22
22
  ];
23
23
 
24
- startRoutedHarness<MvcRouteExports>({
25
- shellId: 'fui-mvc',
24
+ startRoutedHarness<RouteExports>({
25
+ shellId: 'fui-routes',
26
26
  routeBase: routes[0].routePath,
27
27
  routes,
28
28
  recreateRuntimeOnWarmRouteSwap: true,
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta http-equiv="refresh" content="0; url=/home/" />
6
+ <title>FUI-AS Routed App</title>
7
+ </head>
8
+ <body>
9
+ <p>Redirecting to <a href="/home/">/home/</a>…</p>
10
+ </body>
11
+ </html>
@@ -3,19 +3,19 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
- "description": "Scaffolded FUI-AS MVC app",
6
+ "description": "Scaffolded FUI-AS routed app",
7
7
  "scripts": {
8
8
  "build": "npm run generate:host && npm run build:assets && npm run build:wasm && npm run build:harness",
9
9
  "build:assets": "tsx scripts/prepare-runtime.ts",
10
10
  "build:wasm": "npm run build:wasm:home && npm run build:wasm:settings",
11
- "build:wasm:home": "asc src/routes/mvc_home.ts --config asconfig.json --target release --outFile public/mvc-home.wasm",
12
- "build:wasm:settings": "asc src/routes/mvc_settings.ts --config asconfig.json --target release --outFile public/mvc-settings.wasm",
11
+ "build:wasm:home": "asc src/routes/HomeApp.ts --config asconfig.json --target release --outFile public/home.wasm",
12
+ "build:wasm:settings": "asc src/routes/SettingsApp.ts --config asconfig.json --target release --outFile public/settings.wasm",
13
13
  "build:harness": "esbuild harness.ts --bundle --format=esm --platform=browser --outfile=public/harness.js",
14
14
  "generate:host-services": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-services.ts src/host/host-services.ts appHostServices src/host/generated/HostServices.ts ../../fui/FuiPrimitives",
15
15
  "generate:host-events": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-events.ts src/host/host-events.ts appHostEvents src/host/generated/HostEvents.ts ../../fui/FuiPrimitives",
16
16
  "generate:host": "npm run generate:host-services && npm run generate:host-events",
17
17
  "watch": "chokidar \"src/**/*.ts\" \"harness.ts\" \"route-shell.html\" \"index.html\" \"asconfig.json\" --ignore \"src/host/generated/**\" -c \"npm run build\"",
18
- "serve": "http-server public -p 8080 -c-1",
18
+ "serve": "serve public --listen 8080",
19
19
  "dev": "npm run build && concurrently -k -n watch,serve \"npm run watch\" \"npm run serve\"",
20
20
  "test": "npm run build && tsx scripts/smoke.ts"
21
21
  },
@@ -28,7 +28,7 @@
28
28
  "chokidar-cli": "^3.0.0",
29
29
  "concurrently": "^9.2.1",
30
30
  "esbuild": "^0.27.7",
31
- "http-server": "^14.1.1",
31
+ "serve": "^14.2.5",
32
32
  "tsx": "^4.20.6"
33
33
  }
34
34
  }
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <base href="../" />
7
- <title>FUI-AS MVC Demo</title>
7
+ <title>FUI-AS Routed Demo</title>
8
8
  <style>
9
9
  html, body {
10
10
  margin: 0;
@@ -47,7 +47,7 @@
47
47
  <canvas id="fui-canvas"></canvas>
48
48
  </main>
49
49
  <div class="effindom-loading-overlay" id="effindom-loading-overlay" data-state="loading">
50
- <div>Loading MVC demo…</div>
50
+ <div>Loading routed demo…</div>
51
51
  </div>
52
52
  <script src="./effindom-runtime-config.js"></script>
53
53
  <script src="./bridge.js"></script>
@@ -3,8 +3,8 @@ import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync, writeFileSync } fr
3
3
  const outputDir = "public";
4
4
  rmSync(outputDir, { recursive: true, force: true });
5
5
  mkdirSync(`${outputDir}/runtime`, { recursive: true });
6
- mkdirSync(`${outputDir}/mvc-home`, { recursive: true });
7
- mkdirSync(`${outputDir}/mvc-settings`, { recursive: true });
6
+ mkdirSync(`${outputDir}/home`, { recursive: true });
7
+ mkdirSync(`${outputDir}/settings`, { recursive: true });
8
8
 
9
9
  cpSync("node_modules/@effindomv2/runtime/dist", `${outputDir}/runtime/dist`, { recursive: true });
10
10
  cpSync("node_modules/@effindomv2/runtime/dist/fonts", `${outputDir}/runtime/fonts`, { recursive: true });
@@ -18,5 +18,5 @@ writeFileSync(
18
18
  "utf8",
19
19
  );
20
20
  copyFileSync("index.html", `${outputDir}/index.html`);
21
- copyFileSync("route-shell.html", `${outputDir}/mvc-home/index.html`);
22
- copyFileSync("route-shell.html", `${outputDir}/mvc-settings/index.html`);
21
+ copyFileSync("route-shell.html", `${outputDir}/home/index.html`);
22
+ copyFileSync("route-shell.html", `${outputDir}/settings/index.html`);
@@ -3,10 +3,10 @@ import { accessSync } from "node:fs";
3
3
  const expectedFiles = [
4
4
  "public/index.html",
5
5
  "public/harness.js",
6
- "public/mvc-home/index.html",
7
- "public/mvc-settings/index.html",
8
- "public/mvc-home.wasm",
9
- "public/mvc-settings.wasm",
6
+ "public/home/index.html",
7
+ "public/settings/index.html",
8
+ "public/home.wasm",
9
+ "public/settings.wasm",
10
10
  "public/bridge.js",
11
11
  "public/effindom-runtime-config.js",
12
12
  "public/runtime/dist/effindom.v2.manifest.json",
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/Fui";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/browser";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/FuiExports";
@@ -0,0 +1 @@
1
+ export * from "@effindomv2/fui-as/src/FuiPrimitives";
@@ -0,0 +1,16 @@
1
+ export * from "../fui/FuiExports";
2
+ export * from "../host/generated/HostEvents";
3
+
4
+ import { createManagedApplication } from "../fui/Fui";
5
+ import { HomeController } from "./home/HomeController";
6
+
7
+ const app = createManagedApplication<HomeController>(() => new HomeController());
8
+ app.useSystemTheme();
9
+
10
+ export function __runApp(): void {
11
+ app.run();
12
+ }
13
+
14
+ export function __disposeApp(): void {
15
+ app.dispose();
16
+ }
@@ -0,0 +1,16 @@
1
+ export * from "../fui/FuiExports";
2
+ export * from "../host/generated/HostEvents";
3
+
4
+ import { createManagedApplication } from "../fui/Fui";
5
+ import { SettingsController } from "./settings/SettingsController";
6
+
7
+ const app = createManagedApplication<SettingsController>(() => new SettingsController());
8
+ app.useSystemTheme();
9
+
10
+ export function __runApp(): void {
11
+ app.run();
12
+ }
13
+
14
+ export function __disposeApp(): void {
15
+ app.dispose();
16
+ }
@@ -1,7 +1,7 @@
1
- import { Application, Node } from "../../../../fui/Fui";
2
- import { Callback1 } from "../../../../fui/FuiPrimitives";
3
- import { onAppClockTick } from "../../../../host/generated/HostEvents";
4
- import { appClockNowUnixSeconds } from "../../../../host/generated/HostServices";
1
+ import { ManagedApplicationController, Node } from "../../fui/Fui";
2
+ import { Callback1 } from "../../fui/FuiPrimitives";
3
+ import { onAppClockTick } from "../../host/generated/HostEvents";
4
+ import { appClockNowUnixSeconds } from "../../host/generated/HostServices";
5
5
  import { HomeModel } from "./HomeModel";
6
6
  import { HomeView } from "./HomeView";
7
7
 
@@ -19,12 +19,13 @@ class ClockTickHandler extends Callback1<i32> {
19
19
  }
20
20
  }
21
21
 
22
- export class HomeController {
22
+ export class HomeController extends ManagedApplicationController {
23
23
  readonly model: HomeModel = new HomeModel();
24
24
  readonly view: HomeView = new HomeView(this.model);
25
25
  private readonly clockTickHandler: ClockTickHandler = new ClockTickHandler(this);
26
26
 
27
27
  constructor() {
28
+ super();
28
29
  this.view.actionButton.onClickWith(this, (controller) => {
29
30
  controller.model.actionCount += 1;
30
31
  controller.view.setActionCount(controller.model.actionCount);
@@ -44,12 +45,9 @@ export class HomeController {
44
45
  return this.view.getRoot();
45
46
  }
46
47
 
47
- mount(): void {
48
- Application.mount(this.view.getRoot());
49
- }
50
-
51
48
  dispose(): void {
52
49
  onAppClockTick(null);
53
- Application.unmount();
50
+ this.view.dispose();
51
+ super.dispose();
54
52
  }
55
53
  }
@@ -1,37 +1,26 @@
1
- import { Column, FlexBox, JustifyContent, Row, SelectionArea, Text, Unit, rgb } from "../../../../fui/Fui";
2
- import { MvcNavPill } from "../../shared/design-system/MvcNavPill";
3
- import { MvcPrimaryButton } from "../../shared/design-system/MvcPrimaryButton";
4
- import { mvcHomeRoute, mvcSettingsRoute } from "../../shared/routes";
1
+ import { Column, Disposable, FlexBox, SelectionArea, Theme, activeTheme, bindTheme, disposeAll, Text, Unit } from "../../fui/Fui";
2
+ import { createNavBar } from "../shared/design-system/NavBar";
3
+ import { PrimaryButton } from "../shared/design-system/PrimaryButton";
5
4
  import { HomeModel } from "./HomeModel";
6
5
 
7
- function navSpacer(): FlexBox {
8
- return new FlexBox().width(10.0, Unit.Pixel).height(1.0, Unit.Pixel);
9
- }
10
-
11
6
  export class HomeView {
12
- readonly actionButton: MvcPrimaryButton;
7
+ readonly actionButton: PrimaryButton;
13
8
  private readonly statusText: Text;
14
9
  private readonly hostServiceText: Text;
15
10
  private readonly hostEventText: Text;
16
11
  private readonly root!: SelectionArea;
12
+ private readonly themeBindings: Array<Disposable> = new Array<Disposable>();
13
+ private themeBindingDisposed: bool = false;
17
14
 
18
15
  constructor(model: HomeModel) {
19
- const homePill = new MvcNavPill(mvcHomeRoute(), "Home").active(true);
20
- const settingsPill = new MvcNavPill(mvcSettingsRoute(), "Settings").active(false);
21
-
22
- const navBar = Row()
23
- .width(100.0, Unit.Percent)
24
- .justifyContent(JustifyContent.End)
25
- .child(homePill)
26
- .child(navSpacer())
27
- .child(settingsPill);
16
+ const navBar = createNavBar(true);
28
17
 
29
- const title = new Text(model.title).fontSize(34.0).textColor(rgb(241, 245, 249)) as Text;
30
- const subtitle = new Text(model.subtitle).fontSize(16.0).textColor(rgb(148, 163, 184)) as Text;
31
- this.statusText = new Text("Home counter: 0").fontSize(18.0).textColor(rgb(147, 197, 253)) as Text;
32
- this.hostServiceText = new Text("Host service time: -").fontSize(15.0).textColor(rgb(191, 219, 254)) as Text;
33
- this.hostEventText = new Text("Host event tick: -").fontSize(15.0).textColor(rgb(134, 239, 172)) as Text;
34
- this.actionButton = new MvcPrimaryButton(model.actionLabel);
18
+ const title = new Text(model.title).fontSize(34.0) as Text;
19
+ const subtitle = new Text(model.subtitle).fontSize(16.0) as Text;
20
+ this.statusText = new Text("Home counter: 0").fontSize(18.0) as Text;
21
+ this.hostServiceText = new Text("Host service time: -").fontSize(15.0) as Text;
22
+ this.hostEventText = new Text("Host event tick: -").fontSize(15.0) as Text;
23
+ this.actionButton = new PrimaryButton(model.actionLabel);
35
24
 
36
25
  const content = Column(
37
26
  navBar,
@@ -54,14 +43,21 @@ export class HomeView {
54
43
  this.root = new SelectionArea()
55
44
  .fillWidth()
56
45
  .fillHeight()
57
- .bgColor(rgb(2, 6, 23))
58
46
  .child(content) as SelectionArea;
47
+ this.trackTheme(bindTheme(this, (view, theme): void => {
48
+ view.applyTheme(theme);
49
+ }));
50
+ this.applyTheme(activeTheme.value);
59
51
  }
60
52
 
61
53
  getRoot(): SelectionArea {
62
54
  return this.root;
63
55
  }
64
56
 
57
+ dispose(): void {
58
+ this.disposeThemeBindings();
59
+ }
60
+
65
61
  setActionCount(count: i32): void {
66
62
  this.statusText.text("Home counter: " + count.toString());
67
63
  }
@@ -73,4 +69,20 @@ export class HomeView {
73
69
  setHostEventSeconds(value: i32): void {
74
70
  this.hostEventText.text("Host event tick: " + value.toString());
75
71
  }
72
+
73
+ private applyTheme(theme: Theme): void {
74
+ this.root.bgColor(theme.colors.background);
75
+ }
76
+
77
+ private trackTheme(disposable: Disposable): void {
78
+ this.themeBindings.push(disposable);
79
+ }
80
+
81
+ private disposeThemeBindings(): void {
82
+ if (this.themeBindingDisposed) {
83
+ return;
84
+ }
85
+ this.themeBindingDisposed = true;
86
+ disposeAll(this.themeBindings);
87
+ }
76
88
  }
@@ -1,12 +1,13 @@
1
- import { Application, Node } from "../../../../fui/Fui";
1
+ import { ManagedApplicationController, Node } from "../../fui/Fui";
2
2
  import { SettingsModel } from "./SettingsModel";
3
3
  import { SettingsView } from "./SettingsView";
4
4
 
5
- export class SettingsController {
5
+ export class SettingsController extends ManagedApplicationController {
6
6
  readonly model: SettingsModel = new SettingsModel();
7
7
  readonly view: SettingsView = new SettingsView(this.model);
8
8
 
9
9
  constructor() {
10
+ super();
10
11
  this.view.actionButton.onClickWith(this, (controller) => {
11
12
  controller.model.saveCount += 1;
12
13
  controller.view.setSaveCount(controller.model.saveCount);
@@ -17,11 +18,8 @@ export class SettingsController {
17
18
  return this.view.getRoot();
18
19
  }
19
20
 
20
- mount(): void {
21
- Application.mount(this.view.getRoot());
22
- }
23
-
24
21
  dispose(): void {
25
- Application.unmount();
22
+ this.view.dispose();
23
+ super.dispose();
26
24
  }
27
25
  }