@effindomv2/create-fui-as-app 0.1.6 → 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 (63) 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/package.json +5 -4
  6. package/{build/templates → templates}/hello/package.json +2 -2
  7. package/{build/templates → templates}/hello/src/App.ts +1 -1
  8. package/{build/templates → templates}/hello/src/HelloWorld.ts +32 -21
  9. package/templates/hello/src/fui/Fui.ts +1 -0
  10. package/templates/hello/src/fui/FuiBrowser.ts +1 -0
  11. package/templates/hello/src/fui/FuiExports.ts +1 -0
  12. package/templates/hello/src/fui/FuiPrimitives.ts +1 -0
  13. package/{build/templates → templates}/mvc/package.json +2 -2
  14. package/templates/mvc/src/fui/Fui.ts +1 -0
  15. package/templates/mvc/src/fui/FuiBrowser.ts +1 -0
  16. package/templates/mvc/src/fui/FuiExports.ts +1 -0
  17. package/templates/mvc/src/fui/FuiPrimitives.ts +1 -0
  18. package/{build/templates → templates}/mvc/src/routes/HomeApp.ts +1 -0
  19. package/{build/templates → templates}/mvc/src/routes/SettingsApp.ts +1 -0
  20. package/{build/templates → templates}/mvc/src/routes/home/HomeController.ts +1 -0
  21. package/{build/templates → templates}/mvc/src/routes/home/HomeView.ts +32 -7
  22. package/{build/templates → templates}/mvc/src/routes/settings/SettingsController.ts +5 -0
  23. package/{build/templates → templates}/mvc/src/routes/settings/SettingsView.ts +30 -5
  24. package/templates/mvc/src/routes/shared/design-system/NavPill.ts +59 -0
  25. package/{build/templates → templates}/mvc/src/routes/shared/design-system/PrimaryButton.ts +1 -6
  26. package/build/templates/hello/src/fui/Fui.ts +0 -1
  27. package/build/templates/hello/src/fui/FuiBrowser.ts +0 -1
  28. package/build/templates/hello/src/fui/FuiExports.ts +0 -1
  29. package/build/templates/hello/src/fui/FuiPrimitives.ts +0 -1
  30. package/build/templates/mvc/src/fui/Fui.ts +0 -1
  31. package/build/templates/mvc/src/fui/FuiBrowser.ts +0 -1
  32. package/build/templates/mvc/src/fui/FuiExports.ts +0 -1
  33. package/build/templates/mvc/src/fui/FuiPrimitives.ts +0 -1
  34. package/build/templates/mvc/src/routes/shared/design-system/NavPill.ts +0 -42
  35. package/dist/scripts/sync-templates.d.ts +0 -1
  36. package/dist/scripts/sync-templates.js +0 -299
  37. /package/{build/templates → templates}/hello/README.md +0 -0
  38. /package/{build/templates → templates}/hello/asconfig.json +0 -0
  39. /package/{build/templates → templates}/hello/harness.ts +0 -0
  40. /package/{build/templates → templates}/hello/index.html +0 -0
  41. /package/{build/templates → templates}/hello/scripts/prepare-runtime.ts +0 -0
  42. /package/{build/templates → templates}/hello/scripts/smoke.ts +0 -0
  43. /package/{build/templates → templates}/hello/src/host/generated/HostEvents.ts +0 -0
  44. /package/{build/templates → templates}/hello/src/host/generated/HostServices.ts +0 -0
  45. /package/{build/templates → templates}/hello/src/host/host-events.ts +0 -0
  46. /package/{build/templates → templates}/hello/src/host/host-services.ts +0 -0
  47. /package/{build/templates → templates}/hello/tsconfig.json +0 -0
  48. /package/{build/templates → templates}/mvc/README.md +0 -0
  49. /package/{build/templates → templates}/mvc/asconfig.json +0 -0
  50. /package/{build/templates → templates}/mvc/harness.ts +0 -0
  51. /package/{build/templates → templates}/mvc/index.html +0 -0
  52. /package/{build/templates → templates}/mvc/route-shell.html +0 -0
  53. /package/{build/templates → templates}/mvc/scripts/prepare-runtime.ts +0 -0
  54. /package/{build/templates → templates}/mvc/scripts/smoke.ts +0 -0
  55. /package/{build/templates → templates}/mvc/src/host/generated/HostEvents.ts +0 -0
  56. /package/{build/templates → templates}/mvc/src/host/generated/HostServices.ts +0 -0
  57. /package/{build/templates → templates}/mvc/src/host/host-events.ts +0 -0
  58. /package/{build/templates → templates}/mvc/src/host/host-services.ts +0 -0
  59. /package/{build/templates → templates}/mvc/src/routes/home/HomeModel.ts +0 -0
  60. /package/{build/templates → templates}/mvc/src/routes/settings/SettingsModel.ts +0 -0
  61. /package/{build/templates → templates}/mvc/src/routes/shared/design-system/NavBar.ts +0 -0
  62. /package/{build/templates → templates}/mvc/src/routes/shared/routes.ts +0 -0
  63. /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.4";
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.4";
1
+ export const FUI_AS_VERSION = "latest";
2
2
  export const RUNTIME_VERSION = "0.1.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/create-fui-as-app",
3
- "version": "0.1.6",
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": {
@@ -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";
@@ -15,7 +15,7 @@
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
  }
@@ -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";
@@ -5,6 +5,7 @@ import { createManagedApplication } from "../fui/Fui";
5
5
  import { HomeController } from "./home/HomeController";
6
6
 
7
7
  const app = createManagedApplication<HomeController>(() => new HomeController());
8
+ app.useSystemTheme();
8
9
 
9
10
  export function __runApp(): void {
10
11
  app.run();
@@ -5,6 +5,7 @@ import { createManagedApplication } from "../fui/Fui";
5
5
  import { SettingsController } from "./settings/SettingsController";
6
6
 
7
7
  const app = createManagedApplication<SettingsController>(() => new SettingsController());
8
+ app.useSystemTheme();
8
9
 
9
10
  export function __runApp(): void {
10
11
  app.run();
@@ -47,6 +47,7 @@ export class HomeController extends ManagedApplicationController {
47
47
 
48
48
  dispose(): void {
49
49
  onAppClockTick(null);
50
+ this.view.dispose();
50
51
  super.dispose();
51
52
  }
52
53
  }
@@ -1,4 +1,4 @@
1
- import { Column, FlexBox, SelectionArea, Text, Unit, rgb } from "../../fui/Fui";
1
+ import { Column, Disposable, FlexBox, SelectionArea, Theme, activeTheme, bindTheme, disposeAll, Text, Unit } from "../../fui/Fui";
2
2
  import { createNavBar } from "../shared/design-system/NavBar";
3
3
  import { PrimaryButton } from "../shared/design-system/PrimaryButton";
4
4
  import { HomeModel } from "./HomeModel";
@@ -9,15 +9,17 @@ export class HomeView {
9
9
  private readonly hostServiceText: Text;
10
10
  private readonly hostEventText: Text;
11
11
  private readonly root!: SelectionArea;
12
+ private readonly themeBindings: Array<Disposable> = new Array<Disposable>();
13
+ private themeBindingDisposed: bool = false;
12
14
 
13
15
  constructor(model: HomeModel) {
14
16
  const navBar = createNavBar(true);
15
17
 
16
- const title = new Text(model.title).fontSize(34.0).textColor(rgb(241, 245, 249)) as Text;
17
- const subtitle = new Text(model.subtitle).fontSize(16.0).textColor(rgb(148, 163, 184)) as Text;
18
- this.statusText = new Text("Home counter: 0").fontSize(18.0).textColor(rgb(147, 197, 253)) as Text;
19
- this.hostServiceText = new Text("Host service time: -").fontSize(15.0).textColor(rgb(191, 219, 254)) as Text;
20
- this.hostEventText = new Text("Host event tick: -").fontSize(15.0).textColor(rgb(134, 239, 172)) as Text;
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;
21
23
  this.actionButton = new PrimaryButton(model.actionLabel);
22
24
 
23
25
  const content = Column(
@@ -41,14 +43,21 @@ export class HomeView {
41
43
  this.root = new SelectionArea()
42
44
  .fillWidth()
43
45
  .fillHeight()
44
- .bgColor(rgb(2, 6, 23))
45
46
  .child(content) as SelectionArea;
47
+ this.trackTheme(bindTheme(this, (view, theme): void => {
48
+ view.applyTheme(theme);
49
+ }));
50
+ this.applyTheme(activeTheme.value);
46
51
  }
47
52
 
48
53
  getRoot(): SelectionArea {
49
54
  return this.root;
50
55
  }
51
56
 
57
+ dispose(): void {
58
+ this.disposeThemeBindings();
59
+ }
60
+
52
61
  setActionCount(count: i32): void {
53
62
  this.statusText.text("Home counter: " + count.toString());
54
63
  }
@@ -60,4 +69,20 @@ export class HomeView {
60
69
  setHostEventSeconds(value: i32): void {
61
70
  this.hostEventText.text("Host event tick: " + value.toString());
62
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
+ }
63
88
  }
@@ -17,4 +17,9 @@ export class SettingsController extends ManagedApplicationController {
17
17
  getRoot(): Node {
18
18
  return this.view.getRoot();
19
19
  }
20
+
21
+ dispose(): void {
22
+ this.view.dispose();
23
+ super.dispose();
24
+ }
20
25
  }
@@ -1,4 +1,4 @@
1
- import { Column, FlexBox, SelectionArea, Text, Unit, rgb } from "../../fui/Fui";
1
+ import { Column, Disposable, FlexBox, SelectionArea, Theme, activeTheme, bindTheme, disposeAll, Text, Unit } from "../../fui/Fui";
2
2
  import { createNavBar } from "../shared/design-system/NavBar";
3
3
  import { PrimaryButton } from "../shared/design-system/PrimaryButton";
4
4
  import { SettingsModel } from "./SettingsModel";
@@ -7,13 +7,15 @@ export class SettingsView {
7
7
  readonly actionButton: PrimaryButton;
8
8
  private readonly statusText: Text;
9
9
  private readonly root!: SelectionArea;
10
+ private readonly themeBindings: Array<Disposable> = new Array<Disposable>();
11
+ private themeBindingDisposed: bool = false;
10
12
 
11
13
  constructor(model: SettingsModel) {
12
14
  const navBar = createNavBar(false);
13
15
 
14
- const title = new Text(model.title).fontSize(34.0).textColor(rgb(241, 245, 249)) as Text;
15
- const subtitle = new Text(model.subtitle).fontSize(16.0).textColor(rgb(148, 163, 184)) as Text;
16
- this.statusText = new Text("Settings saved: 0").fontSize(18.0).textColor(rgb(134, 239, 172)) as Text;
16
+ const title = new Text(model.title).fontSize(34.0) as Text;
17
+ const subtitle = new Text(model.subtitle).fontSize(16.0) as Text;
18
+ this.statusText = new Text("Settings saved: 0").fontSize(18.0) as Text;
17
19
  this.actionButton = new PrimaryButton(model.actionLabel);
18
20
 
19
21
  const content = Column(
@@ -34,15 +36,38 @@ export class SettingsView {
34
36
  this.root = new SelectionArea()
35
37
  .fillWidth()
36
38
  .fillHeight()
37
- .bgColor(rgb(2, 6, 23))
38
39
  .child(content) as SelectionArea;
40
+ this.trackTheme(bindTheme(this, (view, theme): void => {
41
+ view.applyTheme(theme);
42
+ }));
43
+ this.applyTheme(activeTheme.value);
39
44
  }
40
45
 
41
46
  getRoot(): SelectionArea {
42
47
  return this.root;
43
48
  }
44
49
 
50
+ dispose(): void {
51
+ this.disposeThemeBindings();
52
+ }
53
+
45
54
  setSaveCount(count: i32): void {
46
55
  this.statusText.text("Settings saved: " + count.toString());
47
56
  }
57
+
58
+ private applyTheme(theme: Theme): void {
59
+ this.root.bgColor(theme.colors.background);
60
+ }
61
+
62
+ private trackTheme(disposable: Disposable): void {
63
+ this.themeBindings.push(disposable);
64
+ }
65
+
66
+ private disposeThemeBindings(): void {
67
+ if (this.themeBindingDisposed) {
68
+ return;
69
+ }
70
+ this.themeBindingDisposed = true;
71
+ disposeAll(this.themeBindings);
72
+ }
48
73
  }
@@ -0,0 +1,59 @@
1
+ import { Disposable, Theme, activeTheme, bindTheme, disposeAll, NavLink, Text } from "../../../fui/Fui";
2
+
3
+ const PILL_RADIUS: f32 = 999.0;
4
+ const PILL_PADDING_X: f32 = 16.0;
5
+ const PILL_PADDING_Y: f32 = 8.0;
6
+ export class NavPill extends NavLink {
7
+ private readonly labelNode: Text;
8
+ private readonly themeBindings: Array<Disposable> = new Array<Disposable>();
9
+ private themeBindingDisposed: bool = false;
10
+ private activeValue: bool = false;
11
+
12
+ constructor(href: string, label: string) {
13
+ super(href, label, false);
14
+ this.labelNode = new Text(label)
15
+ .fontSize(14.0)
16
+ .selectable(false) as Text;
17
+ this
18
+ .cornerRadius(PILL_RADIUS)
19
+ .padding(PILL_PADDING_X, PILL_PADDING_Y, PILL_PADDING_X, PILL_PADDING_Y)
20
+ .child(this.labelNode);
21
+ this.trackTheme(bindTheme(this, (pill, theme): void => {
22
+ pill.applyVisualState(theme);
23
+ }));
24
+ this.applyVisualState(activeTheme.value);
25
+ }
26
+
27
+ active(flag: bool = true): this {
28
+ this.activeValue = flag;
29
+ this.applyVisualState(activeTheme.value);
30
+ return this;
31
+ }
32
+
33
+ dispose(): void {
34
+ this.disposeThemeBindings();
35
+ super.dispose();
36
+ }
37
+
38
+ private applyVisualState(theme: Theme): void {
39
+ if (this.activeValue) {
40
+ this.bgColor(theme.colors.accent);
41
+ this.labelNode.textColor(theme.colors.textPrimary);
42
+ return;
43
+ }
44
+ this.bgColor(theme.colors.surface);
45
+ this.labelNode.textColor(theme.colors.textMuted);
46
+ }
47
+
48
+ private trackTheme(disposable: Disposable): void {
49
+ this.themeBindings.push(disposable);
50
+ }
51
+
52
+ private disposeThemeBindings(): void {
53
+ if (this.themeBindingDisposed) {
54
+ return;
55
+ }
56
+ this.themeBindingDisposed = true;
57
+ disposeAll(this.themeBindings);
58
+ }
59
+ }
@@ -1,19 +1,14 @@
1
- import { Button, rgb } from "../../../fui/Fui";
1
+ import { Button } from "../../../fui/Fui";
2
2
 
3
3
  const BUTTON_RADIUS: f32 = 12.0;
4
4
  const BUTTON_PADDING_X: f32 = 18.0;
5
5
  const BUTTON_PADDING_Y: f32 = 10.0;
6
- const BUTTON_BG: u32 = rgb(59, 130, 246);
7
- const BUTTON_TEXT: u32 = rgb(241, 245, 249);
8
-
9
6
  export class PrimaryButton extends Button {
10
7
  constructor(label: string) {
11
8
  super(label);
12
9
  this
13
10
  .cornerRadius(BUTTON_RADIUS)
14
11
  .padding(BUTTON_PADDING_X, BUTTON_PADDING_Y, BUTTON_PADDING_X, BUTTON_PADDING_Y)
15
- .bgColor(BUTTON_BG)
16
- .textColor(BUTTON_TEXT)
17
12
  .fontSize(14.0);
18
13
  }
19
14
  }
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/Fui";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/browser/src/index";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/FuiExports";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/FuiPrimitives";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/Fui";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/browser/src/index";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/FuiExports";
@@ -1 +0,0 @@
1
- export * from "../../node_modules/@effindomv2/fui-as/src/FuiPrimitives";
@@ -1,42 +0,0 @@
1
- import { NavLink, Text, rgb } from "../../../fui/Fui";
2
-
3
- const PILL_RADIUS: f32 = 999.0;
4
- const PILL_PADDING_X: f32 = 16.0;
5
- const PILL_PADDING_Y: f32 = 8.0;
6
- const PILL_INACTIVE_BG: u32 = rgb(33, 45, 67);
7
- const PILL_ACTIVE_BG: u32 = rgb(34, 197, 94);
8
- const PILL_INACTIVE_TEXT: u32 = rgb(226, 232, 240);
9
- const PILL_ACTIVE_TEXT: u32 = rgb(12, 16, 24);
10
-
11
- export class NavPill extends NavLink {
12
- private readonly labelNode: Text;
13
- private activeValue: bool = false;
14
-
15
- constructor(href: string, label: string) {
16
- super(href, label, false);
17
- this.labelNode = new Text(label)
18
- .fontSize(14.0)
19
- .selectable(false) as Text;
20
- this
21
- .cornerRadius(PILL_RADIUS)
22
- .padding(PILL_PADDING_X, PILL_PADDING_Y, PILL_PADDING_X, PILL_PADDING_Y)
23
- .child(this.labelNode);
24
- this.applyVisualState();
25
- }
26
-
27
- active(flag: bool = true): this {
28
- this.activeValue = flag;
29
- this.applyVisualState();
30
- return this;
31
- }
32
-
33
- private applyVisualState(): void {
34
- if (this.activeValue) {
35
- this.bgColor(PILL_ACTIVE_BG);
36
- this.labelNode.textColor(PILL_ACTIVE_TEXT);
37
- return;
38
- }
39
- this.bgColor(PILL_INACTIVE_BG);
40
- this.labelNode.textColor(PILL_INACTIVE_TEXT);
41
- }
42
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,299 +0,0 @@
1
- import { cpSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { dirname, join, relative, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
5
- const REPO_ROOT = resolve(PACKAGE_ROOT, "..", "..");
6
- const FUI_AS_ROOT = join(REPO_ROOT, "v2", "fui-as");
7
- const FUI_AS_TEMPLATES_ROOT = join(FUI_AS_ROOT, "templates");
8
- const TEMPLATES_ROOT = join(PACKAGE_ROOT, "build", "templates");
9
- const LEGACY_TEMPLATES_ROOT = join(PACKAGE_ROOT, "templates");
10
- function writeTextFile(filePath, contents) {
11
- mkdirSync(dirname(filePath), { recursive: true });
12
- writeFileSync(filePath, contents, "utf8");
13
- }
14
- function posixRelativeImport(fromFilePath, toFilePath) {
15
- const fromDirectory = dirname(fromFilePath);
16
- let specifier = relative(fromDirectory, toFilePath).replaceAll("\\", "/");
17
- if (specifier.endsWith(".ts")) {
18
- specifier = specifier.slice(0, -3);
19
- }
20
- if (!specifier.startsWith(".")) {
21
- specifier = `./${specifier}`;
22
- }
23
- return specifier;
24
- }
25
- function rewriteSdkImportsInFile(filePath, templateSrcRoot) {
26
- const original = readFileSync(filePath, "utf8");
27
- const rewritten = original
28
- .replace(/(['"])(?:\.\.\/)+src\/(Fui|FuiExports|FuiPrimitives|FuiBrowser)\1/g, (_full, quote, symbolName) => {
29
- const targetPath = join(templateSrcRoot, "fui", `${symbolName}.ts`);
30
- const relativeSpecifier = posixRelativeImport(filePath, targetPath);
31
- return `${quote}${relativeSpecifier}${quote}`;
32
- })
33
- .replace(/(['"])(?:\.\.\/)+browser\/src\/(?:host-events|host-services)\1/g, '"@effindomv2/fui-as/browser"');
34
- if (rewritten !== original) {
35
- writeFileSync(filePath, rewritten, "utf8");
36
- }
37
- }
38
- function walkFiles(root, callback) {
39
- for (const entry of readdirSync(root)) {
40
- const absolutePath = join(root, entry);
41
- const stats = statSync(absolutePath);
42
- if (stats.isDirectory()) {
43
- walkFiles(absolutePath, callback);
44
- continue;
45
- }
46
- callback(absolutePath);
47
- }
48
- }
49
- function writeSharedSdkShims(templateRoot) {
50
- const fuiRoot = join(templateRoot, "src", "fui");
51
- writeTextFile(join(fuiRoot, "Fui.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/Fui";\n');
52
- writeTextFile(join(fuiRoot, "FuiExports.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/FuiExports";\n');
53
- writeTextFile(join(fuiRoot, "FuiPrimitives.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/FuiPrimitives";\n');
54
- writeTextFile(join(fuiRoot, "FuiBrowser.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/browser/src/index";\n');
55
- }
56
- function writeAsconfig(templateRoot) {
57
- writeTextFile(join(templateRoot, "asconfig.json"), JSON.stringify({
58
- targets: {
59
- debug: {
60
- debug: true,
61
- exportRuntime: true,
62
- bindings: "esm",
63
- outFile: "public/app.wasm",
64
- sourceMap: true,
65
- textFile: "public/app.wat",
66
- },
67
- release: {
68
- exportRuntime: true,
69
- bindings: "esm",
70
- optimizeLevel: 3,
71
- outFile: "public/app.wasm",
72
- shrinkLevel: 1,
73
- sourceMap: false,
74
- textFile: "public/app.wat",
75
- },
76
- },
77
- options: {
78
- runtime: "stub",
79
- },
80
- }, null, 2) + "\n");
81
- }
82
- function writeTsconfig(templateRoot) {
83
- writeTextFile(join(templateRoot, "tsconfig.json"), JSON.stringify({
84
- extends: "assemblyscript/std/assembly.json",
85
- compilerOptions: {
86
- noEmit: true,
87
- },
88
- include: ["src/**/*.ts"],
89
- }, null, 2) + "\n");
90
- }
91
- function writePackageJsonTemplate(templateRoot, scripts, description) {
92
- writeTextFile(join(templateRoot, "package.json"), JSON.stringify({
93
- name: "__PACKAGE_NAME__",
94
- version: "0.1.0",
95
- private: true,
96
- type: "module",
97
- description,
98
- scripts,
99
- dependencies: {
100
- "@effindomv2/fui-as": "__FUI_AS_VERSION__",
101
- "@effindomv2/runtime": "__RUNTIME_VERSION__",
102
- },
103
- devDependencies: {
104
- assemblyscript: "0.28.17",
105
- "chokidar-cli": "^3.0.0",
106
- concurrently: "^9.2.1",
107
- esbuild: "^0.27.7",
108
- "http-server": "^14.1.1",
109
- tsx: "^4.20.6",
110
- },
111
- }, null, 2) + "\n");
112
- }
113
- function writeHelloSupportFiles(templateRoot) {
114
- writeTextFile(join(templateRoot, "scripts", "prepare-runtime.ts"), `import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
115
-
116
- const outputDir = "public";
117
- rmSync(outputDir, { recursive: true, force: true });
118
- mkdirSync(\`\${outputDir}/runtime\`, { recursive: true });
119
-
120
- cpSync("node_modules/@effindomv2/runtime/dist", \`\${outputDir}/runtime/dist\`, { recursive: true });
121
- cpSync("node_modules/@effindomv2/runtime/dist/fonts", \`\${outputDir}/runtime/fonts\`, { recursive: true });
122
- copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js", \`\${outputDir}/bridge.js\`);
123
- if (existsSync("node_modules/@effindomv2/runtime/dist/bridge.js.map")) {
124
- copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js.map", \`\${outputDir}/bridge.js.map\`);
125
- }
126
- writeFileSync(
127
- \`\${outputDir}/effindom-runtime-config.js\`,
128
- 'window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, { manifestUrl: "./runtime/dist/effindom.v2.manifest.json" });\\n',
129
- "utf8",
130
- );
131
- copyFileSync("index.html", \`\${outputDir}/index.html\`);
132
- `);
133
- writeTextFile(join(templateRoot, "scripts", "smoke.ts"), `import { accessSync } from "node:fs";
134
-
135
- const expectedFiles = [
136
- "public/index.html",
137
- "public/harness.js",
138
- "public/app.wasm",
139
- "public/bridge.js",
140
- "public/effindom-runtime-config.js",
141
- "public/runtime/dist/effindom.v2.manifest.json",
142
- "public/runtime/fonts/NotoSans-Regular.ttf",
143
- ];
144
-
145
- for (const filePath of expectedFiles) {
146
- accessSync(filePath);
147
- }
148
- `);
149
- writePackageJsonTemplate(templateRoot, {
150
- build: "npm run generate:host && npm run build:assets && npm run build:wasm && npm run build:harness",
151
- "build:assets": "tsx scripts/prepare-runtime.ts",
152
- "build:wasm": "asc src/App.ts --config asconfig.json --target release",
153
- "build:harness": "esbuild harness.ts --bundle --format=esm --platform=browser --outfile=public/harness.js",
154
- "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",
155
- "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",
156
- "generate:host": "npm run generate:host-services && npm run generate:host-events",
157
- watch: 'chokidar "src/**/*.ts" "harness.ts" "index.html" "asconfig.json" --ignore "src/host/generated/**" -c "npm run build"',
158
- serve: "http-server public -p 8080 -c-1",
159
- dev: 'npm run build && concurrently -k -n watch,serve "npm run watch" "npm run serve"',
160
- test: "npm run build && tsx scripts/smoke.ts",
161
- }, "Scaffolded FUI-AS hello-world app");
162
- }
163
- function writeMvcSupportFiles(templateRoot) {
164
- writeTextFile(join(templateRoot, "index.html"), `<!doctype html>
165
- <html lang="en">
166
- <head>
167
- <meta charset="utf-8" />
168
- <meta http-equiv="refresh" content="0; url=/home/" />
169
- <title>FUI-AS Routed App</title>
170
- </head>
171
- <body>
172
- <p>Redirecting to <a href="/home/">/home/</a>…</p>
173
- </body>
174
- </html>
175
- `);
176
- writeTextFile(join(templateRoot, "scripts", "prepare-runtime.ts"), `import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
177
-
178
- const outputDir = "public";
179
- rmSync(outputDir, { recursive: true, force: true });
180
- mkdirSync(\`\${outputDir}/runtime\`, { recursive: true });
181
- mkdirSync(\`\${outputDir}/home\`, { recursive: true });
182
- mkdirSync(\`\${outputDir}/settings\`, { recursive: true });
183
-
184
- cpSync("node_modules/@effindomv2/runtime/dist", \`\${outputDir}/runtime/dist\`, { recursive: true });
185
- cpSync("node_modules/@effindomv2/runtime/dist/fonts", \`\${outputDir}/runtime/fonts\`, { recursive: true });
186
- copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js", \`\${outputDir}/bridge.js\`);
187
- if (existsSync("node_modules/@effindomv2/runtime/dist/bridge.js.map")) {
188
- copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js.map", \`\${outputDir}/bridge.js.map\`);
189
- }
190
- writeFileSync(
191
- \`\${outputDir}/effindom-runtime-config.js\`,
192
- 'window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, { manifestUrl: "./runtime/dist/effindom.v2.manifest.json" });\\n',
193
- "utf8",
194
- );
195
- copyFileSync("index.html", \`\${outputDir}/index.html\`);
196
- copyFileSync("route-shell.html", \`\${outputDir}/home/index.html\`);
197
- copyFileSync("route-shell.html", \`\${outputDir}/settings/index.html\`);
198
- `);
199
- writeTextFile(join(templateRoot, "scripts", "smoke.ts"), `import { accessSync } from "node:fs";
200
-
201
- const expectedFiles = [
202
- "public/index.html",
203
- "public/harness.js",
204
- "public/home/index.html",
205
- "public/settings/index.html",
206
- "public/home.wasm",
207
- "public/settings.wasm",
208
- "public/bridge.js",
209
- "public/effindom-runtime-config.js",
210
- "public/runtime/dist/effindom.v2.manifest.json",
211
- "public/runtime/fonts/NotoSans-Regular.ttf",
212
- ];
213
-
214
- for (const filePath of expectedFiles) {
215
- accessSync(filePath);
216
- }
217
- `);
218
- writePackageJsonTemplate(templateRoot, {
219
- build: "npm run generate:host && npm run build:assets && npm run build:wasm && npm run build:harness",
220
- "build:assets": "tsx scripts/prepare-runtime.ts",
221
- "build:wasm": "npm run build:wasm:home && npm run build:wasm:settings",
222
- "build:wasm:home": "asc src/routes/HomeApp.ts --config asconfig.json --target release --outFile public/home.wasm",
223
- "build:wasm:settings": "asc src/routes/SettingsApp.ts --config asconfig.json --target release --outFile public/settings.wasm",
224
- "build:harness": "esbuild harness.ts --bundle --format=esm --platform=browser --outfile=public/harness.js",
225
- "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",
226
- "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",
227
- "generate:host": "npm run generate:host-services && npm run generate:host-events",
228
- watch: 'chokidar "src/**/*.ts" "harness.ts" "route-shell.html" "index.html" "asconfig.json" --ignore "src/host/generated/**" -c "npm run build"',
229
- serve: "http-server public -p 8080 -c-1",
230
- dev: 'npm run build && concurrently -k -n watch,serve "npm run watch" "npm run serve"',
231
- test: "npm run build && tsx scripts/smoke.ts",
232
- }, "Scaffolded FUI-AS routed app");
233
- }
234
- function rewriteHarnessImports(filePath) {
235
- const original = readFileSync(filePath, "utf8");
236
- const rewritten = original
237
- .replace(/(['"])(?:\.\.\/)+browser\/src\/common-harness\1/g, '"@effindomv2/fui-as/browser"')
238
- .replace(/(['"])(?:\.\.\/)+browser\/src\/routed-harness\1/g, '"@effindomv2/fui-as/browser"')
239
- .replaceAll("?v=midnight-6", "");
240
- if (rewritten !== original) {
241
- writeFileSync(filePath, rewritten, "utf8");
242
- }
243
- }
244
- function normalizeGeneratedHeader(filePath, label) {
245
- const original = readFileSync(filePath, "utf8");
246
- const rewritten = original.replace(/^\/\/ Generated by .*$/m, `// Generated from ${label}.`);
247
- if (rewritten !== original) {
248
- writeFileSync(filePath, rewritten, "utf8");
249
- }
250
- }
251
- function syncHelloTemplate() {
252
- const templateRoot = join(TEMPLATES_ROOT, "hello");
253
- const templateSrcRoot = join(templateRoot, "src");
254
- rmSync(templateRoot, { recursive: true, force: true });
255
- const sourceRoot = join(FUI_AS_TEMPLATES_ROOT, "demo-hello-world");
256
- cpSync(join(sourceRoot, "src"), templateSrcRoot, { recursive: true });
257
- cpSync(join(sourceRoot, "harness.ts"), join(templateRoot, "harness.ts"));
258
- cpSync(join(sourceRoot, "index.html"), join(templateRoot, "index.html"));
259
- cpSync(join(sourceRoot, "README.md"), join(templateRoot, "README.md"));
260
- writeSharedSdkShims(templateRoot);
261
- writeAsconfig(templateRoot);
262
- writeTsconfig(templateRoot);
263
- writeHelloSupportFiles(templateRoot);
264
- walkFiles(templateSrcRoot, (filePath) => {
265
- if (filePath.endsWith(".ts")) {
266
- rewriteSdkImportsInFile(filePath, templateSrcRoot);
267
- }
268
- });
269
- rewriteHarnessImports(join(templateRoot, "harness.ts"));
270
- normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostEvents.ts"), "the scaffold host-events definition");
271
- normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostServices.ts"), "the scaffold host-services definition");
272
- }
273
- function syncMvcTemplate() {
274
- const templateRoot = join(TEMPLATES_ROOT, "mvc");
275
- const templateSrcRoot = join(templateRoot, "src");
276
- rmSync(templateRoot, { recursive: true, force: true });
277
- const sourceRoot = join(FUI_AS_TEMPLATES_ROOT, "demo-mvc");
278
- cpSync(join(sourceRoot, "src"), templateSrcRoot, { recursive: true });
279
- cpSync(join(sourceRoot, "harness.ts"), join(templateRoot, "harness.ts"));
280
- cpSync(join(sourceRoot, "route-shell.html"), join(templateRoot, "route-shell.html"));
281
- cpSync(join(sourceRoot, "README.md"), join(templateRoot, "README.md"));
282
- writeSharedSdkShims(templateRoot);
283
- writeAsconfig(templateRoot);
284
- writeTsconfig(templateRoot);
285
- writeMvcSupportFiles(templateRoot);
286
- walkFiles(templateSrcRoot, (filePath) => {
287
- if (filePath.endsWith(".ts")) {
288
- rewriteSdkImportsInFile(filePath, templateSrcRoot);
289
- }
290
- });
291
- rewriteHarnessImports(join(templateRoot, "harness.ts"));
292
- normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostEvents.ts"), "the scaffold host-events definition");
293
- normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostServices.ts"), "the scaffold host-services definition");
294
- }
295
- rmSync(TEMPLATES_ROOT, { recursive: true, force: true });
296
- rmSync(LEGACY_TEMPLATES_ROOT, { recursive: true, force: true });
297
- mkdirSync(TEMPLATES_ROOT, { recursive: true });
298
- syncHelloTemplate();
299
- syncMvcTemplate();
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes