@bitmagic/cli 0.1.41 → 0.1.43

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.
@@ -0,0 +1,217 @@
1
+ /**
2
+ * The Bitmagic design system, for the pages `bitmagic dev` serves.
3
+ *
4
+ * The dev view is a Bitmagic product surface — for a pro-lane creator it is the only one they ever
5
+ * look at — so it follows `creator/DESIGN.md` the way the Creator and the in-engine editor do.
6
+ * It cannot import the Creator's `BitmagicDesign.css`: this package ships on npm on its own, and
7
+ * both pages are strings assembled in TypeScript rather than bundled documents. So it re-declares
8
+ * the same tokens, exactly as `game/src/editor/editor-styles.ts` does for the engine's editor
9
+ * dialogs — three copies of the palette, one source of truth in DESIGN.md.
10
+ *
11
+ * **No backticks in anything exported here.** These constants are interpolated into the pages'
12
+ * template literals, and a backtick would end the literal somewhere else entirely (there is a test
13
+ * for it: `__tests__/shell-page.test.ts`).
14
+ */
15
+ /** Hex tokens for TS-side use — the same set `game/src/editor/editor-styles.ts` exposes. */
16
+ export const BM = {
17
+ bg: '#0b0b0b',
18
+ surface: '#181818',
19
+ surfaceAlt: '#1f1f1f',
20
+ surfaceCard: '#252525',
21
+ textPrimary: '#ffffff',
22
+ textMuted: '#b3b3b3',
23
+ textDim: '#7c7c7c',
24
+ textOnAccent: '#000000',
25
+ border: '#4d4d4d',
26
+ borderMuted: '#2a2a2a',
27
+ aqua: '#A0DAB9',
28
+ aquaHover: '#B8E4CA',
29
+ aquaPressed: '#7DC29C',
30
+ pink: '#E0218A',
31
+ pinkHover: '#F04AA4',
32
+ pinkPressed: '#B3176D',
33
+ pumpkin: '#FF8200',
34
+ canary: '#F9E547',
35
+ shadowDialog: '0 8px 24px rgba(0, 0, 0, 0.5)',
36
+ shadowCard: '0 8px 8px rgba(0, 0, 0, 0.3)',
37
+ glowAqua: '0 0 24px rgba(160, 218, 185, 0.45)',
38
+ glowPink: '0 0 24px rgba(224, 33, 138, 0.4)',
39
+ insetBorderDim: 'inset 0 0 0 1px #7c7c7c',
40
+ insetBorderFocus: 'inset 0 0 0 1px #ffffff, 0 0 0 2px rgba(160, 218, 185, 0.45)',
41
+ font: '"Red Hat Display", "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif',
42
+ fontMono: 'ui-monospace, SFMono-Regular, Menlo, monospace',
43
+ };
44
+ /**
45
+ * Red Hat Display, from the same Google Fonts URL `creator/index.html` uses.
46
+ *
47
+ * A dev machine with no network gets the fallback stack and a page that is otherwise identical —
48
+ * every rule below sizes and weights type rather than depending on the family's metrics.
49
+ */
50
+ export const BITMAGIC_FONT_LINK = `<link rel="preconnect" href="https://fonts.googleapis.com">
51
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
52
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Red+Hat+Display:wght@300;400;500;700&display=swap">`;
53
+ /** The palette as custom properties, so page-specific rules can `var(--bm-*)` them. */
54
+ export const BITMAGIC_TOKENS_CSS = `
55
+ :root {
56
+ color-scheme: dark;
57
+ --bm-bg: ${BM.bg};
58
+ --bm-surface: ${BM.surface};
59
+ --bm-surface-alt: ${BM.surfaceAlt};
60
+ --bm-surface-card: ${BM.surfaceCard};
61
+ --bm-text: ${BM.textPrimary};
62
+ --bm-text-muted: ${BM.textMuted};
63
+ --bm-text-dim: ${BM.textDim};
64
+ --bm-border: ${BM.border};
65
+ --bm-border-muted: ${BM.borderMuted};
66
+ --bm-aqua: ${BM.aqua};
67
+ --bm-aqua-hover: ${BM.aquaHover};
68
+ --bm-pink: ${BM.pink};
69
+ --bm-pink-hover: ${BM.pinkHover};
70
+ --bm-pumpkin: ${BM.pumpkin};
71
+ --bm-canary: ${BM.canary};
72
+ /* Accent washes. An accent at full strength behind body copy would be a coloured surface,
73
+ which DESIGN.md does not do — these are the same five accents at banner strength. */
74
+ --bm-tint-warning: rgba(255, 130, 0, .12);
75
+ --bm-tint-negative: rgba(224, 33, 138, .12);
76
+ --bm-tint-success: rgba(160, 218, 185, .18);
77
+ --bm-scrim: rgba(11, 11, 11, .78);
78
+ --bm-shadow-dialog: ${BM.shadowDialog};
79
+ --bm-shadow-card: ${BM.shadowCard};
80
+ --bm-glow-aqua: ${BM.glowAqua};
81
+ --bm-glow-pink: ${BM.glowPink};
82
+ --bm-font: ${BM.font};
83
+ --bm-font-mono: ${BM.fontMono};
84
+ }
85
+ `;
86
+ /**
87
+ * The component primitives both served pages use: pill buttons, inputs, badges, modal surfaces.
88
+ *
89
+ * Label voice is UPPERCASE Bold with tracking and content voice is sentence-case Regular, per the
90
+ * brand type guideline — which is why every button here is uppercase and nothing else is.
91
+ */
92
+ export const BITMAGIC_CONTROLS_CSS = `
93
+ /* === Pill buttons =========================================================================
94
+ Secondary dark pill is the default: the toolbar is chrome, and DESIGN.md allows one primary
95
+ accent per screen. Aquamarine is spent on the active tab and on a dialog's confirm. */
96
+ .bm-pill {
97
+ background: var(--bm-surface-alt);
98
+ color: var(--bm-text);
99
+ border: 1px solid var(--bm-border);
100
+ border-radius: 9999px;
101
+ padding: 5px 14px;
102
+ font-family: var(--bm-font);
103
+ font-size: 12px;
104
+ font-weight: 700;
105
+ line-height: 1;
106
+ text-transform: uppercase;
107
+ letter-spacing: 1.4px;
108
+ cursor: pointer;
109
+ transition: background .15s ease, border-color .15s ease, color .15s ease,
110
+ box-shadow .15s ease, transform .15s ease;
111
+ }
112
+ .bm-pill:hover:not(:disabled) { background: var(--bm-surface-card); border-color: var(--bm-text-dim); }
113
+ .bm-pill:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(160, 218, 185, .45); }
114
+ .bm-pill:disabled { opacity: .5; cursor: default; }
115
+
116
+ /* Primary — the one creative action on a surface. Dark text: DESIGN.md forbids white on the
117
+ lighter accents. */
118
+ .bm-pill--primary {
119
+ background: var(--bm-aqua);
120
+ border-color: var(--bm-aqua);
121
+ color: #000000;
122
+ letter-spacing: 1.6px;
123
+ padding: 7px 20px;
124
+ }
125
+ .bm-pill--primary:hover:not(:disabled) {
126
+ background: var(--bm-aqua-hover);
127
+ border-color: var(--bm-aqua-hover);
128
+ transform: scale(1.03);
129
+ box-shadow: var(--bm-glow-aqua);
130
+ }
131
+
132
+ /* Destructive — Shocking Pink, and only ever for discard / delete / reset. */
133
+ .bm-pill--destructive {
134
+ background: var(--bm-pink);
135
+ border-color: var(--bm-pink);
136
+ color: var(--bm-text);
137
+ letter-spacing: 1.6px;
138
+ }
139
+ .bm-pill--destructive:hover:not(:disabled) {
140
+ background: var(--bm-pink-hover);
141
+ border-color: var(--bm-pink-hover);
142
+ transform: scale(1.03);
143
+ box-shadow: var(--bm-glow-pink);
144
+ }
145
+
146
+ /* Outlined — tertiary ("Cancel", "Close"). */
147
+ .bm-pill--ghost { background: transparent; border-color: var(--bm-text-dim); color: var(--bm-text-muted); }
148
+ .bm-pill--ghost:hover:not(:disabled) { background: transparent; border-color: var(--bm-text); color: var(--bm-text); }
149
+
150
+ /* === Input ================================================================================ */
151
+ .bm-input {
152
+ background: var(--bm-surface-alt);
153
+ color: var(--bm-text);
154
+ border: none;
155
+ box-shadow: ${BM.insetBorderDim};
156
+ border-radius: 6px;
157
+ padding: 10px 12px;
158
+ font-family: var(--bm-font);
159
+ font-size: 14px;
160
+ outline: none;
161
+ }
162
+ .bm-input:focus { box-shadow: ${BM.insetBorderFocus}; }
163
+ .bm-input::placeholder { color: var(--bm-text-muted); }
164
+
165
+ /* === Badge ================================================================================ */
166
+ .bm-badge {
167
+ display: inline-block;
168
+ border-radius: 9999px;
169
+ padding: 1px 7px;
170
+ font-size: 10.5px;
171
+ font-weight: 700;
172
+ line-height: 1.33;
173
+ text-transform: uppercase;
174
+ letter-spacing: 1.4px;
175
+ vertical-align: 1px;
176
+ background: var(--bm-surface-card);
177
+ color: var(--bm-text-muted);
178
+ }
179
+ /* Success / featured, and warning. Never both on one row — an asset is one or the other. */
180
+ .bm-badge--success { background: var(--bm-aqua); color: #000000; }
181
+ .bm-badge--warning { background: var(--bm-pumpkin); color: #000000; }
182
+
183
+ /* === Label voice ==========================================================================
184
+ Section headers and anything else speaking as a label rather than as content. */
185
+ .bm-label {
186
+ font-size: 12px;
187
+ font-weight: 700;
188
+ text-transform: uppercase;
189
+ letter-spacing: 1.4px;
190
+ color: var(--bm-text-muted);
191
+ }
192
+
193
+ /* === Modal ================================================================================ */
194
+ .bm-overlay {
195
+ display: none;
196
+ position: fixed;
197
+ inset: 0;
198
+ background: var(--bm-scrim);
199
+ align-items: center;
200
+ justify-content: center;
201
+ }
202
+ .bm-overlay.show { display: flex; }
203
+ .bm-dialog {
204
+ background: var(--bm-surface);
205
+ border: 1px solid var(--bm-border-muted);
206
+ border-radius: 8px;
207
+ box-shadow: var(--bm-shadow-dialog);
208
+ }
209
+ .bm-dialog h2 {
210
+ margin: 0 0 6px;
211
+ font-size: 16px;
212
+ font-weight: 700;
213
+ text-transform: uppercase;
214
+ letter-spacing: .5px;
215
+ }
216
+ `;
217
+ //# sourceMappingURL=design.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design.js","sourceRoot":"","sources":["../../src/editor/design.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,EAAE,EAAE,SAAS;IACb,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,SAAS;IACrB,WAAW,EAAE,SAAS;IAEtB,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,SAAS;IACpB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,SAAS;IAEvB,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,SAAS;IAEtB,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IAEjB,YAAY,EAAE,+BAA+B;IAC7C,UAAU,EAAE,8BAA8B;IAC1C,QAAQ,EAAE,oCAAoC;IAC9C,QAAQ,EAAE,kCAAkC;IAC5C,cAAc,EAAE,yBAAyB;IACzC,gBAAgB,EAAE,8DAA8D;IAEhF,IAAI,EAAE,8EAA8E;IACpF,QAAQ,EAAE,gDAAgD;CAClD,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;0HAEwF,CAAC;AAE3H,uFAAuF;AACvF,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;eAGpB,EAAE,CAAC,EAAE;oBACA,EAAE,CAAC,OAAO;wBACN,EAAE,CAAC,UAAU;yBACZ,EAAE,CAAC,WAAW;iBACtB,EAAE,CAAC,WAAW;uBACR,EAAE,CAAC,SAAS;qBACd,EAAE,CAAC,OAAO;mBACZ,EAAE,CAAC,MAAM;yBACH,EAAE,CAAC,WAAW;iBACtB,EAAE,CAAC,IAAI;uBACD,EAAE,CAAC,SAAS;iBAClB,EAAE,CAAC,IAAI;uBACD,EAAE,CAAC,SAAS;oBACf,EAAE,CAAC,OAAO;mBACX,EAAE,CAAC,MAAM;;;;;;;0BAOF,EAAE,CAAC,YAAY;wBACjB,EAAE,CAAC,UAAU;sBACf,EAAE,CAAC,QAAQ;sBACX,EAAE,CAAC,QAAQ;iBAChB,EAAE,CAAC,IAAI;sBACF,EAAE,CAAC,QAAQ;;CAEhC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+DnB,EAAE,CAAC,cAAc;;;;;;;kCAOD,EAAE,CAAC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDpD,CAAC"}
@@ -51,6 +51,7 @@
51
51
  import { decodeForgeMapHeights, renderForgeMapRgba, } from '@bitmagic/world-forger/pipeline/forge-map-render.js';
52
52
  import { FORGE_MAP_PALETTE } from '@bitmagic/world-forger/pipeline/layout-map.js';
53
53
  import { ASSET_DETAIL_CSS, ASSET_DETAIL_HTML } from './asset-detail-panel.js';
54
+ import { BITMAGIC_CONTROLS_CSS, BITMAGIC_FONT_LINK, BITMAGIC_TOKENS_CSS } from './design.js';
54
55
  /** How often the shell asks the engine whether anything changed. A `Set` read; effectively free. */
55
56
  const POLL_INTERVAL_MS = 500;
56
57
  /**
@@ -104,134 +105,143 @@ export function renderEditorShell(options) {
104
105
  <head>
105
106
  <meta charset="utf-8">
106
107
  <title>bitmagic dev — ${escapeAttr(options.gameId)}</title>
108
+ ${BITMAGIC_FONT_LINK}
107
109
  <style>
108
- :root { color-scheme: dark; }
110
+ ${BITMAGIC_TOKENS_CSS}
111
+ ${BITMAGIC_CONTROLS_CSS}
109
112
  * { box-sizing: border-box; }
110
113
  /* Flex column rather than calc() heights: the banners come and go, and three of them at once
111
114
  must not push the viewport into a scrollbar. */
112
115
  body { margin: 0; height: 100vh; display: flex; flex-direction: column;
113
- background: #101014; color: #e6e6ea; font: 13px/1.45 ui-sans-serif, system-ui, sans-serif; }
114
- #bar { display: flex; align-items: center; gap: 12px; height: 36px; flex: none; padding: 0 12px;
115
- background: #17171d; border-bottom: 1px solid #26262e; }
116
- #bar .id { color: #8a8a99; font-family: ui-monospace, monospace; }
116
+ background: var(--bm-bg); color: var(--bm-text); font: 400 14px/1.5 var(--bm-font); }
117
+ #bar { display: flex; align-items: center; gap: 12px; height: 44px; flex: none; padding: 0 16px;
118
+ background: var(--bm-surface); border-bottom: 1px solid var(--bm-border-muted); }
119
+ /* The wordmark speaks in the brand's headline voice: uppercase, bold, tracked. */
120
+ #bar .wordmark { font-size: 14px; font-weight: 700; text-transform: uppercase;
121
+ letter-spacing: 1.6px; }
122
+ #bar .id { color: var(--bm-text-dim); font-family: var(--bm-font-mono); font-size: 12px; }
117
123
  #bar .spacer { flex: 1; }
118
- #tabs { display: flex; gap: 2px; background: #101014; border: 1px solid #26262e; border-radius: 6px;
119
- padding: 2px; }
120
- .tab { background: transparent; border: 0; border-radius: 4px; color: #9a9aab; padding: 3px 14px;
121
- font: inherit; cursor: pointer; }
122
- .tab:hover { color: #e6e6ea; background: #22222a; }
123
- .tab.active { background: #3352c4; color: #fff; }
124
- #status { display: flex; align-items: center; gap: 6px; }
125
- #dot { width: 8px; height: 8px; border-radius: 50%; background: #4a4a57; }
126
- #dot.saving { background: #d9a441; }
127
- #dot.saved { background: #4caf72; }
128
- #dot.error { background: #e0564f; }
129
- #auto { display: flex; align-items: center; gap: 5px; color: #8a8a99; cursor: pointer; }
130
- #auto input { margin: 0; }
131
- button { background: #26262e; color: #e6e6ea; border: 1px solid #34343f; border-radius: 5px;
132
- padding: 4px 10px; font: inherit; cursor: pointer; }
133
- button:hover { background: #30303a; }
134
- button:disabled { opacity: .5; cursor: default; background: #26262e; }
135
- /* .tab:hover is declared after .tab.active and would otherwise win on an active tab. */
136
- #tabs .tab.active:hover { background: #3352c4; }
137
- .banner { display: none; align-items: center; gap: 12px; flex: none; padding: 8px 12px;
138
- background: #3a2c12; border-bottom: 1px solid #574018; color: #f0d9a8; }
124
+ #tabs { display: flex; gap: 4px; background: var(--bm-surface-alt); border-radius: 9999px;
125
+ padding: 3px; }
126
+ /* A tab is a .bm-pill that starts invisible; the ACTIVE one is what the accent is for —
127
+ DESIGN.md gives Aquamarine to primary actions and active states alike. */
128
+ .tab { background: transparent; border-color: transparent; color: var(--bm-text-muted); }
129
+ .tab:hover:not(.active) { background: var(--bm-surface-card); border-color: transparent;
130
+ color: var(--bm-text); }
131
+ .tab.active, .tab.active:hover { background: var(--bm-aqua); border-color: var(--bm-aqua);
132
+ color: #000000; }
133
+ #status { display: flex; align-items: center; gap: 8px; font-size: 12px;
134
+ color: var(--bm-text-muted); }
135
+ /* Working is Pumpkin, done is Aquamarine, failed is Shocking Pink the same three the steps,
136
+ the progress bar and the banners below use, so one colour never means two things. */
137
+ #dot { width: 8px; height: 8px; border-radius: 50%; background: var(--bm-border); }
138
+ #dot.saving { background: var(--bm-pumpkin); }
139
+ #dot.saved { background: var(--bm-aqua); }
140
+ #dot.error { background: var(--bm-pink); }
141
+ #auto { display: flex; align-items: center; gap: 6px; font-size: 12px;
142
+ color: var(--bm-text-muted); cursor: pointer; }
143
+ #auto input { margin: 0; accent-color: var(--bm-aqua); }
144
+ .banner { display: none; align-items: center; gap: 12px; flex: none; padding: 10px 16px;
145
+ background: var(--bm-tint-warning); border-bottom: 1px solid var(--bm-pumpkin);
146
+ color: var(--bm-text); }
139
147
  .banner.show { display: flex; }
140
- #terrain-banner, #build-banner { background: #3d1f1c; border-bottom-color: #6b2f28; color: #f3c3bd; }
141
- #build-banner code { font-family: ui-monospace, monospace; }
148
+ /* A lost edit and a broken build are worse than out-of-date, and read as negative states. */
149
+ #terrain-banner, #build-banner { background: var(--bm-tint-negative);
150
+ border-bottom-color: var(--bm-pink); }
151
+ #build-banner code, #terrain-banner code { font-family: var(--bm-font-mono); }
142
152
  /* The game and the panel share one row, so the panel takes width from the game rather than
143
153
  overlaying it — an overlay would cover the thing you opened the panel to watch. */
144
154
  #stage { flex: 1; display: flex; min-height: 0; }
145
155
  #frame { display: block; flex: 1; border: 0; min-width: 0; height: 100%; }
146
- #assets { width: 264px; flex: none; overflow-y: auto; background: #131318;
147
- border-left: 1px solid #26262e; }
156
+ #assets { width: 280px; flex: none; overflow-y: auto; background: var(--bm-surface);
157
+ border-left: 1px solid var(--bm-border-muted); }
148
158
  body.assets-closed #assets { display: none; }
149
- #assets-head { position: sticky; top: 0; display: flex; align-items: center; gap: 8px;
150
- padding: 8px 10px; background: #17171d; border-bottom: 1px solid #26262e;
151
- font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #8a8a99; }
152
- #assets-head .count { margin-left: auto; color: #6a6a7a; letter-spacing: 0; }
153
- #assets-empty { padding: 14px 10px; color: #6a6a7a; }
154
- .row { display: flex; gap: 9px; padding: 8px 10px; border-bottom: 1px solid #1e1e26; }
159
+ #assets-head { position: sticky; top: 0; display: flex; align-items: center; gap: 8px; z-index: 1;
160
+ padding: 12px; background: var(--bm-surface-alt);
161
+ border-bottom: 1px solid var(--bm-border-muted); }
162
+ #assets-head .count { margin-left: auto; color: var(--bm-text-dim); letter-spacing: 0; }
163
+ #assets-empty { padding: 16px 12px; color: var(--bm-text-dim); font-size: 12px; }
164
+ .row { display: flex; gap: 12px; padding: 10px 12px;
165
+ border-bottom: 1px solid var(--bm-border-muted); }
155
166
  .row.clickable { cursor: pointer; }
156
- .row.clickable:hover, .row.clickable:focus-visible { background: #1b1b23; outline: none; }
157
- .row .thumb { width: 46px; height: 46px; flex: none; border-radius: 4px; background: #1e1e26;
158
- display: flex; align-items: center; justify-content: center; font-size: 18px;
159
- color: #55556a; overflow: hidden; }
167
+ .row.clickable:hover, .row.clickable:focus-visible { background: var(--bm-surface-card);
168
+ outline: none; }
169
+ .row .thumb { width: 48px; height: 48px; flex: none; border-radius: 6px;
170
+ background: var(--bm-surface-alt); display: flex; align-items: center;
171
+ justify-content: center; font-size: 18px; color: var(--bm-text-dim);
172
+ overflow: hidden; }
160
173
  .row .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
161
174
  .row .body { min-width: 0; flex: 1; }
162
- .row .name { color: #e6e6ea; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
163
- .row .meta { color: #6a6a7a; font-size: 11px; overflow: hidden; text-overflow: ellipsis;
175
+ .row .name { color: var(--bm-text); overflow: hidden; text-overflow: ellipsis;
164
176
  white-space: nowrap; }
165
- .row .chip { display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 3px;
166
- font-size: 10px; background: #2a2a34; color: #9a9aab; vertical-align: 1px; }
167
- .row .chip.hq { background: #1f3a2a; color: #8fd0a4; }
168
- .row .chip.placeholder { background: #3a3320; color: #d9c08a; }
169
- /* A new asset during a forge is the whole show, so it announces itself and then settles. */
177
+ .row .name .bm-badge { margin-left: 6px; }
178
+ .row .meta { color: var(--bm-text-dim); font-size: 12px; overflow: hidden;
179
+ text-overflow: ellipsis; white-space: nowrap; }
180
+ /* A new asset during a forge is the whole show, so it announces itself with the Aquamarine
181
+ "something landed" flourish and then settles. */
170
182
  .row.fresh { animation: landed 2.4s ease-out; }
171
- @keyframes landed { from { background: #22314f; } to { background: transparent; } }
172
- .row.job .thumb { color: #d9a441; }
173
- .row.job .message { color: #9a9aab; font-size: 11px; overflow: hidden; text-overflow: ellipsis;
174
- white-space: nowrap; }
183
+ @keyframes landed { from { background: var(--bm-tint-success); } to { background: transparent; } }
184
+ .row.job .thumb { color: var(--bm-pumpkin); }
185
+ .row.job .message { color: var(--bm-text-muted); font-size: 12px; overflow: hidden;
186
+ text-overflow: ellipsis; white-space: nowrap; }
175
187
  /* The forge's five steps. A whole run is half an hour, so this is what the panel is for. */
176
- .steps { margin-top: 5px; font-size: 11px; }
177
- .step { display: flex; align-items: baseline; gap: 5px; color: #55556a; line-height: 1.6; }
178
- .step.running { color: #d9d9e2; }
179
- .step.done { color: #7c8f7f; }
180
- .step .glyph { width: 9px; flex: none; text-align: center; }
188
+ .steps { margin-top: 6px; font-size: 12px; }
189
+ .step { display: flex; align-items: baseline; gap: 6px; color: var(--bm-text-dim);
190
+ line-height: 1.6; }
191
+ .step.running { color: var(--bm-text); }
192
+ .step.done { color: var(--bm-aqua); }
193
+ .step .glyph { width: 10px; flex: none; text-align: center; }
181
194
  .step .label { flex: none; }
182
195
  .step .tail { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis;
183
- white-space: nowrap; color: #6a6a7a; }
184
- .step.running .tail { color: #9a9aab; }
185
- .bar { height: 3px; margin: 3px 0 4px 14px; border-radius: 2px; background: #26262e;
186
- overflow: hidden; }
187
- .bar .fill { height: 100%; background: #d9a441; }
196
+ white-space: nowrap; color: var(--bm-text-dim); }
197
+ .step.running .tail { color: var(--bm-text-muted); }
198
+ .bar { height: 3px; margin: 4px 0 4px 16px; border-radius: 2px;
199
+ background: var(--bm-surface-card); overflow: hidden; }
200
+ .bar .fill { height: 100%; background: var(--bm-pumpkin); }
188
201
  /* The level's shape, drawn from the forge artifact — twenty-plus minutes before it is playable.
189
202
  image-rendering: pixelated because the canvas is drawn at an integer multiple of the raster
190
203
  and any further scaling should keep the object dots as dots. */
191
- .job-map { margin-top: 7px; }
192
- .job-map canvas { display: block; width: 100%; height: auto; border-radius: 4px;
193
- border: 1px solid #26262e; image-rendering: pixelated; }
204
+ .job-map { margin-top: 8px; }
205
+ .job-map canvas { display: block; width: 100%; height: auto; border-radius: 6px;
206
+ border: 1px solid var(--bm-border-muted); image-rendering: pixelated; }
194
207
  /* What the level being built contains — known minutes before the level itself exists. */
195
- .job-summary { margin-top: 6px; padding-top: 6px; border-top: 1px solid #1e1e26;
196
- color: #8a8a99; font-size: 11px; line-height: 1.6; }
197
- .job-summary .objects { color: #6a6a7a; overflow: hidden; text-overflow: ellipsis;
208
+ .job-summary { margin-top: 8px; padding-top: 8px;
209
+ border-top: 1px solid var(--bm-border-muted); color: var(--bm-text-muted);
210
+ font-size: 12px; line-height: 1.6; }
211
+ .job-summary .objects { color: var(--bm-text-dim); overflow: hidden; text-overflow: ellipsis;
198
212
  white-space: nowrap; }
199
213
  .spin { display: inline-block; animation: spin 1.4s linear infinite; }
200
214
  @keyframes spin { to { transform: rotate(360deg); } }
201
- #hq-overlay { display: none; position: fixed; inset: 0; background: rgba(8,8,12,.72);
202
- align-items: center; justify-content: center; z-index: 10; }
203
- #hq-overlay.show { display: flex; }
204
- #hq-dialog { width: min(560px, 92vw); background: #17171d; border: 1px solid #34343f;
205
- border-radius: 8px; padding: 18px; }
206
- #hq-dialog h2 { margin: 0 0 6px; font-size: 15px; }
207
- #hq-dialog p { margin: 0 0 12px; color: #9a9aab; }
208
- #hq-prompt { width: 100%; min-height: 88px; resize: vertical; padding: 8px; border-radius: 6px;
209
- background: #101014; color: #e6e6ea; border: 1px solid #34343f; font: inherit; }
210
- #hq-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
211
- #hq-confirm { background: #3352c4; border-color: #3f61d8; }
212
- #hq-note { display: none; margin-top: 10px; padding: 10px; border-radius: 6px;
213
- background: #101014; border: 1px solid #26262e; }
215
+ #hq-overlay { z-index: 10; }
216
+ #hq-dialog { width: min(560px, 92vw); padding: 20px; }
217
+ #hq-dialog p { margin: 0 0 16px; color: var(--bm-text-muted); }
218
+ #hq-prompt { width: 100%; min-height: 88px; resize: vertical; }
219
+ #hq-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
220
+ #hq-note { display: none; margin-top: 12px; padding: 12px; border-radius: 6px; font-size: 12px;
221
+ background: var(--bm-surface-alt); border: 1px solid var(--bm-border-muted);
222
+ color: var(--bm-text-muted); }
214
223
  #hq-note.show { display: block; }
215
- #hq-note code { display: block; margin-top: 6px; color: #9fd0a8; word-break: break-all; }
224
+ #hq-note code { display: block; margin-top: 6px; color: var(--bm-aqua);
225
+ font-family: var(--bm-font-mono); word-break: break-all; }
216
226
  ${ASSET_DETAIL_CSS}
217
227
  </style>
218
228
  </head>
219
229
  <body>
220
230
  <div id="bar">
221
- <strong>bitmagic dev</strong>
231
+ <span class="wordmark">bitmagic dev</span>
222
232
  <span class="id">${escapeAttr(options.gameId)}</span>
223
233
  <div id="tabs">
224
- <button class="tab active" type="button" data-tab="game">Game</button>
225
- <button class="tab" type="button" data-tab="editor">Editor</button>
234
+ <button class="tab bm-pill active" type="button" data-tab="game">Game</button>
235
+ <button class="tab bm-pill" type="button" data-tab="editor">Editor</button>
226
236
  </div>
227
237
  <span class="spacer"></span>
228
238
  <label id="auto" title="Reload the game when the project changes on disk">
229
239
  <input id="auto-toggle" type="checkbox" checked> Auto-reload
230
240
  </label>
231
241
  <span id="status"><span id="dot"></span><span id="status-text">Loading…</span></span>
232
- <button id="assets-toggle" type="button" title="Show the assets in this project and what is being generated">Assets</button>
233
- <button id="shot" type="button" title="Copy the current frame to the clipboard">Screenshot</button>
234
- <button id="reload" type="button">Reload</button>
242
+ <button id="assets-toggle" class="bm-pill" type="button" title="Show the assets in this project and what is being made">Assets</button>
243
+ <button id="shot" class="bm-pill" type="button" title="Copy the current frame to the clipboard">Screenshot</button>
244
+ <button id="reload" class="bm-pill" type="button">Reload</button>
235
245
  </div>
236
246
  <div id="build-banner" class="banner">
237
247
  <span id="build-message"></span>
@@ -239,32 +249,32 @@ ${ASSET_DETAIL_CSS}
239
249
  </div>
240
250
  <div id="banner" class="banner">
241
251
  <span>src/work/world.json changed on disk — this scene is out of date.</span>
242
- <button id="banner-reload" type="button">Reload scene</button>
252
+ <button id="banner-reload" class="bm-pill" type="button">Reload scene</button>
243
253
  </div>
244
254
  <div id="terrain-banner" class="banner">
245
255
  <span><strong>Terrain edits are not saved yet.</strong>
246
256
  Sculpting the ground needs an asset upload that <code>bitmagic dev</code> does not do — these
247
257
  voxel changes will be lost. Discard them, or press Cancel in the terrain toolbar.</span>
248
- <button id="terrain-discard" type="button">Discard terrain edits</button>
258
+ <button id="terrain-discard" class="bm-pill bm-pill--destructive" type="button">Discard terrain edits</button>
249
259
  </div>
250
260
  <div id="stage">
251
261
  <iframe id="frame" allow="autoplay; fullscreen; xr-spatial-tracking; clipboard-write"></iframe>
252
262
  <aside id="assets">
253
- <div id="assets-head">Assets<span class="count" id="assets-count"></span></div>
263
+ <div id="assets-head" class="bm-label">Assets<span class="count" id="assets-count"></span></div>
254
264
  <div id="assets-jobs"></div>
255
265
  <div id="assets-list"></div>
256
- <div id="assets-empty">Nothing yet. Assets appear here as they are generated.</div>
266
+ <div id="assets-empty">Nothing yet. Assets appear here as they are made.</div>
257
267
  </aside>
258
268
  </div>
259
- <div id="hq-overlay">
260
- <div id="hq-dialog">
261
- <h2>Generate a high-quality <span id="hq-name"></span></h2>
269
+ <div id="hq-overlay" class="bm-overlay">
270
+ <div id="hq-dialog" class="bm-dialog">
271
+ <h2>Make a high-quality <span id="hq-name"></span></h2>
262
272
  <p>Describe what this object should be. The text is saved as the asset's description and
263
- used as the generation prompt.</p>
264
- <textarea id="hq-prompt" placeholder="e.g. a weathered stone archway covered in moss"></textarea>
273
+ used as the prompt it is made from.</p>
274
+ <textarea id="hq-prompt" class="bm-input" placeholder="e.g. a weathered stone archway covered in moss"></textarea>
265
275
  <div id="hq-actions">
266
- <button id="hq-cancel" type="button">Close</button>
267
- <button id="hq-confirm" type="button">Generate</button>
276
+ <button id="hq-cancel" class="bm-pill bm-pill--ghost" type="button">Close</button>
277
+ <button id="hq-confirm" class="bm-pill bm-pill--primary" type="button">Make it</button>
268
278
  </div>
269
279
  <div id="hq-note">
270
280
  <span id="hq-note-text"></span>
@@ -1101,8 +1111,9 @@ ${ASSET_DETAIL_HTML}
1101
1111
 
1102
1112
  var body = el('div', 'body');
1103
1113
  var name = el('div', 'name', asset.name || asset.id || 'unnamed');
1104
- if (asset.hqGeneratedAt) name.appendChild(el('span', 'chip hq', 'HQ'));
1105
- else if (asset.placeholder === true) name.appendChild(el('span', 'chip placeholder', 'placeholder'));
1114
+ // Success and warning, never both: an asset is either the good version or standing in for it.
1115
+ if (asset.hqGeneratedAt) name.appendChild(el('span', 'bm-badge bm-badge--success', 'HQ'));
1116
+ else if (asset.placeholder === true) name.appendChild(el('span', 'bm-badge bm-badge--warning', 'placeholder'));
1106
1117
  body.appendChild(name);
1107
1118
  var bits = [asset.type || 'unknown'];
1108
1119
  var size = formatSize(asset.size);
@@ -1 +1 @@
1
- {"version":3,"file":"shell-page.js","sourceRoot":"","sources":["../../src/editor/shell-page.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell-page.js","sourceRoot":"","sources":["../../src/editor/shell-page.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,21 @@ export interface BeginPublishRequest {
24
24
  * server simply ignores a field it does not read — the game then keeps its default icon.
25
25
  */
26
26
  faviconSourceUrl?: string;
27
+ /**
28
+ * sha256 of the source archive this publish is about to upload.
29
+ *
30
+ * Sent on BEGIN, unlike everything else about the archive, because `begin` bakes it into the
31
+ * meta block it mints — which is the whole mechanism: `complete` already verifies that block
32
+ * byte-for-byte against what actually landed, so binding the archive's hash into it makes the
33
+ * published bundle carry a claim about which source produced it, at no extra read. That is only
34
+ * possible if the archive is built and hashed BEFORE this call, which is why `runPublish`
35
+ * zips before it begins.
36
+ *
37
+ * Optional: a failure to build the archive must not fail the publish, and an older server
38
+ * ignores a field it does not read (it then mints a block without the line, and `complete`
39
+ * re-renders without it too — see `renderMetaBlock`).
40
+ */
41
+ sourceSha256?: string;
27
42
  }
28
43
  export interface BeginPublishResponse {
29
44
  signedUrl: string;
@@ -44,6 +59,18 @@ export interface BeginPublishResponse {
44
59
  * CLI says nothing rather than guessing.
45
60
  */
46
61
  currentEngineVersion: string | null;
62
+ /**
63
+ * Where to PUT the source archive, and the key it lands at.
64
+ *
65
+ * The server mints the key (it carries a random segment, so the archive cannot be enumerated
66
+ * from a game's public id) — the CLI never proposes one. Both absent when no `sourceSha256`
67
+ * was sent, or when talking to a server that predates this; the CLI then simply publishes
68
+ * without an archive, exactly as it did before.
69
+ */
70
+ sourceSignedUrl?: string;
71
+ sourceKey?: string;
72
+ /** Must be echoed on the archive PUT — bound into the signature, same contract as the bundle. */
73
+ sourceCacheControl?: string;
47
74
  }
48
75
  export declare function beginPublish(environment: Environment, accessToken: string, body: BeginPublishRequest, deps: ApiDeps): Promise<BeginPublishResponse>;
49
76
  export interface CompletePublishRequest {
@@ -74,11 +101,36 @@ export interface CompletePublishRequest {
74
101
  */
75
102
  engineVersion: string;
76
103
  fingerprint: string;
104
+ /**
105
+ * The source archive that was uploaded, so `complete` can audit it: the key `begin` minted
106
+ * (echoed, not proposed — the server re-derives the expected shape and refuses anything else)
107
+ * and the hash that was bound into the meta block.
108
+ *
109
+ * All three optional together. They are absent when the archive could not be built, and absent
110
+ * from every already-installed CLI — so `complete` must treat missing as "cannot check", not as
111
+ * a failure. Making them required would break every publish in the field the moment the server
112
+ * deploys.
113
+ */
114
+ sourceKey?: string;
115
+ sourceSha256?: string;
116
+ /**
117
+ * Which fingerprint algorithm produced `fingerprint`. The server keeps every version it has
118
+ * shipped and dispatches on this, because a creator's pinned CLI and the running server are
119
+ * routinely different copies of the shared code — see SOURCE_INDEX_ALGO.
120
+ */
121
+ fingerprintAlgo?: string;
122
+ }
123
+ /** Per-check outcome from the server's source audit. Advisory: none of it changes the exit code. */
124
+ export interface SourceCheckResult {
125
+ status: 'pass' | 'warn' | 'skip';
126
+ warnings?: string[];
77
127
  }
78
128
  export interface CompletePublishResponse {
79
129
  url: string;
80
130
  publishVersion: number;
81
131
  visibility: 'public' | 'private';
132
+ /** Absent from an older server; the CLI then prints nothing. */
133
+ sourceChecks?: SourceCheckResult;
82
134
  }
83
135
  export declare function completePublish(environment: Environment, accessToken: string, body: CompletePublishRequest, deps: ApiDeps): Promise<CompletePublishResponse>;
84
136
  /**
@@ -89,6 +141,14 @@ export declare function completePublish(environment: Environment, accessToken: s
89
141
  export declare const PUBLISH_BUNDLE_CONTENT_TYPE = "text/html";
90
142
  /** PUT the built (and meta-block-injected) bundle to the signed URL `begin` minted. */
91
143
  export declare function putBundle(signedUrl: string, html: string, cacheControl: string, deps: ApiDeps): Promise<void>;
144
+ /**
145
+ * `begin` signs the source archive as `application/zip`. Fixed here for the same reason as
146
+ * PUBLISH_BUNDLE_CONTENT_TYPE: the response echoes no content type because there is nothing to
147
+ * echo — this is the one value the signature can have been minted with.
148
+ */
149
+ export declare const PUBLISH_SOURCE_CONTENT_TYPE = "application/zip";
150
+ /** PUT the source archive to the signed URL `begin` minted. */
151
+ export declare function putSourceArchive(signedUrl: string, body: Uint8Array, cacheControl: string, deps: ApiDeps): Promise<void>;
92
152
  export interface ThumbnailUploadRequest {
93
153
  gameId: string;
94
154
  filename: string;