castle-web-cli 0.4.172 → 0.4.174

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 (67) hide show
  1. package/dist/agent-prompts.d.ts +8 -2
  2. package/dist/agent-prompts.js +19 -11
  3. package/dist/agent.js +37 -13
  4. package/dist/assetCompression.d.ts +3 -0
  5. package/dist/assetCompression.js +30 -0
  6. package/dist/castleJson.d.ts +1 -1
  7. package/dist/castleJson.js +2 -2
  8. package/dist/imports.js +2 -2
  9. package/dist/init.js +4 -4
  10. package/dist/native/loop.js +9 -0
  11. package/dist/native/openrouter.d.ts +2 -0
  12. package/dist/native/openrouter.js +10 -1
  13. package/dist/native/types.d.ts +1 -0
  14. package/dist/serve.js +2 -0
  15. package/dist/serveSecurity.d.ts +2 -0
  16. package/dist/serveSecurity.js +17 -3
  17. package/dist/shell/assets/{index-Ws0WrCbi.js → index-DF1yMXPS.js} +4 -4
  18. package/dist/shell/index.html +1 -1
  19. package/kits/base/castle.json +1 -1
  20. package/kits/multiplayer-2d/castle.json +2 -2
  21. package/kits/multiplayer-2d/code/server/sceneBodies.js +1 -1
  22. package/kits/multiplayer-2d/code/server/world.js +2 -2
  23. package/kits/multiplayer-2d/code/systems/multiplayer.js +12 -0
  24. package/kits/multiplayer-2d/fonts.generated.js +1 -1
  25. package/kits/multiplayer-2d/package-lock.json +1 -1
  26. package/kits/multiplayer-3d/CLAUDE.md +5 -4
  27. package/kits/multiplayer-3d/castle.json +3 -3
  28. package/kits/multiplayer-3d/code/client/avatars.js +47 -18
  29. package/kits/multiplayer-3d/code/client/nameTags.js +16 -35
  30. package/kits/multiplayer-3d/code/server/sceneBodies.js +1 -1
  31. package/kits/multiplayer-3d/code/systems/multiplayer.js +14 -1
  32. package/kits/multiplayer-3d/fonts.generated.js +1 -1
  33. package/kits/multiplayer-3d/package-lock.json +1 -1
  34. package/kits/physics-2d/CLAUDE.md +7 -6
  35. package/kits/physics-2d/behaviors/Collider.jsx +7 -1
  36. package/kits/physics-2d/behaviors/RigidBody.jsx +6 -3
  37. package/kits/physics-2d/castle.json +2 -2
  38. package/kits/physics-2d/editors/SceneEditor.jsx +1 -0
  39. package/kits/physics-2d/editors/SingleEditor.jsx +4 -4
  40. package/kits/physics-2d/editors/{StyleEditor.jsx → ThemeEditor.jsx} +8 -8
  41. package/kits/physics-2d/editors/deckFont.js +2 -2
  42. package/kits/physics-2d/editors/{styleEditor.module.css → themeEditor.module.css} +2 -2
  43. package/kits/physics-2d/engine/avatarArt.js +108 -14
  44. package/kits/physics-2d/engine/files.js +2 -2
  45. package/kits/physics-2d/engine/physics/matterBridge.js +2 -2
  46. package/kits/physics-2d/engine/scene.js +1 -1
  47. package/kits/physics-2d/fonts.generated.js +1 -1
  48. package/kits/physics-2d/package-lock.json +1 -1
  49. package/kits/physics-3d/behaviors/Body.jsx +10 -1
  50. package/kits/physics-3d/behaviors/Door.jsx +5 -0
  51. package/kits/physics-3d/behaviors/Lookable.jsx +3 -0
  52. package/kits/physics-3d/behaviors/Model.jsx +4 -0
  53. package/kits/physics-3d/behaviors/Pickup.jsx +4 -0
  54. package/kits/physics-3d/behaviors/Player.jsx +7 -0
  55. package/kits/physics-3d/behaviors/Shape.jsx +13 -0
  56. package/kits/physics-3d/castle.json +1 -1
  57. package/kits/physics-3d/engine3d/editor3dInspector.jsx +1 -0
  58. package/kits/physics-3d/fonts.generated.js +1 -1
  59. package/kits/physics-3d/package-lock.json +1 -1
  60. package/kits/real-time/castle.json +1 -1
  61. package/kits/real-time/code/client/connection.js +11 -2
  62. package/kits/real-time/package-lock.json +1 -1
  63. package/kits/turn-based/castle.json +1 -1
  64. package/kits/turn-based/fonts.generated.js +1 -1
  65. package/kits/turn-based/package-lock.json +1 -1
  66. package/package.json +3 -1
  67. /package/kits/physics-2d/editors/{styleTheme.js → theme.js} +0 -0
@@ -1,11 +1,12 @@
1
- // A player's round avatar for drawing in a scene: the photo clipped to a circle
2
- // with their frame over it, as a canvas. Read through `User.get` once per user
3
- // id and kept for the page. A player with no photo has no avatar, and a caller
4
- // keeps its default look.
1
+ // A player's avatar for drawing in a scene, read through `User.get` once per
2
+ // user id and kept for the page. Two pictures: the round one, the photo clipped
3
+ // to a circle with their frame over it, and the face, the photo alone, square,
4
+ // for a surface it should cover edge to edge. A player with no photo has no
5
+ // avatar, and a caller keeps its default look.
5
6
  //
6
- // `avatarCanvas` is for code that draws with a canvas context, like a name tag.
7
- // `avatarSpritePath` registers the same picture as image art in `scene.sprites`,
8
- // so a `Sprite` component can show it by file path.
7
+ // `avatarCanvas` is for code that draws with a canvas context, like a Box.
8
+ // `avatarSpritePath` and `avatarFacePath` register a picture as image art in
9
+ // `scene.sprites`, so a `Sprite` or a model face can show it by file path.
9
10
 
10
11
  import { User } from 'castle-web-sdk';
11
12
  import { imageArt } from './art';
@@ -16,32 +17,48 @@ const SIZE = 128;
16
17
  // own profile views use.
17
18
  const FRAMED_INSET = SIZE / 6;
18
19
 
19
- // userId -> { canvas }, present from the first ask; `canvas` fills in when the
20
+ // userId -> { canvas, face }, present from the first ask; both fill in when the
20
21
  // read completes.
21
22
  const avatars = new Map();
22
23
 
23
24
  // The avatar for one user id, or null until it is loaded or when there is none.
24
25
  export function avatarCanvas(userId) {
26
+ return entryFor(userId)?.canvas ?? null;
27
+ }
28
+
29
+ // The user's photo alone, square, or null as for `avatarCanvas`.
30
+ export function avatarFace(userId) {
31
+ return entryFor(userId)?.face ?? null;
32
+ }
33
+
34
+ function entryFor(userId) {
25
35
  if (!userId) {
26
36
  return null;
27
37
  }
28
38
  let entry = avatars.get(userId);
29
39
  if (!entry) {
30
- entry = { canvas: null };
40
+ entry = { canvas: null, face: null };
31
41
  avatars.set(userId, entry);
32
42
  void load(userId, entry);
33
43
  }
34
- return entry.canvas;
44
+ return entry;
35
45
  }
36
46
 
37
- // The `scene.sprites` path holding this user's avatar, or null while there is
38
- // none. A `Sprite` with this `file` draws it.
47
+ // The `scene.sprites` path holding this user's round avatar, or null while there
48
+ // is none. A `Sprite` with this `file` draws it.
39
49
  export function avatarSpritePath(scene, userId) {
40
- const canvas = avatarCanvas(userId);
50
+ return registered(scene, `avatars/${userId}.png`, avatarCanvas(userId));
51
+ }
52
+
53
+ // The same for the square face.
54
+ export function avatarFacePath(scene, userId) {
55
+ return registered(scene, `avatars/${userId}-face.png`, avatarFace(userId));
56
+ }
57
+
58
+ function registered(scene, path, canvas) {
41
59
  if (!canvas) {
42
60
  return null;
43
61
  }
44
- const path = `avatars/${userId}.png`;
45
62
  if (!scene.sprites[path]) {
46
63
  scene.sprites[path] = imageArt(path, canvas.toDataURL());
47
64
  }
@@ -54,6 +71,7 @@ async function load(userId, entry) {
54
71
  const [photo, frame] = await Promise.all([loadImage(user?.photoUrl), loadImage(user?.frameUrl)]);
55
72
  if (photo) {
56
73
  entry.canvas = compose(photo, frame);
74
+ entry.face = square(photo);
57
75
  }
58
76
  } catch {
59
77
  // A failed read leaves the entry without a canvas; the next page asks again.
@@ -72,6 +90,82 @@ function loadImage(url) {
72
90
  });
73
91
  }
74
92
 
93
+ // How much of the opaque box is cut from each side, so a round photo's edge and
94
+ // its anti-aliased rim stay off the face.
95
+ const FACE_INSET = 0.12;
96
+
97
+ // The opaque box is never taken smaller than this share of the photo on either
98
+ // axis. Transparency reaching far inward would otherwise blow a few pixels up
99
+ // to the whole face.
100
+ const FACE_MIN = 0.55;
101
+
102
+ // The photo cropped to its opaque pixels and then inset, filling the square.
103
+ // Profile photos are circles on a transparent ground, and a face texture wants
104
+ // no corners and no rim.
105
+ function square(photo) {
106
+ const canvas = document.createElement('canvas');
107
+ canvas.width = SIZE;
108
+ canvas.height = SIZE;
109
+ const ctx = canvas.getContext('2d');
110
+ ctx.drawImage(photo, 0, 0, SIZE, SIZE);
111
+ const box = atLeast(opaqueBox(ctx.getImageData(0, 0, SIZE, SIZE)), SIZE * FACE_MIN, SIZE);
112
+ const inset = Math.round(Math.min(box.width, box.height) * FACE_INSET);
113
+ const face = {
114
+ x: box.x + inset,
115
+ y: box.y + inset,
116
+ width: box.width - 2 * inset,
117
+ height: box.height - 2 * inset,
118
+ };
119
+ if (face.width <= 0 || face.height <= 0) {
120
+ return canvas;
121
+ }
122
+ const source = document.createElement('canvas');
123
+ source.width = SIZE;
124
+ source.height = SIZE;
125
+ source.getContext('2d').drawImage(canvas, 0, 0);
126
+ ctx.clearRect(0, 0, SIZE, SIZE);
127
+ ctx.drawImage(source, face.x, face.y, face.width, face.height, 0, 0, SIZE, SIZE);
128
+ return canvas;
129
+ }
130
+
131
+ // The box grown around its centre to at least `min` on each axis, kept inside
132
+ // `size`.
133
+ function atLeast(box, min, size) {
134
+ const out = { ...box };
135
+ for (const [at, extent] of [
136
+ ['x', 'width'],
137
+ ['y', 'height'],
138
+ ]) {
139
+ if (out[extent] < min) {
140
+ out[at] = Math.round(Math.min(Math.max(0, out[at] + out[extent] / 2 - min / 2), size - min));
141
+ out[extent] = Math.round(min);
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+
147
+ // The smallest box holding every pixel with alpha over a small threshold.
148
+ function opaqueBox({ data, width, height }) {
149
+ let left = width;
150
+ let top = height;
151
+ let right = -1;
152
+ let bottom = -1;
153
+ for (let y = 0; y < height; y += 1) {
154
+ for (let x = 0; x < width; x += 1) {
155
+ if (data[(y * width + x) * 4 + 3] > 16) {
156
+ left = Math.min(left, x);
157
+ top = Math.min(top, y);
158
+ right = Math.max(right, x);
159
+ bottom = Math.max(bottom, y);
160
+ }
161
+ }
162
+ }
163
+ if (right < 0) {
164
+ return { x: 0, y: 0, width, height };
165
+ }
166
+ return { x: left, y: top, width: right - left + 1, height: bottom - top + 1 };
167
+ }
168
+
75
169
  function compose(photo, frame) {
76
170
  const canvas = document.createElement('canvas');
77
171
  canvas.width = SIZE;
@@ -119,13 +119,13 @@ export function mediaFilesOfKind(kind) {
119
119
  }
120
120
  // Which editor a file opens in when no `editorModule` was resolved. Media
121
121
  // viewing (image / audio / video) lives on castle.base now; this only covers
122
- // types this kit still routes itself (scene / sprite / style). Each extension
122
+ // types this kit still routes itself (scene / sprite / theme). Each extension
123
123
  // gets its own line: legacy tooling matched one `endsWith` per `return`.
124
124
  export function getFileKind(path) {
125
125
  if (path.endsWith('.scene')) return 'scene';
126
126
  if (path.endsWith('.sprite')) return 'pxart';
127
127
  if (path.endsWith('.pxart')) return 'pxart';
128
- if (path.endsWith('.style')) return 'style';
128
+ if (path.endsWith('.style')) return 'theme';
129
129
  if (path.endsWith('.js') || path.endsWith('.jsx')) return 'code';
130
130
  return 'text';
131
131
  }
@@ -147,7 +147,7 @@ export function patchBody(body, actor) {
147
147
  // used the retired `kind: 'pickup'` label are still honored as sensors.
148
148
  body.isSensor = Boolean(collider.isTrigger || collider.kind === 'pickup');
149
149
  body.frictionAir = rb?.drag ?? 0.01;
150
- body.plugin.gravityScale = rb?.gravityScale ?? 1;
150
+ body.plugin.gravityScale = rb?.gravityScale ?? 0;
151
151
  body.plugin.angularDrag = rb?.angularDrag ?? 0;
152
152
  // Density sets mass (= density x area) -- DYNAMIC bodies only. On a static or
153
153
  // kinematic body, setDensity would replace the infinite mass that setStatic
@@ -172,7 +172,7 @@ export function syncBodyToPoint(body, point, angleDeg) {
172
172
  export function applyWorldGravity(bodies, gravity) {
173
173
  for (const body of bodies) {
174
174
  if (body.isStatic || body.isSleeping) continue;
175
- const scale = (body.plugin.gravityScale ?? 1) * gravity.scale;
175
+ const scale = (body.plugin.gravityScale ?? 0) * gravity.scale;
176
176
  body.force.x += body.mass * gravity.x * scale;
177
177
  body.force.y += body.mass * gravity.y * scale;
178
178
  }
@@ -66,7 +66,7 @@ export class SceneRuntime {
66
66
  this.camera = undefined;
67
67
  this.status = undefined;
68
68
  // The deck's font as a CSS font-family value, always usable: the face
69
- // picked in the Style editor, or a plain sans stack when there is none.
69
+ // picked in the Theme editor, or a plain sans stack when there is none.
70
70
  // For `draw` / `drawUi` hooks -- `ctx.font = `20px ${scene.font}`` -- since
71
71
  // canvas takes a family name, not a class. Prefer `drawUi` for HUD text;
72
72
  // the deprecated React `ui` overlay still inherits this face via CSS.
@@ -1,4 +1,4 @@
1
- // Generated by the Style editor. Do not edit -- picking a font rewrites it.
1
+ // Generated by the Theme editor. Do not edit -- picking a font rewrites it.
2
2
  //
3
3
  // `theme.style` records that this deck's font is DMSans; this module is what
4
4
  // puts DMSans's bytes in the published bundle. It has to name the face in a
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "../../sdk": {
34
34
  "name": "castle-web-sdk",
35
- "version": "0.4.26",
35
+ "version": "0.4.28",
36
36
  "dev": true,
37
37
  "devDependencies": {
38
38
  "eslint": "^9.0.0",
@@ -6,6 +6,7 @@
6
6
  import React from 'react';
7
7
  import { SelectField } from '@imports/castle.physics-2d/engine/ui';
8
8
  import { AutoFields, overrideProps } from '@imports/castle.physics-2d/engine/autoInspector';
9
+ import { POSITIVE } from '@imports/castle.physics-2d/engine/propertyRanges';
9
10
 
10
11
  const TYPE_OPTIONS = [
11
12
  { value: 'fixed', label: 'Fixed' },
@@ -25,6 +26,7 @@ function BodyInspector({ component, setComponent, override }) {
25
26
  />
26
27
  <AutoFields
27
28
  defaultProps={Body.defaultProps}
29
+ meta={Body.propertyMeta}
28
30
  component={component}
29
31
  setComponent={setComponent}
30
32
  exclude={['type']}
@@ -41,10 +43,17 @@ export class Body {
41
43
  type: 'fixed', // 'fixed' | 'dynamic' | 'kinematic'
42
44
  friction: 0.6,
43
45
  restitution: 0,
44
- gravityScale: 1,
46
+ // Match physics-2d RigidBody: newly added dynamics float until opted in.
47
+ gravityScale: 0,
45
48
  lockRotations: false,
46
49
  };
47
50
 
51
+ static propertyMeta = {
52
+ friction: POSITIVE,
53
+ restitution: POSITIVE,
54
+ gravityScale: { step: 0.1 },
55
+ };
56
+
48
57
  constructor(props) {
49
58
  this.props = props;
50
59
  }
@@ -4,6 +4,11 @@
4
4
  export class Door {
5
5
  static behaviorName = 'Door';
6
6
  static defaultProps = { range: 3, slide: 2.1, speed: 2.5 };
7
+ static propertyMeta = {
8
+ range: { min: 0, step: 0.1 },
9
+ slide: { min: 0, step: 0.1 },
10
+ speed: { min: 0, step: 0.1 },
11
+ };
7
12
 
8
13
  constructor(props) {
9
14
  this.props = props;
@@ -4,6 +4,9 @@
4
4
  export class Lookable {
5
5
  static behaviorName = 'Lookable';
6
6
  static defaultProps = { text: '', range: 4 };
7
+ static propertyMeta = {
8
+ range: { min: 0, step: 0.5 },
9
+ };
7
10
 
8
11
  constructor(props) {
9
12
  this.props = props;
@@ -7,6 +7,10 @@
7
7
  export class Model {
8
8
  static behaviorName = 'Model';
9
9
  static defaultProps = { file: '', scale: 1, yOffset: 0 };
10
+ static propertyMeta = {
11
+ scale: { min: 0, step: 0.05 },
12
+ yOffset: { step: 0.05 },
13
+ };
10
14
 
11
15
  constructor(props) {
12
16
  this.props = props;
@@ -14,6 +14,10 @@ function pickupKey(scene, actor) {
14
14
  export class Pickup {
15
15
  static behaviorName = 'Pickup';
16
16
  static defaultProps = { bob: 0.12, spin: 120 };
17
+ static propertyMeta = {
18
+ bob: { min: 0, step: 0.01 },
19
+ spin: { min: 0, step: 5 },
20
+ };
17
21
 
18
22
  constructor(props) {
19
23
  this.props = props;
@@ -112,6 +112,13 @@ function findLookText(scene, actor, t, facing) {
112
112
  export class Player {
113
113
  static behaviorName = 'Player';
114
114
  static defaultProps = { speed: 6, turnSpeed: 2.4, jumpSpeed: 8, camDistance: 9, camHeight: 7 };
115
+ static propertyMeta = {
116
+ speed: { min: 0, step: 0.5 },
117
+ turnSpeed: { min: 0, step: 0.1 },
118
+ jumpSpeed: { min: 0, step: 0.5 },
119
+ camDistance: { min: 0, step: 0.5 },
120
+ camHeight: { min: 0, step: 0.5 },
121
+ };
115
122
 
116
123
  constructor(props) {
117
124
  this.props = props;
@@ -8,6 +8,7 @@
8
8
  import React from 'react';
9
9
  import { SelectField, TextField } from '@imports/castle.physics-2d/engine/ui';
10
10
  import { AutoFields, overrideProps } from '@imports/castle.physics-2d/engine/autoInspector';
11
+ import { POSITIVE, UNIT } from '@imports/castle.physics-2d/engine/propertyRanges';
11
12
 
12
13
  const KIND_OPTIONS = [
13
14
  { value: 'box', label: 'Box' },
@@ -39,6 +40,7 @@ function ShapeInspector({ component, setComponent, override }) {
39
40
  )}
40
41
  <AutoFields
41
42
  defaultProps={Shape.defaultProps}
43
+ meta={Shape.propertyMeta}
42
44
  component={component}
43
45
  setComponent={setComponent}
44
46
  exclude={['kind', 'art', 'artRepeat']}
@@ -69,6 +71,17 @@ export class Shape {
69
71
  solid: false,
70
72
  };
71
73
 
74
+ static propertyMeta = {
75
+ width: POSITIVE,
76
+ height: POSITIVE,
77
+ depth: POSITIVE,
78
+ radius: POSITIVE,
79
+ roughness: UNIT,
80
+ metalness: UNIT,
81
+ clearcoat: UNIT,
82
+ emissiveIntensity: POSITIVE,
83
+ };
84
+
72
85
  constructor(props) {
73
86
  this.props = props;
74
87
  }
@@ -69,5 +69,5 @@
69
69
  "autoUpdateWhenImported": true,
70
70
  "deckId": "JH0SclbPVP0y",
71
71
  "cardId": "eXfAaUdbSU5A",
72
- "publishedVersion": "2026-09-10T20:10:28.916Z"
72
+ "publishedVersion": "2026-09-10T22:05:11.276Z"
73
73
  }
@@ -332,6 +332,7 @@ function BehaviorPanel({
332
332
  component={component}
333
333
  setComponent={setComponent}
334
334
  override={override}
335
+ meta={Behavior?.propertyMeta}
335
336
  />
336
337
  );
337
338
  // Transform is core to every actor; a component an instance only has via
@@ -1,4 +1,4 @@
1
- // Generated by the Style editor. Do not edit -- picking a font rewrites it.
1
+ // Generated by the Theme editor. Do not edit -- picking a font rewrites it.
2
2
  //
3
3
  // `theme.style` records that this deck's font is DMSans; this module is what
4
4
  // puts DMSans's bytes in the published bundle. It has to name the face in a
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "../../sdk": {
36
36
  "name": "castle-web-sdk",
37
- "version": "0.4.26",
37
+ "version": "0.4.28",
38
38
  "dev": true,
39
39
  "devDependencies": {
40
40
  "eslint": "^9.0.0",
@@ -15,5 +15,5 @@
15
15
  "autoUpdateWhenImported": true,
16
16
  "deckId": "xFVr-afOLuUQ",
17
17
  "cardId": "w6G45sy3_P_m",
18
- "publishedVersion": "2026-09-10T20:28:42.134Z"
18
+ "publishedVersion": "2026-09-10T22:05:27.337Z"
19
19
  }
@@ -91,7 +91,16 @@ export async function connectSession({ onLog = () => {} } = {}) {
91
91
  sendMove(net, pose, owned, claims, releases, state),
92
92
 
93
93
  // Send deck-specific data. The server routes these to `code/server/game.js`.
94
- send: (data) => net.connection.send(data),
94
+ send: (data) => {
95
+ if (connection.state !== 'closed') {
96
+ net.connection.send(data);
97
+ }
98
+ },
99
+
100
+ // Whether the session has ended under this connection: the server was
101
+ // replaced, the platform closed it, or `close` was called. Nothing is sent
102
+ // on it again; the client system joins afresh.
103
+ closed: () => connection.state === 'closed',
95
104
 
96
105
  // Connection and presence data for a deck status line.
97
106
  status: () => ({
@@ -127,7 +136,7 @@ function sendMove(net, pose, owned, claims, releases, state) {
127
136
  net.releases.add(id);
128
137
  }
129
138
  const now = performance.now();
130
- if (now - net.sentAt < SEND_MS) {
139
+ if (now - net.sentAt < SEND_MS || net.connection.state === 'closed') {
131
140
  return false;
132
141
  }
133
142
  net.sentAt = now;
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "../../sdk": {
18
18
  "name": "castle-web-sdk",
19
- "version": "0.4.27",
19
+ "version": "0.4.28",
20
20
  "dev": true,
21
21
  "devDependencies": {
22
22
  "eslint": "^9.0.0",
@@ -28,7 +28,7 @@
28
28
  "autoUpdateWhenImported": true,
29
29
  "deckId": "K6-Xs7dCoMVi",
30
30
  "cardId": "Kh_08IYATpC3",
31
- "publishedVersion": "2026-09-10T20:28:55.394Z",
31
+ "publishedVersion": "2026-09-10T22:05:30.856Z",
32
32
  "server": {
33
33
  "main": "code/server/index.js",
34
34
  "maxPlayers": 8,
@@ -1,4 +1,4 @@
1
- // Generated by the Style editor. Do not edit -- picking a font rewrites it.
1
+ // Generated by the Theme editor. Do not edit -- picking a font rewrites it.
2
2
  //
3
3
  // `theme.style` records that this deck's font is DMSans; this module is what
4
4
  // puts DMSans's bytes in the published bundle. It has to name the face in a
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "../../sdk": {
30
30
  "name": "castle-web-sdk",
31
- "version": "0.4.26",
31
+ "version": "0.4.28",
32
32
  "dev": true,
33
33
  "devDependencies": {
34
34
  "eslint": "^9.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.172",
3
+ "version": "0.4.174",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"
@@ -41,6 +41,7 @@
41
41
  "@xterm/headless": "^6.0.0",
42
42
  "@xterm/xterm": "^6.0.0",
43
43
  "codemirror": "^6.0.2",
44
+ "compression": "^1.8.1",
44
45
  "html2canvas": "^1.4.1",
45
46
  "marked": "^18.0.5",
46
47
  "nanoid": "^5.1.7",
@@ -54,6 +55,7 @@
54
55
  "ws": "^8.20.0"
55
56
  },
56
57
  "devDependencies": {
58
+ "@types/compression": "^1.8.1",
57
59
  "@types/node": "^20.0.0",
58
60
  "@types/picomatch": "^4.0.2",
59
61
  "@types/react": "^18.3.31",