@wyndlass/app-sdk 0.0.0 → 1.1.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.
Files changed (75) hide show
  1. package/README.md +212 -1
  2. package/cli/app-package.mjs +111 -0
  3. package/cli/commands.mjs +133 -0
  4. package/cli/main.mjs +73 -0
  5. package/cli/package.mjs +119 -0
  6. package/cli/preview.mjs +142 -0
  7. package/cli/zip.mjs +63 -0
  8. package/dist/contract/app-contract.d.ts +221 -0
  9. package/dist/contract/app-contract.js +6 -0
  10. package/dist/contract/app-definition.d.ts +155 -0
  11. package/dist/contract/app-definition.js +3 -0
  12. package/dist/contract/data-answer.d.ts +40 -0
  13. package/dist/contract/data-answer.js +3 -0
  14. package/dist/contract/data-calendar.d.ts +57 -0
  15. package/dist/contract/data-calendar.js +3 -0
  16. package/dist/contract/data-news.d.ts +29 -0
  17. package/dist/contract/data-news.js +3 -0
  18. package/dist/contract/data-quotes.d.ts +75 -0
  19. package/dist/contract/data-quotes.js +3 -0
  20. package/dist/contract/data-series.d.ts +51 -0
  21. package/dist/contract/data-series.js +3 -0
  22. package/dist/contract/data-snapshot.d.ts +30 -0
  23. package/dist/contract/data-snapshot.js +3 -0
  24. package/dist/contract/data-weather.d.ts +421 -0
  25. package/dist/contract/data-weather.js +3 -0
  26. package/dist/contract/player-api-event.d.ts +253 -0
  27. package/dist/contract/player-api-event.js +3 -0
  28. package/dist/contract/player-api-status.d.ts +178 -0
  29. package/dist/contract/player-api-status.js +3 -0
  30. package/dist/easing.d.ts +16 -0
  31. package/dist/easing.js +71 -0
  32. package/dist/index.d.ts +94 -0
  33. package/dist/index.js +218 -0
  34. package/dist/preview/frame/frame.js +2 -0
  35. package/dist/preview/frame/index.html +12 -0
  36. package/dist/preview/index.html +40 -0
  37. package/dist/preview/page.js +7 -0
  38. package/dist/qr.d.ts +5 -0
  39. package/dist/qr.js +14 -0
  40. package/dist/react.d.ts +52 -0
  41. package/dist/react.js +149 -0
  42. package/package.json +83 -4
  43. package/protocol/app-definition.schema.json +435 -0
  44. package/protocol/app-packages.json +172 -0
  45. package/protocol/common.schema.json +197 -0
  46. package/protocol/samples/calendar.json +63 -0
  47. package/protocol/samples/events/app.command.json +14 -0
  48. package/protocol/samples/events/item.end.json +13 -0
  49. package/protocol/samples/events/item.start.json +18 -0
  50. package/protocol/samples/events/state.changed.json +11 -0
  51. package/protocol/samples/news.json +37 -0
  52. package/protocol/samples/player-status.json +58 -0
  53. package/protocol/samples/weather.json +59 -0
  54. package/skill/wyndlass-app/SKILL.md +90 -0
  55. package/skill/wyndlass-app/references/contract.md +91 -0
  56. package/skill/wyndlass-app/references/mcp.md +79 -0
  57. package/skill/wyndlass-app/references/templates.md +38 -0
  58. package/templates/react/AGENTS.md +17 -0
  59. package/templates/react/README.md +23 -0
  60. package/templates/react/app.json +14 -0
  61. package/templates/react/gitignore +2 -0
  62. package/templates/react/package.json +22 -0
  63. package/templates/react/src/app.css +7 -0
  64. package/templates/react/src/app.tsx +39 -0
  65. package/templates/react/src/env.d.ts +13 -0
  66. package/templates/react/tsconfig.json +13 -0
  67. package/templates/vanilla/AGENTS.md +17 -0
  68. package/templates/vanilla/README.md +23 -0
  69. package/templates/vanilla/app.json +14 -0
  70. package/templates/vanilla/gitignore +2 -0
  71. package/templates/vanilla/package.json +18 -0
  72. package/templates/vanilla/src/app.css +7 -0
  73. package/templates/vanilla/src/app.ts +59 -0
  74. package/templates/vanilla/src/env.d.ts +13 -0
  75. package/templates/vanilla/tsconfig.json +12 -0
@@ -0,0 +1,59 @@
1
+ // __APP_NAME__: a heading and the time on one page, the weather on the next, built on Wyndlass's app SDK.
2
+ // Everything it draws comes from the layout's clock (now()), so the designer's seek and pause show the right moment.
3
+ import { WyndlassApp, color, contextOf, data, kept, type DataAnswer, type weather } from "@wyndlass/app-sdk";
4
+ import css from "./app.css";
5
+
6
+ /** How long each of the two pages shows. */
7
+ const PAGE_MS = 8_000;
8
+ /** How often the weather is asked for again. */
9
+ const EVERY_MS = 10 * 60_000;
10
+
11
+ export default class App extends WyndlassApp {
12
+ #pages: HTMLElement[] = [];
13
+ #heading!: HTMLElement;
14
+ #time!: HTMLElement;
15
+ #weather!: HTMLElement;
16
+ #answer?: DataAnswer<weather.Weather>;
17
+ #askedAt = -Infinity;
18
+ #format?: Intl.DateTimeFormat;
19
+
20
+ protected mount() {
21
+ this.css(css);
22
+ this.style.setProperty("--accent", color(this.prop("accent", "#4f8cff"), "#4f8cff"));
23
+ this.root.innerHTML = `<section class="page"><h1></h1><time></time></section><section class="page" hidden><div data-weather></div></section>`;
24
+ this.#pages = [...this.root.querySelectorAll<HTMLElement>(".page")];
25
+ this.#heading = this.root.querySelector("h1")!;
26
+ this.#time = this.root.querySelector("time")!;
27
+ this.#weather = this.root.querySelector("[data-weather]")!;
28
+ this.#heading.textContent = this.prop("heading", "Good morning");
29
+ this.#format = new Intl.DateTimeFormat(this.locale, { timeZone: this.timeZone, hour: "numeric", minute: "2-digit", ...(this.prop("seconds", false) ? { second: "2-digit" } : {}) });
30
+ // The answer the screen kept, at once: the app is ready without waiting for the network.
31
+ this.#answer = kept<weather.Weather>(this, "weather", this.#params());
32
+ if (this.#answer) this.ready();
33
+ }
34
+
35
+ /** The weather for the screen's place, or Winnipeg's where there is none (the designer's sample). */
36
+ #params() {
37
+ const place = contextOf(this)?.place ?? { lat: 49.9, lon: -97.14 };
38
+ return { lat: place.lat, lon: place.lon, units: this.prop("units", "metric") };
39
+ }
40
+
41
+ protected draw(epochMs: number) {
42
+ if (epochMs - this.#askedAt >= EVERY_MS) {
43
+ this.#askedAt = epochMs;
44
+ data<weather.Weather>(this, "weather", this.#params())
45
+ .then(answer => { this.#answer = answer; this.update(this.now()); }, () => undefined)
46
+ .finally(() => this.ready());
47
+ }
48
+ const page = Math.floor(Math.max(0, epochMs - this.startedAt()) / PAGE_MS) % 2;
49
+ this.#pages.forEach((p, i) => { p.hidden = i !== page; });
50
+ this.#time.textContent = this.#format!.format(epochMs);
51
+ const w = this.#answer?.data;
52
+ this.#weather.innerHTML = w
53
+ ? `<div class="temperature">${Math.round(w.current.temperature)}°${w.units === "imperial" ? "F" : "C"}</div><div class="condition">${w.current.condition.replace(/-/g, " ")}</div>`
54
+ : `<div class="none">The weather is on its way.</div>`;
55
+ }
56
+
57
+ /** Two pages of PAGE_MS each, for a layout that turns its enter and exit with them. */
58
+ paging() { return { count: 2, ms: PAGE_MS }; }
59
+ }
@@ -0,0 +1,13 @@
1
+ // A CSS file the source imports is its text, for css(): pack bundles it so.
2
+ declare module "*.css" {
3
+ const css: string;
4
+ export default css;
5
+ }
6
+
7
+ // An image the source imports is a data URL, inlined by pack: for an <img>'s src or a style set from code.
8
+ declare module "*.png" { const url: string; export default url; }
9
+ declare module "*.jpg" { const url: string; export default url; }
10
+ declare module "*.jpeg" { const url: string; export default url; }
11
+ declare module "*.gif" { const url: string; export default url; }
12
+ declare module "*.webp" { const url: string; export default url; }
13
+ declare module "*.svg" { const url: string; export default url; }
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "skipLibCheck": true
10
+ },
11
+ "include": ["src"]
12
+ }