@bitmagic/cli 0.1.41 → 0.1.42
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/dist/editor/asset-detail-panel.d.ts +8 -3
- package/dist/editor/asset-detail-panel.js +31 -25
- package/dist/editor/asset-detail-panel.js.map +1 -1
- package/dist/editor/asset-preview-page.js +15 -6
- package/dist/editor/asset-preview-page.js.map +1 -1
- package/dist/editor/design.d.ts +59 -0
- package/dist/editor/design.js +217 -0
- package/dist/editor/design.js.map +1 -0
- package/dist/editor/shell-page.js +111 -100
- package/dist/editor/shell-page.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,8 +10,13 @@
|
|
|
10
10
|
* Same template-literal rules as its host: **no backticks**, and a backslash in a regex has to be
|
|
11
11
|
* doubled to survive the literal.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Styles for `#asset-overlay`, appended to the shell's stylesheet.
|
|
15
|
+
*
|
|
16
|
+
* The surface, the pills and the modal backdrop come from `design.ts` (`.bm-overlay`,
|
|
17
|
+
* `.bm-dialog`, `.bm-pill`); only what is specific to this dialog's layout lives here.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ASSET_DETAIL_CSS = "\n /* Above #hq-overlay, which sits at 10 \u2014 a preview opened from a row must cover it. */\n #asset-overlay { z-index: 11; }\n #asset-dialog { display: flex; width: min(1100px, 94vw); height: min(760px, 88vh);\n overflow: hidden; }\n /* The viewer takes the room. Everything else is a caption. The near-black ground is the\n brand's: a creation is the only thing on this surface that carries colour. */\n #asset-view { flex: 1; min-width: 0; position: relative; background: var(--bm-bg); }\n #asset-view iframe { display: block; width: 100%; height: 100%; border: 0; }\n #asset-view img { display: block; width: 100%; height: 100%; object-fit: contain;\n background: var(--bm-bg); }\n #asset-view audio { position: absolute; left: 50%; top: 50%; width: min(420px, 80%);\n transform: translate(-50%, -50%); }\n /* A note here is always a failure or a wait that has gone on too long \u2014 the negative accent. */\n #asset-note { position: absolute; inset: auto 0 0 0; padding: 10px 12px;\n background: var(--bm-scrim); color: var(--bm-pink-hover); font-size: 12px; }\n #asset-note.hidden, #asset-view > .hidden { display: none; }\n /* Wide enough for the three action pills to sit on one row: uppercase tracked labels are\n wider than the sentence-case ones this panel used to have. */\n #asset-meta { width: 360px; flex: none; padding: 16px; overflow-y: auto;\n border-left: 1px solid var(--bm-border-muted); }\n #asset-meta h2 { margin: 0 0 4px; word-break: break-word; }\n #asset-meta .sub { color: var(--bm-text-dim); font-size: 12px; margin-bottom: 16px; }\n #asset-facts { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 12px; }\n #asset-facts dt { color: var(--bm-text-dim); }\n #asset-facts dd { margin: 0; color: var(--bm-text); word-break: break-word; }\n #asset-facts dd.mono { font-family: var(--bm-font-mono); }\n #asset-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }\n #asset-close { margin-left: auto; }\n";
|
|
15
20
|
/**
|
|
16
21
|
* The overlay itself.
|
|
17
22
|
*
|
|
@@ -19,4 +24,4 @@ export declare const ASSET_DETAIL_CSS = "\n /* Modelled on #hq-overlay, deliber
|
|
|
19
24
|
* controls because its player sits in a 46px row, and here there is a whole dialog. A creator
|
|
20
25
|
* checking a sound effect wants the scrubber.
|
|
21
26
|
*/
|
|
22
|
-
export declare const ASSET_DETAIL_HTML = "\n <div id=\"asset-overlay\">\n <div id=\"asset-dialog\">\n <div id=\"asset-view\">\n <iframe id=\"asset-frame\" class=\"hidden\" title=\"Asset preview\"></iframe>\n <img id=\"asset-image\" class=\"hidden\" alt=\"\">\n <audio id=\"asset-audio\" class=\"hidden\" controls preload=\"none\"></audio>\n <div id=\"asset-note\" class=\"hidden\"></div>\n </div>\n <aside id=\"asset-meta\">\n <h2 id=\"asset-name\"></h2>\n <div class=\"sub\" id=\"asset-sub\"></div>\n <dl id=\"asset-facts\"></dl>\n <div id=\"asset-actions\">\n <button id=\"asset-reset\" type=\"button\" title=\"Back to the angle the thumbnail uses\">Reset view</button>\n <button id=\"asset-copy\" type=\"button\" title=\"Copy this asset's id\">Copy id</button>\n <button id=\"asset-close\" type=\"button\">Close</button>\n </div>\n </aside>\n </div>\n </div>\n";
|
|
27
|
+
export declare const ASSET_DETAIL_HTML = "\n <div id=\"asset-overlay\" class=\"bm-overlay\">\n <div id=\"asset-dialog\" class=\"bm-dialog\">\n <div id=\"asset-view\">\n <iframe id=\"asset-frame\" class=\"hidden\" title=\"Asset preview\"></iframe>\n <img id=\"asset-image\" class=\"hidden\" alt=\"\">\n <audio id=\"asset-audio\" class=\"hidden\" controls preload=\"none\"></audio>\n <div id=\"asset-note\" class=\"hidden\"></div>\n </div>\n <aside id=\"asset-meta\">\n <h2 id=\"asset-name\"></h2>\n <div class=\"sub\" id=\"asset-sub\"></div>\n <dl id=\"asset-facts\"></dl>\n <div id=\"asset-actions\">\n <button id=\"asset-reset\" class=\"bm-pill\" type=\"button\" title=\"Back to the angle the thumbnail uses\">Reset view</button>\n <button id=\"asset-copy\" class=\"bm-pill\" type=\"button\" title=\"Copy this asset's id\">Copy id</button>\n <button id=\"asset-close\" class=\"bm-pill bm-pill--ghost\" type=\"button\">Close</button>\n </div>\n </aside>\n </div>\n </div>\n";
|
|
@@ -10,34 +10,40 @@
|
|
|
10
10
|
* Same template-literal rules as its host: **no backticks**, and a backslash in a regex has to be
|
|
11
11
|
* doubled to survive the literal.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Styles for `#asset-overlay`, appended to the shell's stylesheet.
|
|
15
|
+
*
|
|
16
|
+
* The surface, the pills and the modal backdrop come from `design.ts` (`.bm-overlay`,
|
|
17
|
+
* `.bm-dialog`, `.bm-pill`); only what is specific to this dialog's layout lives here.
|
|
18
|
+
*/
|
|
14
19
|
export const ASSET_DETAIL_CSS = `
|
|
15
|
-
/*
|
|
16
|
-
#asset-overlay {
|
|
17
|
-
align-items: center; justify-content: center; z-index: 11; }
|
|
18
|
-
#asset-overlay.show { display: flex; }
|
|
20
|
+
/* Above #hq-overlay, which sits at 10 — a preview opened from a row must cover it. */
|
|
21
|
+
#asset-overlay { z-index: 11; }
|
|
19
22
|
#asset-dialog { display: flex; width: min(1100px, 94vw); height: min(760px, 88vh);
|
|
20
|
-
background: #17171d; border: 1px solid #34343f; border-radius: 8px;
|
|
21
23
|
overflow: hidden; }
|
|
22
|
-
/* The viewer takes the room. Everything else is a caption.
|
|
23
|
-
|
|
24
|
+
/* The viewer takes the room. Everything else is a caption. The near-black ground is the
|
|
25
|
+
brand's: a creation is the only thing on this surface that carries colour. */
|
|
26
|
+
#asset-view { flex: 1; min-width: 0; position: relative; background: var(--bm-bg); }
|
|
24
27
|
#asset-view iframe { display: block; width: 100%; height: 100%; border: 0; }
|
|
25
28
|
#asset-view img { display: block; width: 100%; height: 100%; object-fit: contain;
|
|
26
|
-
background:
|
|
29
|
+
background: var(--bm-bg); }
|
|
27
30
|
#asset-view audio { position: absolute; left: 50%; top: 50%; width: min(420px, 80%);
|
|
28
31
|
transform: translate(-50%, -50%); }
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
/* A note here is always a failure or a wait that has gone on too long — the negative accent. */
|
|
33
|
+
#asset-note { position: absolute; inset: auto 0 0 0; padding: 10px 12px;
|
|
34
|
+
background: var(--bm-scrim); color: var(--bm-pink-hover); font-size: 12px; }
|
|
31
35
|
#asset-note.hidden, #asset-view > .hidden { display: none; }
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#asset-meta
|
|
35
|
-
|
|
36
|
-
#asset-
|
|
37
|
-
#asset-
|
|
38
|
-
#asset-facts
|
|
39
|
-
#asset-facts
|
|
40
|
-
#asset-
|
|
36
|
+
/* Wide enough for the three action pills to sit on one row: uppercase tracked labels are
|
|
37
|
+
wider than the sentence-case ones this panel used to have. */
|
|
38
|
+
#asset-meta { width: 360px; flex: none; padding: 16px; overflow-y: auto;
|
|
39
|
+
border-left: 1px solid var(--bm-border-muted); }
|
|
40
|
+
#asset-meta h2 { margin: 0 0 4px; word-break: break-word; }
|
|
41
|
+
#asset-meta .sub { color: var(--bm-text-dim); font-size: 12px; margin-bottom: 16px; }
|
|
42
|
+
#asset-facts { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 12px; }
|
|
43
|
+
#asset-facts dt { color: var(--bm-text-dim); }
|
|
44
|
+
#asset-facts dd { margin: 0; color: var(--bm-text); word-break: break-word; }
|
|
45
|
+
#asset-facts dd.mono { font-family: var(--bm-font-mono); }
|
|
46
|
+
#asset-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
|
|
41
47
|
#asset-close { margin-left: auto; }
|
|
42
48
|
`;
|
|
43
49
|
/**
|
|
@@ -48,8 +54,8 @@ export const ASSET_DETAIL_CSS = `
|
|
|
48
54
|
* checking a sound effect wants the scrubber.
|
|
49
55
|
*/
|
|
50
56
|
export const ASSET_DETAIL_HTML = `
|
|
51
|
-
<div id="asset-overlay">
|
|
52
|
-
<div id="asset-dialog">
|
|
57
|
+
<div id="asset-overlay" class="bm-overlay">
|
|
58
|
+
<div id="asset-dialog" class="bm-dialog">
|
|
53
59
|
<div id="asset-view">
|
|
54
60
|
<iframe id="asset-frame" class="hidden" title="Asset preview"></iframe>
|
|
55
61
|
<img id="asset-image" class="hidden" alt="">
|
|
@@ -61,9 +67,9 @@ export const ASSET_DETAIL_HTML = `
|
|
|
61
67
|
<div class="sub" id="asset-sub"></div>
|
|
62
68
|
<dl id="asset-facts"></dl>
|
|
63
69
|
<div id="asset-actions">
|
|
64
|
-
<button id="asset-reset" type="button" title="Back to the angle the thumbnail uses">Reset view</button>
|
|
65
|
-
<button id="asset-copy" type="button" title="Copy this asset's id">Copy id</button>
|
|
66
|
-
<button id="asset-close" type="button">Close</button>
|
|
70
|
+
<button id="asset-reset" class="bm-pill" type="button" title="Back to the angle the thumbnail uses">Reset view</button>
|
|
71
|
+
<button id="asset-copy" class="bm-pill" type="button" title="Copy this asset's id">Copy id</button>
|
|
72
|
+
<button id="asset-close" class="bm-pill bm-pill--ghost" type="button">Close</button>
|
|
67
73
|
</div>
|
|
68
74
|
</aside>
|
|
69
75
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-detail-panel.js","sourceRoot":"","sources":["../../src/editor/asset-detail-panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH
|
|
1
|
+
{"version":3,"file":"asset-detail-panel.js","sourceRoot":"","sources":["../../src/editor/asset-detail-panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBhC,CAAC"}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { CDN_IMPORTS } from '../scaffold/project-files.js';
|
|
20
20
|
import { importMapEntries } from '../scaffold/aliases.js';
|
|
21
|
+
import { BITMAGIC_FONT_LINK, BITMAGIC_TOKENS_CSS } from './design.js';
|
|
21
22
|
/** Attribute- and script-safe: these land inside a `<script type="importmap">` block. */
|
|
22
23
|
function importMapFor(gamePort) {
|
|
23
24
|
const origin = `http://localhost:${gamePort}`;
|
|
@@ -31,17 +32,25 @@ export function renderAssetPreviewPage(options) {
|
|
|
31
32
|
<head>
|
|
32
33
|
<meta charset="utf-8">
|
|
33
34
|
<title>Asset preview</title>
|
|
35
|
+
${BITMAGIC_FONT_LINK}
|
|
34
36
|
<style>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
${BITMAGIC_TOKENS_CSS}
|
|
38
|
+
/* The ground before the canvas exists, and the type over it once it does. Brand near-black, so
|
|
39
|
+
a viewer that is still loading looks like part of the dialog around it rather than a blank
|
|
40
|
+
window. The RENDER surface stays the slate asset-viewer.ts clears to — that is the colour the
|
|
41
|
+
engine bakes the thumbnails on, and a tile and its preview must not disagree. */
|
|
42
|
+
html, body { margin: 0; height: 100%; background: var(--bm-bg); overflow: hidden;
|
|
43
|
+
font: 400 12px/1.5 var(--bm-font); color: var(--bm-text-muted); }
|
|
37
44
|
#stage { position: absolute; inset: 0; }
|
|
38
45
|
#stage canvas { display: block; width: 100%; height: 100%; }
|
|
39
46
|
/* Sits over the canvas rather than beside it: the dialog gives this iframe every pixel, and a
|
|
40
47
|
status line that reserved space would shrink the thing being looked at. */
|
|
41
|
-
#status { position: absolute; left: 0; right: 0; top: 0; padding:
|
|
42
|
-
background: linear-gradient(
|
|
43
|
-
|
|
44
|
-
#
|
|
48
|
+
#status { position: absolute; left: 0; right: 0; top: 0; padding: 12px;
|
|
49
|
+
background: linear-gradient(var(--bm-scrim), transparent);
|
|
50
|
+
pointer-events: none; }
|
|
51
|
+
#status.failed { color: var(--bm-pink-hover); }
|
|
52
|
+
#hint { position: absolute; left: 12px; bottom: 12px; color: var(--bm-text-dim);
|
|
53
|
+
pointer-events: none; }
|
|
45
54
|
#hint.hidden, #status.hidden { display: none; }
|
|
46
55
|
</style>
|
|
47
56
|
</head>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-preview-page.js","sourceRoot":"","sources":["../../src/editor/asset-preview-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"asset-preview-page.js","sourceRoot":"","sources":["../../src/editor/asset-preview-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQtE,yFAAyF;AACzF,SAAS,YAAY,CAAC,QAAgB;IACpC,MAAM,MAAM,GAAG,oBAAoB,QAAQ,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAC/B,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CACzF,CAAC;IACF,OAAO,EAAE,GAAG,WAAW,EAAE,GAAG,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACrF,OAAO;;;;;EAKP,kBAAkB;;EAElB,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BnB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
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 declare const BM: {
|
|
17
|
+
readonly bg: "#0b0b0b";
|
|
18
|
+
readonly surface: "#181818";
|
|
19
|
+
readonly surfaceAlt: "#1f1f1f";
|
|
20
|
+
readonly surfaceCard: "#252525";
|
|
21
|
+
readonly textPrimary: "#ffffff";
|
|
22
|
+
readonly textMuted: "#b3b3b3";
|
|
23
|
+
readonly textDim: "#7c7c7c";
|
|
24
|
+
readonly textOnAccent: "#000000";
|
|
25
|
+
readonly border: "#4d4d4d";
|
|
26
|
+
readonly borderMuted: "#2a2a2a";
|
|
27
|
+
readonly aqua: "#A0DAB9";
|
|
28
|
+
readonly aquaHover: "#B8E4CA";
|
|
29
|
+
readonly aquaPressed: "#7DC29C";
|
|
30
|
+
readonly pink: "#E0218A";
|
|
31
|
+
readonly pinkHover: "#F04AA4";
|
|
32
|
+
readonly pinkPressed: "#B3176D";
|
|
33
|
+
readonly pumpkin: "#FF8200";
|
|
34
|
+
readonly canary: "#F9E547";
|
|
35
|
+
readonly shadowDialog: "0 8px 24px rgba(0, 0, 0, 0.5)";
|
|
36
|
+
readonly shadowCard: "0 8px 8px rgba(0, 0, 0, 0.3)";
|
|
37
|
+
readonly glowAqua: "0 0 24px rgba(160, 218, 185, 0.45)";
|
|
38
|
+
readonly glowPink: "0 0 24px rgba(224, 33, 138, 0.4)";
|
|
39
|
+
readonly insetBorderDim: "inset 0 0 0 1px #7c7c7c";
|
|
40
|
+
readonly insetBorderFocus: "inset 0 0 0 1px #ffffff, 0 0 0 2px rgba(160, 218, 185, 0.45)";
|
|
41
|
+
readonly font: "\"Red Hat Display\", \"Helvetica Neue\", Helvetica, Arial, system-ui, sans-serif";
|
|
42
|
+
readonly 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 declare const BITMAGIC_FONT_LINK = "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Red+Hat+Display:wght@300;400;500;700&display=swap\">";
|
|
51
|
+
/** The palette as custom properties, so page-specific rules can `var(--bm-*)` them. */
|
|
52
|
+
export declare const BITMAGIC_TOKENS_CSS: string;
|
|
53
|
+
/**
|
|
54
|
+
* The component primitives both served pages use: pill buttons, inputs, badges, modal surfaces.
|
|
55
|
+
*
|
|
56
|
+
* Label voice is UPPERCASE Bold with tracking and content voice is sentence-case Regular, per the
|
|
57
|
+
* brand type guideline — which is why every button here is uppercase and nothing else is.
|
|
58
|
+
*/
|
|
59
|
+
export declare const BITMAGIC_CONTROLS_CSS: string;
|
|
@@ -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
|
-
|
|
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:
|
|
114
|
-
#bar { display: flex; align-items: center; gap: 12px; height:
|
|
115
|
-
background:
|
|
116
|
-
|
|
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:
|
|
119
|
-
padding:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
.tab
|
|
123
|
-
.tab.active { background:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
#build-banner
|
|
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:
|
|
147
|
-
border-left: 1px solid
|
|
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:
|
|
151
|
-
|
|
152
|
-
#assets-head .count { margin-left: auto; color:
|
|
153
|
-
#assets-empty { padding:
|
|
154
|
-
.row { display: flex; gap:
|
|
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:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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:
|
|
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 .
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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:
|
|
172
|
-
.row.job .thumb { color:
|
|
173
|
-
.row.job .message { color:
|
|
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:
|
|
177
|
-
.step { display: flex; align-items: baseline; gap:
|
|
178
|
-
|
|
179
|
-
.step.
|
|
180
|
-
.step
|
|
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:
|
|
184
|
-
.step.running .tail { color:
|
|
185
|
-
.bar { height: 3px; margin:
|
|
186
|
-
overflow: hidden; }
|
|
187
|
-
.bar .fill { height: 100%; background:
|
|
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:
|
|
192
|
-
.job-map canvas { display: block; width: 100%; height: auto; border-radius:
|
|
193
|
-
border: 1px solid
|
|
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:
|
|
196
|
-
|
|
197
|
-
|
|
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 {
|
|
202
|
-
|
|
203
|
-
#hq-
|
|
204
|
-
#hq-
|
|
205
|
-
|
|
206
|
-
#hq-
|
|
207
|
-
|
|
208
|
-
|
|
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:
|
|
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
|
-
<
|
|
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
|
|
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
|
|
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>
|
|
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
|
|
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">
|
|
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
|
-
|
|
1105
|
-
|
|
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,EACL,qBAAqB,EAAE,kBAAkB,GAC1C,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"shell-page.js","sourceRoot":"","sources":["../../src/editor/shell-page.ts"],"names":[],"mappings":"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"}
|