@cloudcrane/app-runtime 0.5.0 → 0.8.0

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.
@@ -13,6 +13,7 @@ export type LinkedRuntime = {
13
13
  path: string;
14
14
  integrity: string;
15
15
  };
16
+ /** `runtime` names the runtime the page carries, or is "page" for an HTML app's spec, which is its page as written, with none. */
16
17
  export type Compiled = {
17
18
  html: string;
18
19
  sha256: string;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { canonical, validateSpec } from "@cloudcrane/app-spec";
2
+ import { canonical, pageOf, validateSpec } from "@cloudcrane/app-spec";
3
3
  import { CODED_CSS, DATESTRIP_CSS } from "../runtime/styles.js";
4
4
  import { tokensCss } from "../runtime/theme.js";
5
5
  import { RUNTIME_JS, RUNTIME_SHA256, RUNTIME_VERSION } from "./runtime.generated.js";
@@ -40,6 +40,10 @@ export function compile(input, options = {}) {
40
40
  const { spec, problems } = validateSpec(input);
41
41
  if (!spec)
42
42
  throw new CompileError(problems);
43
+ // A spec that is an HTML app's page is that page, as written: no runtime, so any runtime version builds it the same.
44
+ const page = pageOf(spec);
45
+ if (page !== null)
46
+ return { html: page, sha256: createHash("sha256").update(page).digest("hex"), runtime: "page" };
43
47
  if (spec.manifest.runtime !== RUNTIME_VERSION)
44
48
  throw new CompileError([{ at: "manifest.runtime", problem: `this compiler builds runtime ${RUNTIME_VERSION}` }]);
45
49
  // Only what drawing the app needs goes in the page: not the journeys, promises or tests.
package/dist/host.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { validateSpec, leakProblems, deriveBlueprint, CLOUDCRANE_CATALOG_ID } from "@cloudcrane/app-spec";
1
+ export { validateSpec, leakProblems, deriveBlueprint, CLOUDCRANE_CATALOG_ID, pageOf, wrapPage, type PageApp } from "@cloudcrane/app-spec";
2
2
  export { compile, CompileError, runtimeFile } from "./compiler/compile.js";
3
3
  export { RUNTIME_JS, RUNTIME_SHA256, RUNTIME_VERSION } from "./compiler/runtime.generated.js";
package/dist/host.js CHANGED
@@ -2,6 +2,6 @@
2
2
  // one file, built by vendor.mjs): check a spec, build it, and describe it as a
3
3
  // blueprint. The runtime's code is exported so a host can check a built page
4
4
  // without it: the runtime is reviewed once per version and locked by its hash.
5
- export { validateSpec, leakProblems, deriveBlueprint, CLOUDCRANE_CATALOG_ID } from "@cloudcrane/app-spec";
5
+ export { validateSpec, leakProblems, deriveBlueprint, CLOUDCRANE_CATALOG_ID, pageOf, wrapPage } from "@cloudcrane/app-spec";
6
6
  export { compile, CompileError, runtimeFile } from "./compiler/compile.js";
7
7
  export { RUNTIME_JS, RUNTIME_SHA256, RUNTIME_VERSION } from "./compiler/runtime.generated.js";
@@ -1,10 +1,19 @@
1
1
  import type { TokenNode } from "@cloudcrane/app-spec/schema";
2
- /** Every token as [--a2ui-name, value], in name order. A group's $type applies to its tokens. */
2
+ /**
3
+ * Every token as [--a2ui-name, value], in name order. A group's $type applies to its tokens. The top-level `dark` group
4
+ * is the dark colours (darkVariables), not tokens of its own.
5
+ */
3
6
  export declare function tokenVariables(tokens: TokenNode): [string, string][];
7
+ /** The dark colours (tokens.dark, laid out as the light ones: dark.color.background…), as the variables they replace. */
8
+ export declare function darkVariables(tokens: TokenNode): [string, string][];
9
+ /** A colour's relative lightness, 0 to 1, from #rgb, #rrggbb or rgb(r g b); null for anything else. */
10
+ declare function lightness(css: string): number | null;
4
11
  /**
5
12
  * The tokens as a :root rule, with the page's colour scheme taken from its own
6
13
  * background: A2UI's defaults for what a spec leaves out (a plain button's
7
14
  * text) are light-dark() pairs, which would otherwise follow the phone's
8
- * setting and put light text on a light page. Light unless the background is dark.
15
+ * setting and put light text on a light page. Light unless the background is dark; with dark colours (tokens.dark),
16
+ * the light ones by default and the dark ones when the phone is set to dark.
9
17
  */
10
18
  export declare function tokensCss(tokens: TokenNode): string;
19
+ export { lightness };
@@ -19,8 +19,20 @@ function cssValue(type, v) {
19
19
  }
20
20
  return null;
21
21
  }
22
- /** Every token as [--a2ui-name, value], in name order. A group's $type applies to its tokens. */
22
+ /**
23
+ * Every token as [--a2ui-name, value], in name order. A group's $type applies to its tokens. The top-level `dark` group
24
+ * is the dark colours (darkVariables), not tokens of its own.
25
+ */
23
26
  export function tokenVariables(tokens) {
27
+ const { dark: _dark, ...light } = tokens;
28
+ return variablesOf(light);
29
+ }
30
+ /** The dark colours (tokens.dark, laid out as the light ones: dark.color.background…), as the variables they replace. */
31
+ export function darkVariables(tokens) {
32
+ const dark = tokens["dark"];
33
+ return dark && typeof dark === "object" ? variablesOf(dark) : [];
34
+ }
35
+ function variablesOf(tokens) {
24
36
  const out = [];
25
37
  const walk = (node, path, type) => {
26
38
  const t = node.$type ?? type;
@@ -60,12 +72,20 @@ function lightness(css) {
60
72
  * The tokens as a :root rule, with the page's colour scheme taken from its own
61
73
  * background: A2UI's defaults for what a spec leaves out (a plain button's
62
74
  * text) are light-dark() pairs, which would otherwise follow the phone's
63
- * setting and put light text on a light page. Light unless the background is dark.
75
+ * setting and put light text on a light page. Light unless the background is dark; with dark colours (tokens.dark),
76
+ * the light ones by default and the dark ones when the phone is set to dark.
64
77
  */
65
78
  export function tokensCss(tokens) {
66
79
  const vars = tokenVariables(tokens);
80
+ const dark = darkVariables(tokens);
81
+ // With dark colours, the page follows the phone's setting; without, it keeps the one scheme its background is.
82
+ if (dark.length) {
83
+ const rule = (vs) => vs.map(([k, v]) => `${k}:${v};`).join("");
84
+ return `:root{color-scheme:light dark;${rule(vars)}}@media (prefers-color-scheme:dark){:root{${rule(dark)}}}`;
85
+ }
67
86
  const background = vars.find(([k]) => k === "--a2ui-color-background")?.[1];
68
87
  const l = background === undefined ? null : lightness(background);
69
88
  const scheme = l !== null && l < 0.18 ? "dark" : "light";
70
89
  return `:root{color-scheme:${scheme};${vars.map(([k, v]) => `${k}:${v};`).join("")}}`;
71
90
  }
91
+ export { lightness };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudcrane/app-runtime",
3
- "version": "0.5.0",
3
+ "version": "0.8.0",
4
4
  "runtimeVersion": "0.4.0",
5
5
  "description": "Builds a CloudCrane app spec into one HTML page, the same bytes every time, and the runtime that draws it with A2UI's renderer.",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  "LICENSE"
45
45
  ],
46
46
  "dependencies": {
47
- "@cloudcrane/app-spec": "^0.4.0"
47
+ "@cloudcrane/app-spec": "^0.7.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "zod": "^3.25.76"