@synopackageland/templates 0.41.1 → 0.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.41.2
4
+
5
+ - Align SDK/App references across native and Compose templates and refresh dependency locks
6
+ - Use frontend SDK ~0.53.1 and UI ~0.23.2 to allow compatible patch updates
7
+ - Document phone tab pages, preserved page titles and App branding, shared header action menus and the Package Center URL helper
8
+
3
9
  ## 0.41.1
4
10
 
5
11
  - Pin SDK/App 0.51.1 across Go, Python, Node.js, Rust and Compose templates
package/README.md CHANGED
@@ -83,13 +83,18 @@ The placeholders `__PACKAGE__` (also the Go module path), `__DISPLAY_NAME__` and
83
83
  `__BACKGROUND_ENABLED__` are substituted by `init`.
84
84
 
85
85
  `backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk`; that line is
86
- the single source of the SDK version and `frontend/package.json` pins
87
- `@synopackageland/sdk` to the same version. Python and Node.js name it in
86
+ the single source of the SDK version and `frontend/package.json` sets
87
+ `@synopackageland/sdk` to a patch range starting at the same version. Python and Node.js name it in
88
88
  `backend.sdk`, Rust in the `syno-pkg-land-app` tag of `backend/Cargo.toml`
89
- (and `Cargo.lock`). The Vue frontend pins a compatible `@synopackageland/ui`.
89
+ (and `Cargo.lock`). The Vue frontend uses a compatible patch range for
90
+ `@synopackageland/ui`. The shipped lockfile fixes both installed versions;
91
+ update it deliberately to take newer patches.
90
92
  Read the SDK's published peer dependency range with
91
93
  `npm view @synopackageland/sdk peerDependencies` rather than overriding it. Tests use jsdom 30 for UI floating-layer styles.
92
94
 
95
+ For an "install another package" link, use the SDK's
96
+ `packageCenterUrl(origin?)` helper to open DSM's Package Center.
97
+
93
98
  Interactive NAS file operations use same-origin browser calls.
94
99
  Custom file handlers use the SDK's
95
100
  [session transport migration](https://github.com/synopackageland/syno-pkg-land-sdk/blob/main/docs/dsm-session-transport.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/templates",
3
- "version": "0.41.1",
3
+ "version": "0.41.2",
4
4
  "description": "Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,7 +23,7 @@ compose:
23
23
  web: {service: web, port: 8080, health: {path: /, status: 200}}
24
24
  network: {egress: isolated}
25
25
  identity_gateway:
26
- sdk: 0.51.1
26
+ sdk: 0.53.1
27
27
  mode: none
28
28
  listen: {http: 28000}
29
29
  # Web Apps default to new-page through the shared app-launch resolver.
@@ -1,6 +1,6 @@
1
1
  Native Go package build dependencies
2
2
 
3
- github.com/synopackageland/syno-pkg-land-sdk v0.51.1 (MIT)
3
+ github.com/synopackageland/syno-pkg-land-sdk v0.53.1 (MIT)
4
4
 
5
5
  Copyright (c) 2026 synopkgland
6
6
 
@@ -2,6 +2,6 @@ module __PACKAGE__
2
2
 
3
3
  go 1.26.10
4
4
 
5
- require github.com/synopackageland/syno-pkg-land-sdk v0.51.1
5
+ require github.com/synopackageland/syno-pkg-land-sdk v0.53.1
6
6
 
7
7
  require golang.org/x/sys v0.44.0 // indirect
@@ -1,4 +1,4 @@
1
- github.com/synopackageland/syno-pkg-land-sdk v0.51.1 h1:5KkjKvLBVCSaUK+GFe40UUKMQ62NneoaCLEqEuqdzsw=
2
- github.com/synopackageland/syno-pkg-land-sdk v0.51.1/go.mod h1:A4ZvKEQnCMk5offv/57Zsf1Z/pV1RmylS9cMfDqdBMQ=
1
+ github.com/synopackageland/syno-pkg-land-sdk v0.53.1 h1:bgfWJRVjWWAofaIxRh4lhGrToPdhZ2AwbQp+YQMWqMo=
2
+ github.com/synopackageland/syno-pkg-land-sdk v0.53.1/go.mod h1:JMfenGwAhlVgGUtoSQFA9XRbtN8Zcs+0xdrOaaam+Fg=
3
3
  golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
4
4
  golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
@@ -3,7 +3,7 @@
3
3
  Edit `frontend/src/App.vue` and `main.ts` to build a DSM-window app with the
4
4
  public `@synopackageland/ui` components and embedded theme. The shell, sign-in,
5
5
  files, portal, jobs and delegate components come from `@synopackageland/sdk/vue`
6
- (types from `@synopackageland/sdk`); its version must equal the
6
+ (types from `@synopackageland/sdk`); its patch range starts at the
7
7
  `github.com/synopackageland/syno-pkg-land-sdk` version in `backend/go.mod`. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
@@ -77,10 +77,10 @@ Generated build and unit tests do not establish NAS runtime acceptance.
77
77
 
78
78
  The manifest is dsm-package.yaml schema 2: backend.kind: go and capabilities.files: session, jobs: true, identity: standalone, rootOps: true. Use --with login to declare standalone identity. Existing projects use synopkgland add <cap>...; app entrypoints stay app-owned, SDK conflicts are refused. check, inspect, build, deploy and status accept --json.
79
79
 
80
- Use the installed `@synopackageland/ui`; package.json pins its version.
80
+ Use the installed `@synopackageland/ui` and its declared compatible patch range.
81
81
  `App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
82
82
  `pages` from `shellPages`, the page's buttons in `#actions` and the pages in its
83
- default slot. The sidebar names the page: the shell shows no page title, puts
83
+ default slot. On desktop, the sidebar names the page: the shell shows no page title, puts
84
84
  `#actions` in a bar above the page and scrolls only the content; a settings
85
85
  page (`SectionTabs fill` or `SettingsPage`) keeps its tabs and footer in place
86
86
  with no CSS of its own. Given `ctx`, it follows the launch. In a DSM desktop window it
@@ -93,6 +93,21 @@ device-width viewport. Its full-page sidebar names the App with
93
93
  `ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
94
94
  shows the signed-in DSM account and its role from `ctx.user`. See the ui's
95
95
  `docs/adaptive-layout.md`.
96
+
97
+ The frontend SDK and UI use patch ranges; the lockfile records the installed
98
+ versions. Update the lockfile deliberately when taking a newer patch.
99
+
100
+ On a phone, a page with its own current tab in `#tabbar` has no repeated title
101
+ row. `AppShell` passes `phone-bar-title="none"` to `AppLayout` for that page,
102
+ while retaining the real page title and App name/icon from `ctx`. The drawer
103
+ keeps that branding, and the UI hides a bar with nothing in it. When using
104
+ `AppLayout` directly, pass the real `title`, `brand-name` and `brand-icon`
105
+ even with `phone-bar-title="none"`. Pages outside the tab bar and detail pages
106
+ with Back keep their title bar. `header-actions` share one "…" menu; only an
107
+ entry marked `primary: true`, or a sole entry with an icon, becomes a separate
108
+ bar button. The template's `#actions` stays page actions; no title or empty-bar
109
+ workaround is needed.
110
+
96
111
  `AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
97
112
  a full page as well, bounded at `height: 100%`) with the UI locale from
98
113
  `locale`; do not add another `AppRoot`.
@@ -7,8 +7,8 @@
7
7
  "name": "native-dsm-vue-app",
8
8
  "dependencies": {
9
9
  "@lucide/vue": "1.48.0",
10
- "@synopackageland/sdk": "~0.51.1",
11
- "@synopackageland/ui": "0.20.5",
10
+ "@synopackageland/sdk": "~0.53.1",
11
+ "@synopackageland/ui": "~0.23.2",
12
12
  "vue": "3.5.43"
13
13
  },
14
14
  "devDependencies": {
@@ -1174,13 +1174,13 @@
1174
1174
  "license": "Apache-2.0"
1175
1175
  },
1176
1176
  "node_modules/@synopackageland/sdk": {
1177
- "version": "0.51.1",
1178
- "resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.51.1.tgz",
1179
- "integrity": "sha512-X92IbEGzFCEPIqujZX3AfreWDks099zMdgAPZZoc3qG1ErGFssDqVYxhUVwEpj6zFGGgDapVTMOd5xBkUaraCA==",
1177
+ "version": "0.53.1",
1178
+ "resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.53.1.tgz",
1179
+ "integrity": "sha512-Riw45zAJW4L4aU7VxLT95q3NhG+zD2dIFsuiHyqPZizugeT3e2U4bckFRTMY8YPL5Nc0jz4Jk4MFpDSCnXddqw==",
1180
1180
  "license": "MIT",
1181
1181
  "peerDependencies": {
1182
1182
  "@lucide/vue": "^1.48.0",
1183
- "@synopackageland/ui": ">=0.20.5 <0.21.0",
1183
+ "@synopackageland/ui": ">=0.23.2 <0.24.0",
1184
1184
  "vue": "^3.5.0"
1185
1185
  },
1186
1186
  "peerDependenciesMeta": {
@@ -1193,9 +1193,9 @@
1193
1193
  }
1194
1194
  },
1195
1195
  "node_modules/@synopackageland/ui": {
1196
- "version": "0.20.5",
1197
- "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.20.5.tgz",
1198
- "integrity": "sha512-lyn3W+VvPu5BuZN1CMPZP3R7B1phUcqRzhmSmSJ8rubptPk1/wbfa91cLdYtRC9qKBBSnXlf6c9ibjkskf5qLQ==",
1196
+ "version": "0.23.2",
1197
+ "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.23.2.tgz",
1198
+ "integrity": "sha512-3qVl482czZERMsxd2M0ueP+Qy6INW1+OlaalX9RpUMxp6z5eydN/9s6HhT8SQZSX2dpboZg61R8djgQm9PqOuQ==",
1199
1199
  "license": "MIT",
1200
1200
  "dependencies": {
1201
1201
  "@floating-ui/dom": "1.8.0",
@@ -8,8 +8,8 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@lucide/vue": "1.48.0",
11
- "@synopackageland/sdk": "~0.51.1",
12
- "@synopackageland/ui": "0.20.5",
11
+ "@synopackageland/sdk": "~0.53.1",
12
+ "@synopackageland/ui": "~0.23.2",
13
13
  "vue": "3.5.43"
14
14
  },
15
15
  "devDependencies": {
@@ -49,7 +49,7 @@
49
49
  "license": "MIT"
50
50
  },
51
51
  "vendor/@synopackageland/app": {
52
- "version": "0.51.1",
52
+ "version": "0.53.1",
53
53
  "license": "MIT",
54
54
  "engines": {
55
55
  "node": ">=22"
@@ -49,7 +49,7 @@ backend:
49
49
  runtime: node
50
50
  dir: backend/app
51
51
  entry: main.js
52
- sdk: 0.51.1
52
+ sdk: 0.53.1
53
53
  node: { package: Node.js_v22 }
54
54
 
55
55
  ui:
@@ -4,7 +4,7 @@ The Vue shell is the same as the Go template's. Edit `frontend/src/App.vue`
4
4
  and `main.ts` to build a DSM-window app with the public `@synopackageland/ui`
5
5
  components and embedded theme. The shell, sign-in, files, portal, jobs and
6
6
  delegate components come from `@synopackageland/sdk/vue` (types from
7
- `@synopackageland/sdk`); its version must equal the `backend.sdk` in dsm-package.yaml. See
7
+ `@synopackageland/sdk`); its patch range starts at the `backend.sdk` in dsm-package.yaml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
10
  ## Local tests
@@ -43,10 +43,10 @@ background-jobs page and executor settings call the runtime's own browser
43
43
  methods (`bg.*`, `executor.*`); check the SDK release notes for the ones a
44
44
  bridge App's runtime serves.
45
45
 
46
- Use the installed `@synopackageland/ui`; package.json pins its version.
46
+ Use the installed `@synopackageland/ui` and its declared compatible patch range.
47
47
  `App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
48
48
  `pages` from `shellPages`, the page's buttons in `#actions` and the pages in its
49
- default slot. The sidebar names the page: the shell shows no page title, puts
49
+ default slot. On desktop, the sidebar names the page: the shell shows no page title, puts
50
50
  `#actions` in a bar above the page and scrolls only the content; a settings
51
51
  page (`SectionTabs fill` or `SettingsPage`) keeps its tabs and footer in place
52
52
  with no CSS of its own. Given `ctx`, it follows the launch. In a DSM desktop window it
@@ -59,6 +59,21 @@ device-width viewport. Its full-page sidebar names the App with
59
59
  `ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
60
60
  shows the signed-in DSM account and its role from `ctx.user`. See the ui's
61
61
  `docs/adaptive-layout.md`.
62
+
63
+ The frontend SDK and UI use patch ranges; the lockfile records the installed
64
+ versions. Update the lockfile deliberately when taking a newer patch.
65
+
66
+ On a phone, a page with its own current tab in `#tabbar` has no repeated title
67
+ row. `AppShell` passes `phone-bar-title="none"` to `AppLayout` for that page,
68
+ while retaining the real page title and App name/icon from `ctx`. The drawer
69
+ keeps that branding, and the UI hides a bar with nothing in it. When using
70
+ `AppLayout` directly, pass the real `title`, `brand-name` and `brand-icon`
71
+ even with `phone-bar-title="none"`. Pages outside the tab bar and detail pages
72
+ with Back keep their title bar. `header-actions` share one "…" menu; only an
73
+ entry marked `primary: true`, or a sole entry with an icon, becomes a separate
74
+ bar button. The template's `#actions` stays page actions; no title or empty-bar
75
+ workaround is needed.
76
+
62
77
  `AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
63
78
  a full page as well, bounded at `height: 100%`) with the UI locale from
64
79
  `locale`; do not add another `AppRoot`.
@@ -47,7 +47,7 @@ backend:
47
47
  runtime: python
48
48
  dir: backend/app
49
49
  entry: main.py
50
- sdk: 0.51.1
50
+ sdk: 0.53.1
51
51
 
52
52
  ui:
53
53
  # Non-UI-SDK frontends open a full page; Portal is a separate opt-in.
@@ -4,7 +4,7 @@ The Vue shell is the same as the Go template's. Edit `frontend/src/App.vue`
4
4
  and `main.ts` to build a DSM-window app with the public `@synopackageland/ui`
5
5
  components and embedded theme. The shell, sign-in, files, portal, jobs and
6
6
  delegate components come from `@synopackageland/sdk/vue` (types from
7
- `@synopackageland/sdk`); its version must equal the `backend.sdk` in dsm-package.yaml. See
7
+ `@synopackageland/sdk`); its patch range starts at the `backend.sdk` in dsm-package.yaml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
10
  ## Local tests
@@ -43,10 +43,10 @@ background-jobs page and executor settings call the runtime's own browser
43
43
  methods (`bg.*`, `executor.*`); check the SDK release notes for the ones a
44
44
  bridge App's runtime serves.
45
45
 
46
- Use the installed `@synopackageland/ui`; package.json pins its version.
46
+ Use the installed `@synopackageland/ui` and its declared compatible patch range.
47
47
  `App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
48
48
  `pages` from `shellPages`, the page's buttons in `#actions` and the pages in its
49
- default slot. The sidebar names the page: the shell shows no page title, puts
49
+ default slot. On desktop, the sidebar names the page: the shell shows no page title, puts
50
50
  `#actions` in a bar above the page and scrolls only the content; a settings
51
51
  page (`SectionTabs fill` or `SettingsPage`) keeps its tabs and footer in place
52
52
  with no CSS of its own. Given `ctx`, it follows the launch. In a DSM desktop window it
@@ -59,6 +59,21 @@ device-width viewport. Its full-page sidebar names the App with
59
59
  `ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
60
60
  shows the signed-in DSM account and its role from `ctx.user`. See the ui's
61
61
  `docs/adaptive-layout.md`.
62
+
63
+ The frontend SDK and UI use patch ranges; the lockfile records the installed
64
+ versions. Update the lockfile deliberately when taking a newer patch.
65
+
66
+ On a phone, a page with its own current tab in `#tabbar` has no repeated title
67
+ row. `AppShell` passes `phone-bar-title="none"` to `AppLayout` for that page,
68
+ while retaining the real page title and App name/icon from `ctx`. The drawer
69
+ keeps that branding, and the UI hides a bar with nothing in it. When using
70
+ `AppLayout` directly, pass the real `title`, `brand-name` and `brand-icon`
71
+ even with `phone-bar-title="none"`. Pages outside the tab bar and detail pages
72
+ with Back keep their title bar. `header-actions` share one "…" menu; only an
73
+ entry marked `primary: true`, or a sole entry with an icon, becomes a separate
74
+ bar button. The template's `#actions` stays page actions; no title or empty-bar
75
+ workaround is needed.
76
+
62
77
  `AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
63
78
  a full page as well, bounded at `height: 100%`) with the UI locale from
64
79
  `locale`; do not add another `AppRoot`.
@@ -104,8 +104,8 @@ dependencies = [
104
104
 
105
105
  [[package]]
106
106
  name = "syno-pkg-land-app"
107
- version = "0.51.1"
108
- source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.51.1#a5800f71d5de4bc4655865b96d2bd60419c676fd"
107
+ version = "0.53.1"
108
+ source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.53.1#67feea7b8c05f0bbb5e31b07e40be617691b9de9"
109
109
  dependencies = [
110
110
  "libc",
111
111
  "serde",
@@ -9,7 +9,7 @@ libc = "0.2"
9
9
  serde = { version = "1", features = ["derive"] }
10
10
  serde_json = "1"
11
11
  # The SDK version is the tag; `synopkgland check` reads it from Cargo.lock.
12
- syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.51.1" }
12
+ syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.53.1" }
13
13
 
14
14
  [profile.release]
15
15
  # A handler panic becomes an "internal" reply; abort would end the App without one.
@@ -170,8 +170,8 @@ dependencies = [
170
170
 
171
171
  [[package]]
172
172
  name = "syno-pkg-land-app"
173
- version = "0.51.1"
174
- source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.51.1#a5800f71d5de4bc4655865b96d2bd60419c676fd"
173
+ version = "0.53.1"
174
+ source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.53.1#67feea7b8c05f0bbb5e31b07e40be617691b9de9"
175
175
  dependencies = [
176
176
  "libc",
177
177
  "serde",
@@ -10,7 +10,7 @@ libc = "0.2"
10
10
  serde = { version = "1", features = ["derive"] }
11
11
  serde_json = "1"
12
12
  # The SDK version is the tag; `synopkgland check` reads it from Cargo.lock.
13
- syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.51.1" }
13
+ syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.53.1" }
14
14
 
15
15
  [profile.release]
16
16
  # A handler panic becomes an "internal" reply; abort would end the App without one.
@@ -4,7 +4,7 @@ The Vue shell is the same as the Go template's. Edit `frontend/src/App.vue`
4
4
  and `main.ts` to build a DSM-window app with the public `@synopackageland/ui`
5
5
  components and embedded theme. The shell, sign-in, files, portal, jobs and
6
6
  delegate components come from `@synopackageland/sdk/vue` (types from
7
- `@synopackageland/sdk`); its version must equal the `syno-pkg-land-app` tag in backend/Cargo.toml. See
7
+ `@synopackageland/sdk`); its patch range starts at the `syno-pkg-land-app` tag in backend/Cargo.toml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
10
  ## Local tests
@@ -43,10 +43,10 @@ background-jobs page and executor settings call the runtime's own browser
43
43
  methods (`bg.*`, `executor.*`); check the SDK release notes for the ones a
44
44
  bridge App's runtime serves.
45
45
 
46
- Use the installed `@synopackageland/ui`; package.json pins its version.
46
+ Use the installed `@synopackageland/ui` and its declared compatible patch range.
47
47
  `App.vue` lays the shell out with the SDK's `AppShell` (`@synopackageland/sdk/vue`):
48
48
  `pages` from `shellPages`, the page's buttons in `#actions` and the pages in its
49
- default slot. The sidebar names the page: the shell shows no page title, puts
49
+ default slot. On desktop, the sidebar names the page: the shell shows no page title, puts
50
50
  `#actions` in a bar above the page and scrolls only the content; a settings
51
51
  page (`SectionTabs fill` or `SettingsPage`) keeps its tabs and footer in place
52
52
  with no CSS of its own. Given `ctx`, it follows the launch. In a DSM desktop window it
@@ -59,6 +59,21 @@ device-width viewport. Its full-page sidebar names the App with
59
59
  `ctx.displayName` and `ctx.appIcon` (without them the page title and icon) and
60
60
  shows the signed-in DSM account and its role from `ctx.user`. See the ui's
61
61
  `docs/adaptive-layout.md`.
62
+
63
+ The frontend SDK and UI use patch ranges; the lockfile records the installed
64
+ versions. Update the lockfile deliberately when taking a newer patch.
65
+
66
+ On a phone, a page with its own current tab in `#tabbar` has no repeated title
67
+ row. `AppShell` passes `phone-bar-title="none"` to `AppLayout` for that page,
68
+ while retaining the real page title and App name/icon from `ctx`. The drawer
69
+ keeps that branding, and the UI hides a bar with nothing in it. When using
70
+ `AppLayout` directly, pass the real `title`, `brand-name` and `brand-icon`
71
+ even with `phone-bar-title="none"`. Pages outside the tab bar and detail pages
72
+ with Back keep their title bar. `header-actions` share one "…" menu; only an
73
+ entry marked `primary: true`, or a sole entry with an icon, becomes a separate
74
+ bar button. The template's `#actions` stays page actions; no title or empty-bar
75
+ workaround is needed.
76
+
62
77
  `AppShell`'s `app-root` wraps it in an app-local `AppRoot` (app modal scope, on
63
78
  a full page as well, bounded at `height: 100%`) with the UI locale from
64
79
  `locale`; do not add another `AppRoot`.