@betterportal/theme-bootstrap1 10.1.42 → 10.1.44
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/bp-resources/SKILL.md +5 -4
- package/bp-resources/ui-guide.md +2 -0
- package/lib/bp-contracts/org.betterportal.theme.bootstrap1.json +34 -4
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.d.ts +3 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.js +33 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.js.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/adapter.js +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/assets.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/assets.js +4 -5
- package/lib/plugins/service-betterportal-theme-bootstrap1/assets.js.map +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/index.d.ts +0 -4
- package/lib/plugins/service-betterportal-theme-bootstrap1/index.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/index.js +4 -104
- package/lib/plugins/service-betterportal-theme-bootstrap1/index.js.map +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_footer/index.d.ts +6 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_footer/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_footer/index.js +7 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_footer/index.js.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_nav/index.d.ts +6 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_nav/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_nav/index.js +7 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_nav/index.js.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_theme-selector.d.ts +6 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_theme-selector.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_theme-selector.js +7 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/_theme-selector.js.map +1 -0
- package/lib/plugins/service-betterportal-theme-bootstrap1/{theme → shell}/index.d.ts +2 -9
- package/lib/plugins/service-betterportal-theme-bootstrap1/{theme → shell}/index.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-theme-bootstrap1/{theme → shell}/index.js +3 -13
- package/lib/plugins/service-betterportal-theme-bootstrap1/shell/index.js.map +1 -0
- package/lib/schemas/service-betterportal-theme-bootstrap1.json +1 -8
- package/lib/schemas/service-betterportal-theme-bootstrap1.plugin.json +1 -8
- package/package.json +11 -6
- package/lib/plugins/service-betterportal-theme-bootstrap1/theme/index.js.map +0 -1
package/bp-resources/SKILL.md
CHANGED
|
@@ -9,9 +9,10 @@ description: Build BetterPortal service UI that fits the active Bootstrap1 shell
|
|
|
9
9
|
2. Identify the service view, methods, schemas, required permissions and stable `viewId`.
|
|
10
10
|
3. Render server-side JSX/HTMX for the `bootstrap1` theme. Return only service content, never the shell.
|
|
11
11
|
4. Use `ctx.routeUrl()` for service requests and `ctx.uiRouteUrl()` only for GET page navigation.
|
|
12
|
-
5.
|
|
13
|
-
6.
|
|
14
|
-
7.
|
|
15
|
-
8.
|
|
12
|
+
5. Use the framework `BPElement` JSX helper with `ctx` for cross-service or active-shell fragments. Reference a declared dependency alias, never a service UUID or absolute URL. Keep loading/error/status UI inside `bp-loading`, `bp-status`, and `bp-nok`; an explicit `bp-ok` must have exactly one `<template />`.
|
|
13
|
+
6. Prefer Bootstrap components and native HTML controls. Add custom CSS or JavaScript only when the theme and platform primitives cannot express the behavior.
|
|
14
|
+
7. Include loading, empty, validation, forbidden and service-unavailable states.
|
|
15
|
+
8. Check keyboard operation, visible labels, heading order and responsive wrapping.
|
|
16
|
+
9. Verify JSON and HTML representations against the published schemas.
|
|
16
17
|
|
|
17
18
|
For side tasks, use `data-bp-sidebar` and `data-bp-sidebar-open`. For authenticated downloads, use `hx-download`. For live refresh, emit an `HX-Trigger` event and subscribe with `hx-trigger="event from:body"`.
|
package/bp-resources/ui-guide.md
CHANGED
|
@@ -14,6 +14,8 @@ Use Bootstrap 5 classes and server-rendered JSX/HTMX. BetterPortal service rende
|
|
|
14
14
|
|
|
15
15
|
- Use real `<form>` elements and labelled controls. Validation text uses `.invalid-feedback` or `.form-text`.
|
|
16
16
|
- Use `ctx.routeUrl()` for form actions, HTMX calls, downloads and SSE. `ctx.uiRouteUrl()` is only for GET navigation through a mounted page route.
|
|
17
|
+
- Use `<BPElement ctx={ctx} service="dependency-alias" path="/contract/path" fragment="location.id">` for cross-service UI fragments. Never put service UUIDs or absolute service URLs in renderer code; the server consumes `ctx` and resolves the app-mounted provider.
|
|
18
|
+
- Use `service="shell"` and a singular shell fragment id to reuse active-shell UI such as `theme-selector`. Add `bp-loading`, optional `bp-ok` with exactly one `<template />`, status-specific `bp-status`, and `bp-nok` states as needed.
|
|
17
19
|
- Mutations should return a useful fragment and emit an `HX-Trigger` event for passive regions that need to reload.
|
|
18
20
|
- Disable buttons only while their own request is active. Keep a visible loading indicator and an error fragment.
|
|
19
21
|
|
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
"pluginId": "org.betterportal.theme.bootstrap1",
|
|
5
5
|
"title": "Bootstrap1 Theme",
|
|
6
6
|
"description": "Bootstrap 5 + htmx theme that renders BetterPortal app shells.",
|
|
7
|
-
"version": "10.1.
|
|
7
|
+
"version": "10.1.44",
|
|
8
8
|
"category": "theme",
|
|
9
9
|
"deploymentModes": [
|
|
10
10
|
"self-hosted"
|
|
11
11
|
],
|
|
12
12
|
"capabilities": [
|
|
13
13
|
"theme",
|
|
14
|
+
"renderer.bootstrap5",
|
|
14
15
|
"view.json",
|
|
15
16
|
"view.metadata"
|
|
16
17
|
],
|
|
17
|
-
"
|
|
18
|
+
"supportedRenderers": [
|
|
19
|
+
"bootstrap5"
|
|
20
|
+
],
|
|
18
21
|
"supportedRenderModes": [],
|
|
19
22
|
"views": [],
|
|
20
23
|
"configSchemas": [
|
|
@@ -246,7 +249,7 @@
|
|
|
246
249
|
"title": "Bootstrap1 UI guide",
|
|
247
250
|
"description": "Layout, component, HTMX, responsive and accessibility rules.",
|
|
248
251
|
"mediaType": "text/markdown; charset=utf-8",
|
|
249
|
-
"content": "# Bootstrap1 UI guide\n\nUse Bootstrap 5 classes and server-rendered JSX/HTMX. BetterPortal service renderers return fragments; the theme owns the shell, navigation, visible URL and global assets.\n\n## Layout\n\n- Render primary content for the `main` slot. Never emit another page shell, `<html>`, `<head>`, sidebar navigation or top bar.\n- Start pages with a semantic heading and a short action row when actions exist.\n- Use `.container-fluid`, Bootstrap grid classes and responsive gaps. Avoid fixed widths except a declared `data-bp-sidebar-width`.\n- Use cards for grouped settings, tables for comparable records and list groups for short navigation or status collections.\n- Keep primary actions on the right of desktop action rows and allow them to wrap on small screens.\n\n## Forms and mutations\n\n- Use real `<form>` elements and labelled controls. Validation text uses `.invalid-feedback` or `.form-text`.\n- Use `ctx.routeUrl()` for form actions, HTMX calls, downloads and SSE. `ctx.uiRouteUrl()` is only for GET navigation through a mounted page route.\n- Mutations should return a useful fragment and emit an `HX-Trigger` event for passive regions that need to reload.\n- Disable buttons only while their own request is active. Keep a visible loading indicator and an error fragment.\n\n## Components\n\n- Buttons: `.btn`, with one obvious `.btn-primary` action per group.\n- Status: Bootstrap badges with text; never communicate state by colour alone.\n- Destructive actions: `.btn-outline-danger` or `.btn-danger`, with confirmation proportional to impact.\n- Side panels: wrap content in `data-bp-sidebar`, add `data-bp-sidebar-title`, and open it with `data-bp-sidebar-open`. The theme converts it to an offcanvas.\n- Modals and offcanvas content may be teleported by the shell. Do not depend on a specific DOM parent after activation.\n\n## Accessibility\n\n- Preserve heading order, visible labels, keyboard focus and native controls.\n- Every icon-only action needs an accessible name.\n- Tables need headers; use a responsive wrapper instead of shrinking text.\n- Focus the first meaningful control when opening a task panel and return focus to its trigger when it closes.\n\n## Avoid\n\n- No SPA framework, client router, iframe, client state store or hardcoded service hostname.\n- No inline copy of Bootstrap or shell JavaScript.\n- No theme-origin mutation URL. Service requests go to service URLs.\n- No custom colour that bypasses the app palette when a Bootstrap semantic token exists.\n"
|
|
252
|
+
"content": "# Bootstrap1 UI guide\n\nUse Bootstrap 5 classes and server-rendered JSX/HTMX. BetterPortal service renderers return fragments; the theme owns the shell, navigation, visible URL and global assets.\n\n## Layout\n\n- Render primary content for the `main` slot. Never emit another page shell, `<html>`, `<head>`, sidebar navigation or top bar.\n- Start pages with a semantic heading and a short action row when actions exist.\n- Use `.container-fluid`, Bootstrap grid classes and responsive gaps. Avoid fixed widths except a declared `data-bp-sidebar-width`.\n- Use cards for grouped settings, tables for comparable records and list groups for short navigation or status collections.\n- Keep primary actions on the right of desktop action rows and allow them to wrap on small screens.\n\n## Forms and mutations\n\n- Use real `<form>` elements and labelled controls. Validation text uses `.invalid-feedback` or `.form-text`.\n- Use `ctx.routeUrl()` for form actions, HTMX calls, downloads and SSE. `ctx.uiRouteUrl()` is only for GET navigation through a mounted page route.\n- Use `<BPElement ctx={ctx} service=\"dependency-alias\" path=\"/contract/path\" fragment=\"location.id\">` for cross-service UI fragments. Never put service UUIDs or absolute service URLs in renderer code; the server consumes `ctx` and resolves the app-mounted provider.\n- Use `service=\"shell\"` and a singular shell fragment id to reuse active-shell UI such as `theme-selector`. Add `bp-loading`, optional `bp-ok` with exactly one `<template />`, status-specific `bp-status`, and `bp-nok` states as needed.\n- Mutations should return a useful fragment and emit an `HX-Trigger` event for passive regions that need to reload.\n- Disable buttons only while their own request is active. Keep a visible loading indicator and an error fragment.\n\n## Components\n\n- Buttons: `.btn`, with one obvious `.btn-primary` action per group.\n- Status: Bootstrap badges with text; never communicate state by colour alone.\n- Destructive actions: `.btn-outline-danger` or `.btn-danger`, with confirmation proportional to impact.\n- Side panels: wrap content in `data-bp-sidebar`, add `data-bp-sidebar-title`, and open it with `data-bp-sidebar-open`. The theme converts it to an offcanvas.\n- Modals and offcanvas content may be teleported by the shell. Do not depend on a specific DOM parent after activation.\n\n## Accessibility\n\n- Preserve heading order, visible labels, keyboard focus and native controls.\n- Every icon-only action needs an accessible name.\n- Tables need headers; use a responsive wrapper instead of shrinking text.\n- Focus the first meaningful control when opening a task panel and return focus to its trigger when it closes.\n\n## Avoid\n\n- No SPA framework, client router, iframe, client state store or hardcoded service hostname.\n- No inline copy of Bootstrap or shell JavaScript.\n- No theme-origin mutation URL. Service requests go to service URLs.\n- No custom colour that bypasses the app palette when a Bootstrap semantic token exists.\n"
|
|
250
253
|
},
|
|
251
254
|
{
|
|
252
255
|
"id": "bootstrap1.skill",
|
|
@@ -254,7 +257,7 @@
|
|
|
254
257
|
"title": "Bootstrap1 UI skill",
|
|
255
258
|
"description": "Agent workflow for producing theme-compatible service UI.",
|
|
256
259
|
"mediaType": "text/markdown; charset=utf-8",
|
|
257
|
-
"content": "---\nname: betterportal-bootstrap1-ui\ndescription: Build BetterPortal service UI that fits the active Bootstrap1 shell.\n---\n\n# BetterPortal Bootstrap1 UI\n\n1. Read the active app's `/llms-api.txt` and this theme's UI guide.\n2. Identify the service view, methods, schemas, required permissions and stable `viewId`.\n3. Render server-side JSX/HTMX for the `bootstrap1` theme. Return only service content, never the shell.\n4. Use `ctx.routeUrl()` for service requests and `ctx.uiRouteUrl()` only for GET page navigation.\n5. Prefer Bootstrap components and native HTML controls. Add custom CSS or JavaScript only when the theme and platform primitives cannot express the behavior.\
|
|
260
|
+
"content": "---\nname: betterportal-bootstrap1-ui\ndescription: Build BetterPortal service UI that fits the active Bootstrap1 shell.\n---\n\n# BetterPortal Bootstrap1 UI\n\n1. Read the active app's `/llms-api.txt` and this theme's UI guide.\n2. Identify the service view, methods, schemas, required permissions and stable `viewId`.\n3. Render server-side JSX/HTMX for the `bootstrap1` theme. Return only service content, never the shell.\n4. Use `ctx.routeUrl()` for service requests and `ctx.uiRouteUrl()` only for GET page navigation.\n5. Use the framework `BPElement` JSX helper with `ctx` for cross-service or active-shell fragments. Reference a declared dependency alias, never a service UUID or absolute URL. Keep loading/error/status UI inside `bp-loading`, `bp-status`, and `bp-nok`; an explicit `bp-ok` must have exactly one `<template />`.\n6. Prefer Bootstrap components and native HTML controls. Add custom CSS or JavaScript only when the theme and platform primitives cannot express the behavior.\n7. Include loading, empty, validation, forbidden and service-unavailable states.\n8. Check keyboard operation, visible labels, heading order and responsive wrapping.\n9. Verify JSON and HTML representations against the published schemas.\n\nFor side tasks, use `data-bp-sidebar` and `data-bp-sidebar-open`. For authenticated downloads, use `hx-download`. For live refresh, emit an `HX-Trigger` event and subscribe with `hx-trigger=\"event from:body\"`.\n"
|
|
258
261
|
},
|
|
259
262
|
{
|
|
260
263
|
"id": "bootstrap1.page-template",
|
|
@@ -266,6 +269,33 @@
|
|
|
266
269
|
"content": "/** @jsxImportSource jsx-htmx */\nimport type { PageRenderer } from \"@betterportal/framework\";\n\nexport const render: PageRenderer = (data, ctx) => (\n <section class=\"container-fluid py-3\">\n <div class=\"d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3\">\n <div>\n <h1 class=\"h3 mb-1\">Page title</h1>\n <p class=\"text-body-secondary mb-0\">Explain the task in one sentence.</p>\n </div>\n <a class=\"btn btn-primary\" href={ctx.uiRouteUrl(\"example.create\") ?? \"#\"}>Create</a>\n </div>\n <div class=\"card shadow-sm\">\n <div class=\"card-body\">{JSON.stringify(data)}</div>\n </div>\n </section>\n);\n"
|
|
267
270
|
}
|
|
268
271
|
],
|
|
272
|
+
"shell": {
|
|
273
|
+
"service": "bootstrap1",
|
|
274
|
+
"renderer": "bootstrap5",
|
|
275
|
+
"fragments": [
|
|
276
|
+
{
|
|
277
|
+
"id": "footer",
|
|
278
|
+
"kind": "block",
|
|
279
|
+
"title": "Footer fragments",
|
|
280
|
+
"description": "Ordered fragments shown in the page footer.",
|
|
281
|
+
"defaultItems": []
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"id": "nav",
|
|
285
|
+
"kind": "block",
|
|
286
|
+
"title": "Topbar fragments",
|
|
287
|
+
"description": "Ordered fragments shown on the right side of the topbar.",
|
|
288
|
+
"defaultItems": []
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "theme-selector",
|
|
292
|
+
"kind": "fragment",
|
|
293
|
+
"title": "Theme selector",
|
|
294
|
+
"description": "Lets the user choose light, dark, or system color mode.",
|
|
295
|
+
"defaultItems": []
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
},
|
|
269
299
|
"cacheHints": {
|
|
270
300
|
"metadataTtlSeconds": 1800
|
|
271
301
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "./plugins/service-betterportal-theme-bootstrap1/
|
|
1
|
+
export * from "./plugins/service-betterportal-theme-bootstrap1/shell/index.js";
|
|
2
2
|
export * from "./plugins/service-betterportal-theme-bootstrap1/index.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../src/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,eAAO,MAAM,QAAQ,EAAE,oBA4BtB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// AUTO-GENERATED by BetterPortal codegen - DO NOT EDIT
|
|
2
|
+
import * as shellFragment0 from "../shell/_footer/index.js";
|
|
3
|
+
import * as shellFragment1 from "../shell/_nav/index.js";
|
|
4
|
+
import * as shellFragment2 from "../shell/_theme-selector.js";
|
|
5
|
+
export const registry = {
|
|
6
|
+
routes: [],
|
|
7
|
+
shellFragments: [
|
|
8
|
+
{
|
|
9
|
+
id: "footer",
|
|
10
|
+
kind: "block",
|
|
11
|
+
title: shellFragment0.title,
|
|
12
|
+
description: shellFragment0.description,
|
|
13
|
+
defaultItems: shellFragment0.defaultItems,
|
|
14
|
+
render: shellFragment0.render
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "nav",
|
|
18
|
+
kind: "block",
|
|
19
|
+
title: shellFragment1.title,
|
|
20
|
+
description: shellFragment1.description,
|
|
21
|
+
defaultItems: shellFragment1.defaultItems,
|
|
22
|
+
render: shellFragment1.render
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "theme-selector",
|
|
26
|
+
kind: "fragment",
|
|
27
|
+
title: shellFragment2.title,
|
|
28
|
+
description: shellFragment2.description,
|
|
29
|
+
render: shellFragment2.render
|
|
30
|
+
},
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../src/plugins/service-betterportal-theme-bootstrap1/.bp-generated/registry.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,cAAc,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAC;AAG9D,MAAM,CAAC,MAAM,QAAQ,GAAyB;IAC5C,MAAM,EAAE,EACP;IACD,cAAc,EAAE;QACd;YACE,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,cAAc,CAAC,KAAK;YAC3B,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,MAAM,EAAE,cAAc,CAAC,MAAM;SAC9B;QACD;YACE,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,cAAc,CAAC,KAAK;YAC3B,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,MAAM,EAAE,cAAc,CAAC,MAAM;SAC9B;QACD;YACE,EAAE,EAAE,gBAAgB;YACpB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,cAAc,CAAC,KAAK;YAC3B,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,MAAM,EAAE,cAAc,CAAC,MAAM;SAC9B;KACF;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/assets.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAwBD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/assets.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAwBD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAiC/F"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { BETTERPORTAL_BROWSER_SOURCE_PREAMBLE, betterPortalShellRuntimeSource,
|
|
4
|
+
import { BETTERPORTAL_BROWSER_SOURCE_PREAMBLE, betterPortalShellRuntimeSource, buildBetterPortalShellRuntimeAsset, loadShellRuntimeVendorAsset } from "@betterportal/theme-runtime";
|
|
5
5
|
import { Bootstrap1AdapterSource } from "./adapter.js";
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
7
7
|
const BootstrapCssPath = require.resolve("bootstrap/dist/css/bootstrap.min.css");
|
|
@@ -29,7 +29,7 @@ export async function loadBootstrap1Asset(assetPath) {
|
|
|
29
29
|
if (normalized === "bootstrap.bundle.min.js") {
|
|
30
30
|
return cachedAsset(normalized, () => readTextAsset(BootstrapBundlePath, "application/javascript; charset=utf-8"));
|
|
31
31
|
}
|
|
32
|
-
const vendor = await
|
|
32
|
+
const vendor = await loadShellRuntimeVendorAsset(normalized);
|
|
33
33
|
if (vendor)
|
|
34
34
|
return vendor;
|
|
35
35
|
if (normalized === "betterportal-logo.png") {
|
|
@@ -43,13 +43,12 @@ export async function loadBootstrap1Asset(assetPath) {
|
|
|
43
43
|
}
|
|
44
44
|
if (normalized === "bootstrap1-shell.js") {
|
|
45
45
|
return {
|
|
46
|
-
body: [BETTERPORTAL_BROWSER_SOURCE_PREAMBLE, Bootstrap1AdapterSource, betterPortalShellRuntimeSource(
|
|
46
|
+
body: [BETTERPORTAL_BROWSER_SOURCE_PREAMBLE, Bootstrap1AdapterSource, betterPortalShellRuntimeSource()].join("\n;\n"),
|
|
47
47
|
contentType: "application/javascript; charset=utf-8"
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
if (normalized === "bootstrap1-core.js") {
|
|
51
|
-
return cachedAsset(normalized, () =>
|
|
52
|
-
themeId: "bootstrap1",
|
|
51
|
+
return cachedAsset(normalized, () => buildBetterPortalShellRuntimeAsset({
|
|
53
52
|
adapterSource: Bootstrap1AdapterSource
|
|
54
53
|
}));
|
|
55
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/assets.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/assets.tsx"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,6 @@ declare const Config: import("@bsb/base").BSBPluginConfigClass<av.ObjectSchema<{
|
|
|
8
8
|
betterportal: av.OptionalSchema<av.ObjectSchema<{
|
|
9
9
|
bpConfigPath: av.OptionalSchema<av.StringSchema>;
|
|
10
10
|
configApiToken: av.OptionalSchema<av.StringSchema>;
|
|
11
|
-
configEncryptionKey: av.OptionalSchema<av.StringSchema>;
|
|
12
11
|
controlPlaneUrl: av.OptionalSchema<av.StringSchema>;
|
|
13
12
|
serviceApiKey: av.OptionalSchema<av.StringSchema>;
|
|
14
13
|
bootstrapStatePath: av.StringSchema;
|
|
@@ -36,7 +35,6 @@ export declare class Plugin extends BPService<InstanceType<typeof Config>, typeo
|
|
|
36
35
|
betterportal: av.OptionalSchema<av.ObjectSchema<{
|
|
37
36
|
bpConfigPath: av.OptionalSchema<av.StringSchema>;
|
|
38
37
|
configApiToken: av.OptionalSchema<av.StringSchema>;
|
|
39
|
-
configEncryptionKey: av.OptionalSchema<av.StringSchema>;
|
|
40
38
|
controlPlaneUrl: av.OptionalSchema<av.StringSchema>;
|
|
41
39
|
serviceApiKey: av.OptionalSchema<av.StringSchema>;
|
|
42
40
|
bootstrapStatePath: av.StringSchema;
|
|
@@ -83,8 +81,6 @@ export declare class Plugin extends BPService<InstanceType<typeof Config>, typeo
|
|
|
83
81
|
private handleThemeStyle;
|
|
84
82
|
private handleThemeBrand;
|
|
85
83
|
private handleThemeNav;
|
|
86
|
-
private buildLocationFragments;
|
|
87
|
-
private handleThemeFragments;
|
|
88
84
|
private logThemeContextResolutionFailure;
|
|
89
85
|
private tagRequestContext;
|
|
90
86
|
private renderBootstrapErrorPage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG1B,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,0BAA0B,EA0B1B,KAAK,oBAAoB,EAK1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,SAAS,EAET,KAAK,mBAAmB,EAEzB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/service-betterportal-theme-bootstrap1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG1B,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,0BAA0B,EA0B1B,KAAK,oBAAoB,EAK1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,SAAS,EAET,KAAK,mBAAmB,EAEzB,MAAM,0BAA0B,CAAC;AAkKlC,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;GASX,CAAC;AAEF,QAAA,MAAM,YAAY;;;;;;;CAOhB,CAAC;AAEH,qBAAa,MAAO,SAAQ,SAAS,CAAC,YAAY,CAAC,OAAO,MAAM,CAAC,EAAE,OAAO,YAAY,CAAC;IACrF,MAAM,CAAC,MAAM;;;;;;;;;;;;;;;;;QAAU;IACvB,MAAM,CAAC,YAAY;;;;;;;MAAgB;IACnC,SAAS,CAAC,WAAW,6BAAoC;gBAE7C,GAAG,EAAE,qBAAqB,CAAC,YAAY,CAAC,OAAO,MAAM,CAAC,EAAE,OAAO,YAAY,CAAC;IAIxF,SAAS,CAAC,UAAU,IAAI,mBAAmB;IAgB3C,OAAO,KAAK,YAAY,GAEvB;IAED,SAAS,CAAC,kBAAkB,IAAI;QAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;cASpE,YAAY,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7F,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,uBAAuB;IAmB/B,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,kBAAkB;YAYZ,gBAAgB;IAS9B,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;YAIN,qBAAqB;YAmBrB,qBAAqB;YAuCrB,gBAAgB;YAYhB,gBAAgB;YAWhB,cAAc;IA2B5B,OAAO,CAAC,gCAAgC;IAyBxC,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,wBAAwB;IA8GhC,OAAO,CAAC,gBAAgB;IAkExB,OAAO,CAAC,kBAAkB;YAsIZ,yBAAyB;YAkCzB,cAAc;YAiBd,kBAAkB;YAoClB,mBAAmB;YAgCnB,WAAW;YAwBX,WAAW;YA0NX,cAAc;YASd,YAAY;CAU3B;AAED,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -2,10 +2,11 @@ import { createConfigSchema, createEventSchemas } from "@bsb/base";
|
|
|
2
2
|
import * as av from "anyvali";
|
|
3
3
|
import { InMemoryServiceConfigStore, buildThemeAiManifest, resolveThemeLlmsContext, renderThemeLlmsApi, renderThemeLlmsDev, renderThemeLlmsIndex, renderThemeLlmsUi, buildServiceViewUrl, buildOriginPolicy, htmlResponse, inferServicePathFromViewId, registerServiceConfigRoutes, resolveServiceForTenant, resolveAppRoute, resolveRequestContextDetailed, resolveThemeRequestContext, serviceBaseUrl, eventObservability, eventHeaders, jsonResponse, resolveThemeHostname, withObservedEvent } from "@betterportal/framework";
|
|
4
4
|
import { BPService, BetterPortalConfigSchema } from "@betterportal/plugin-bsb";
|
|
5
|
-
import { isUserFacingRoute, renderBootstrap1HostPage, renderNavItems, shellStyles, renderBrand } from "./
|
|
5
|
+
import { isUserFacingRoute, renderBootstrap1HostPage, renderNavItems, shellStyles, renderBrand } from "./shell/index.js";
|
|
6
6
|
import { toHtmlString } from "@betterportal/framework";
|
|
7
7
|
import { loadBootstrap1Asset } from "./assets.js";
|
|
8
8
|
import { Bootstrap1DeveloperResources } from "./resources.js";
|
|
9
|
+
import { registry } from "./.bp-generated/registry.js";
|
|
9
10
|
// Parse-only base for relative request URLs. Never emit this origin.
|
|
10
11
|
const RELATIVE_URL_PARSE_BASE = "http://betterportal.invalid";
|
|
11
12
|
const PluginConfigSchema = av.object({
|
|
@@ -117,12 +118,6 @@ function resolveSafeServiceTarget(service, path, themeOrigin) {
|
|
|
117
118
|
* Any service response can fire these via the HX-Trigger header (e.g. auth
|
|
118
119
|
* after login/logout); fragments that listen reload, everyone else ignores.
|
|
119
120
|
*/
|
|
120
|
-
function fragmentTriggerSpec(fragmentKey, pluginId) {
|
|
121
|
-
const triggers = ["load", `bp:fragment:${fragmentKey} from:body`];
|
|
122
|
-
if (pluginId)
|
|
123
|
-
triggers.push(`bp:fragments:${pluginId} from:body`);
|
|
124
|
-
return triggers.join(", ");
|
|
125
|
-
}
|
|
126
121
|
function resolveSafeServiceViewTarget(service, route, currentPath, themeOrigin) {
|
|
127
122
|
const viewUrl = buildServiceViewUrl(service, route, currentPath);
|
|
128
123
|
const parsed = parseAbsoluteHttpUrl(viewUrl);
|
|
@@ -175,11 +170,11 @@ export class Plugin extends BPService {
|
|
|
175
170
|
description: "Bootstrap 5 + htmx theme that renders BetterPortal app shells.",
|
|
176
171
|
category: "theme",
|
|
177
172
|
capabilities: ["theme"],
|
|
173
|
+
shell: { service: "bootstrap1", renderer: "bootstrap5", fragments: [] },
|
|
178
174
|
developerResources: Bootstrap1DeveloperResources,
|
|
179
175
|
configSchemas: THEME_CONFIG_SCHEMAS
|
|
180
176
|
},
|
|
181
|
-
|
|
182
|
-
registry: { routes: [] }
|
|
177
|
+
registry
|
|
183
178
|
};
|
|
184
179
|
}
|
|
185
180
|
get betterportal() {
|
|
@@ -278,7 +273,6 @@ export class Plugin extends BPService {
|
|
|
278
273
|
this.app.get("/.well-known/bp/theme/style", (event) => this.handleThemeStyle(event));
|
|
279
274
|
this.app.get("/.well-known/bp/theme/brand", (event) => this.handleThemeBrand(event));
|
|
280
275
|
this.app.get("/.well-known/bp/theme/nav", (event) => this.handleThemeNav(event));
|
|
281
|
-
this.app.get("/.well-known/bp/theme/fragments", (event) => this.handleThemeFragments(event));
|
|
282
276
|
this.app.get("/**", (event) => this.handleIndex(event));
|
|
283
277
|
}
|
|
284
278
|
resolveThemeAiContext(activeEvent) {
|
|
@@ -392,45 +386,6 @@ export class Plugin extends BPService {
|
|
|
392
386
|
const html = Array.isArray(rendered) ? rendered.map((r) => toHtmlString(r)).join("") : toHtmlString(rendered);
|
|
393
387
|
return htmlResponse(html, 200, "text/html; mode=fragment", { "cache-control": "no-store" });
|
|
394
388
|
}
|
|
395
|
-
buildLocationFragments(portalConfig, requestContext, location, themeOrigin) {
|
|
396
|
-
const appFragments = requestContext.app.fragments;
|
|
397
|
-
const assignments = appFragments?.[location] ?? [];
|
|
398
|
-
return assignments
|
|
399
|
-
.filter((a) => a.enabled)
|
|
400
|
-
.map((a) => {
|
|
401
|
-
const binding = resolveServiceForTenant(portalConfig, a.serviceId, requestContext);
|
|
402
|
-
if (!binding)
|
|
403
|
-
return null;
|
|
404
|
-
const safeTarget = resolveSafeServiceTarget(binding.service, a.targetPath, themeOrigin);
|
|
405
|
-
if (!safeTarget.ok)
|
|
406
|
-
return null;
|
|
407
|
-
// Load-triggered fragments must be absolute before client JS runs.
|
|
408
|
-
return {
|
|
409
|
-
fragmentId: a.fragmentId,
|
|
410
|
-
serviceId: a.serviceId,
|
|
411
|
-
pluginId: binding.service.serviceId,
|
|
412
|
-
url: safeTarget.url,
|
|
413
|
-
fragmentKey: `${location}.${a.fragmentId}`
|
|
414
|
-
};
|
|
415
|
-
})
|
|
416
|
-
.filter((x) => x !== null);
|
|
417
|
-
}
|
|
418
|
-
async handleThemeFragments(event) {
|
|
419
|
-
const url = new URL(event.req.url ?? "", RELATIVE_URL_PARSE_BASE);
|
|
420
|
-
const location = url.searchParams.get("location") ?? "nav";
|
|
421
|
-
const portalConfig = this.requirePortalConfig();
|
|
422
|
-
const requestContext = resolveThemeRequestContext(portalConfig, eventHeaders(event), resolveThemeHostname(eventHeaders(event), this.headerTrustOptions()) ?? undefined, this.headerTrustOptions());
|
|
423
|
-
if (!requestContext) {
|
|
424
|
-
this.logThemeContextResolutionFailure(event);
|
|
425
|
-
return htmlResponse("", 200, "text/html; mode=fragment");
|
|
426
|
-
}
|
|
427
|
-
this.tagRequestContext(event, requestContext.tenant.id, requestContext.app.id);
|
|
428
|
-
const frags = this.buildLocationFragments(portalConfig, requestContext, location, url.origin);
|
|
429
|
-
const escape = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
430
|
-
const appendFragmentKey = (targetUrl, fragmentKey) => `${targetUrl}${targetUrl.includes("?") ? "&" : "?"}_f=${fragmentKey}`;
|
|
431
|
-
const html = frags.map((f) => `<div data-bp-fragment="${escape(f.fragmentId)}" data-bp-fragment-location="${escape(location)}" data-bp-service="${escape(f.serviceId)}" hx-get="${escape(appendFragmentKey(f.url, f.fragmentKey))}" hx-trigger="${escape(fragmentTriggerSpec(f.fragmentKey, f.pluginId))}" hx-target="this" hx-swap="innerHTML"><span class="placeholder-glow"><span class="placeholder col-12 rounded-pill"></span></span></div>`).join("");
|
|
432
|
-
return htmlResponse(html, 200, "text/html; mode=fragment", { "cache-control": "no-store" });
|
|
433
|
-
}
|
|
434
389
|
logThemeContextResolutionFailure(event, error) {
|
|
435
390
|
const obs = eventObservability(event);
|
|
436
391
|
if (!obs)
|
|
@@ -984,60 +939,6 @@ export class Plugin extends BPService {
|
|
|
984
939
|
: initialSafeTarget && !initialSafeTarget.ok
|
|
985
940
|
? initialSafeTarget.error
|
|
986
941
|
: undefined;
|
|
987
|
-
const resolvedFragments = {};
|
|
988
|
-
// New fragments config
|
|
989
|
-
const appFragments = requestContext.app.fragments;
|
|
990
|
-
if (appFragments && Object.keys(appFragments).length > 0) {
|
|
991
|
-
for (const [location, assignments] of Object.entries(appFragments)) {
|
|
992
|
-
resolvedFragments[location] = assignments
|
|
993
|
-
.filter(a => a.enabled)
|
|
994
|
-
.map(a => {
|
|
995
|
-
const binding = resolveServiceForTenant(portalConfig, a.serviceId, requestContext);
|
|
996
|
-
if (!binding)
|
|
997
|
-
return null;
|
|
998
|
-
const safeTarget = resolveSafeServiceTarget(binding.service, a.targetPath, themeOrigin);
|
|
999
|
-
if (!safeTarget.ok)
|
|
1000
|
-
return null;
|
|
1001
|
-
// Load-triggered fragments must be absolute before client JS runs.
|
|
1002
|
-
return {
|
|
1003
|
-
fragmentId: a.fragmentId,
|
|
1004
|
-
serviceId: a.serviceId,
|
|
1005
|
-
pluginId: binding.service.serviceId,
|
|
1006
|
-
url: safeTarget.url,
|
|
1007
|
-
fragmentKey: `${location}.${a.fragmentId}`
|
|
1008
|
-
};
|
|
1009
|
-
})
|
|
1010
|
-
.filter((x) => x !== null);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
else {
|
|
1014
|
-
// Backward compat: map old slots to fragments
|
|
1015
|
-
for (const slot of requestContext.app.slots) {
|
|
1016
|
-
if (!slot.enabled)
|
|
1017
|
-
continue;
|
|
1018
|
-
const dotIdx = slot.slotId.indexOf(".");
|
|
1019
|
-
if (dotIdx === -1)
|
|
1020
|
-
continue;
|
|
1021
|
-
const location = slot.slotId.slice(0, dotIdx);
|
|
1022
|
-
const id = slot.slotId.slice(dotIdx + 1);
|
|
1023
|
-
const binding = resolveServiceForTenant(portalConfig, slot.serviceId, requestContext);
|
|
1024
|
-
if (!binding)
|
|
1025
|
-
continue;
|
|
1026
|
-
if (!resolvedFragments[location])
|
|
1027
|
-
resolvedFragments[location] = [];
|
|
1028
|
-
const viewPath = inferServicePathFromViewId(slot.viewId);
|
|
1029
|
-
const safeTarget = resolveSafeServiceTarget(binding.service, viewPath, themeOrigin);
|
|
1030
|
-
if (!safeTarget.ok)
|
|
1031
|
-
continue;
|
|
1032
|
-
resolvedFragments[location].push({
|
|
1033
|
-
fragmentId: id,
|
|
1034
|
-
serviceId: slot.serviceId,
|
|
1035
|
-
pluginId: binding.service.serviceId,
|
|
1036
|
-
url: safeTarget.url,
|
|
1037
|
-
fragmentKey: slot.slotId
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
942
|
const originPolicy = buildOriginPolicy(requestContext);
|
|
1042
943
|
const baseTheme = this.applyThemeServiceConfig(requestContext.app.themeConfig, this.readStoredThemeValues(requestContext.tenant.id, requestContext.app.id));
|
|
1043
944
|
const mergedThemeConfig = {
|
|
@@ -1082,7 +983,6 @@ export class Plugin extends BPService {
|
|
|
1082
983
|
initialServiceId: currentRoute?.serviceId,
|
|
1083
984
|
routeLinks,
|
|
1084
985
|
navItems: navItems,
|
|
1085
|
-
resolvedFragments,
|
|
1086
986
|
loginUrl,
|
|
1087
987
|
chrome: currentRoute?.chrome,
|
|
1088
988
|
aiManifestUrl: "/.well-known/bp/ai.json",
|