@wasm-gaming/engine-specs 0.2.3 → 0.2.5

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/demo/README.md CHANGED
@@ -6,19 +6,75 @@ This directory contains a standalone, themable web interface template for hostin
6
6
 
7
7
  1. **Copy the `demo/` folder** into your engine repository.
8
8
 
9
- 2. **Connect your engine SDK in `demo/sdk.js`**:
10
- Open `demo/sdk.js` and point it to your engine SDK build:
11
- ```javascript
12
- import sdk from "../src/index.js"; // or "../dist/index.js"
13
-
14
- export async function getSdk() {
15
- return sdk;
16
- }
9
+ 2. **Initialize via ES module script**:
10
+ ```html
11
+ <script type="module">
12
+ import demo from './demo.js'
13
+ import sdk from './demo.sdk.js' // Or your engine SDK import
14
+
15
+ demo
16
+ .init({
17
+ messages: {
18
+ title: 'My Custom Engine Demo',
19
+ },
20
+ bios: false, // 'required' | 'optional' | false
21
+ escMenu: {
22
+ enabled: true,
23
+ title: 'Game Pause Menu',
24
+ items: [
25
+ { label: 'Toggle Fullscreen', action: (instance) => { document.documentElement.requestFullscreen?.(); } }
26
+ ],
27
+ },
28
+ })
29
+ .on('file', ({ type, action, file }) => {
30
+ if (file) {
31
+ console.log(`[Demo] Selected ${type} file: ${file.name}`)
32
+ }
33
+ })
34
+ .on('launch', async (e) => {
35
+ const assets = {}
36
+
37
+ // if rom loaded
38
+ if (demo.hasFile('rom')) {
39
+ assets.rom = await demo.loadToMEMFS('rom')
40
+ }
41
+
42
+ // if bios required and loaded
43
+ if (demo.hasFile('bios')) {
44
+ assets.bios = await demo.loadToMEMFS('bios')
45
+ }
46
+
47
+ const instance = await sdk.load({
48
+ attachTo: e.attachTo,
49
+ assets,
50
+ options: { fileName: e.fileName, byteLength: e.byteLength },
51
+ })
52
+
53
+ instance.start()
54
+ return instance
55
+ })
56
+ </script>
17
57
  ```
18
- *Alternatively*, if embedding in HTML without modifying `sdk.js`, set `window.SDK = myEngineSdk;` before `demo.js` runs.
19
58
 
20
- 3. **Serve the demo**:
21
- Serve `demo/index.html` using any local development server (e.g. `npx serve demo` or Vite).
59
+ ---
60
+
61
+ ## 🎮 Component79 ESC Menu
62
+
63
+ The demo includes an interactive, customizable in-game ESC pause menu component built with **Component79**.
64
+
65
+ ### ESC Menu Options (`escMenu`)
66
+
67
+ | Option | Type | Default | Description |
68
+ |---|---|---|---|
69
+ | `enabled` | `boolean` | `true` | Enable/disable in-game ESC menu overlay. |
70
+ | `title` | `string` | `"Game Menu"` | Title displayed at top of ESC menu modal. |
71
+ | `shortcutKey` | `string` | `"Escape"` | Keyboard key that toggles the menu overlay. |
72
+ | `showTriggerButton` | `boolean` | `true` | Display floating top-right "ESC Menu" trigger badge for mouse/touch access. |
73
+ | `items` | `Array<{label: string, action: function}>` | `[]` | Custom action buttons added to ESC menu. |
74
+ | `onReset` | `function` | `instance.reset()` | Callback when Reset Game button is pressed. |
75
+ | `onExit` | `function` | `instance.destroy()` | Callback when Exit to Launcher button is pressed. |
76
+ | `onSaveState` | `function` | `instance.saveState()` | Callback when Save State button is pressed. |
77
+ | `onLoadState` | `function` | `instance.loadState()` | Callback when Load State button is pressed. |
22
78
 
23
79
  ---
24
80
 
@@ -50,15 +106,10 @@ To apply your custom branding, override any of the `:root` variables in `demo.cs
50
106
  --demo-subpanel-bg: rgba(30, 41, 59, 0.6);
51
107
  --demo-subpanel-border: rgba(255, 255, 255, 0.1);
52
108
 
53
- /* Buttons & Dropzone */
54
- --demo-drop-bg: rgba(30, 41, 59, 0.4);
55
- --demo-picker-bg: rgba(255, 255, 255, 0.1);
56
- --demo-picker-hover-bg: rgba(255, 255, 255, 0.2);
57
- --demo-picker-text: #ffffff;
58
-
59
- /* Launch button */
60
- --demo-launch-bg: var(--demo-accent);
61
- --demo-launch-text: #ffffff;
109
+ /* ESC Menu styling */
110
+ --demo-esc-bg: rgba(6, 9, 16, 0.85);
111
+ --demo-esc-card-bg: rgba(15, 23, 42, 0.9);
112
+ --demo-esc-text: #f8fafc;
62
113
  }
63
114
  ```
64
115
 
@@ -84,8 +135,9 @@ To apply your custom branding, override any of the `:root` variables in `demo.cs
84
135
  | `--demo-picker-bg` | Pick File button background | `rgba(255, 255, 255, 0.5)` |
85
136
  | `--demo-pill-bg` | Metadata badge background | `rgba(var(--demo-accent-rgb), 0.1)` |
86
137
  | `--demo-file-info-bg` | Canvas overlay panel background | `rgba(255, 255, 255, 0.7)` |
87
- | `--demo-launch-bg` | Launch button background color | `var(--demo-accent)` |
88
- | `--demo-launch-text` | Launch button text color | `#ffffff` |
138
+ | `--demo-esc-bg` | ESC menu overlay background | `rgba(6, 9, 16, 0.82)` |
139
+ | `--demo-esc-card-bg` | ESC menu card background | `rgba(15, 23, 42, 0.88)` |
140
+ | `--demo-esc-text` | ESC menu text color | `#f8fafc` |
89
141
  | `--demo-font-sans` | Primary sans-serif font family | `"Outfit", system-ui, sans-serif` |
90
142
  | `--demo-font-mono` | Monospace code font family | `"Space Mono", monospace` |
91
143
 
@@ -93,6 +145,7 @@ To apply your custom branding, override any of the `:root` variables in `demo.cs
93
145
 
94
146
  ## 🛠 Features
95
147
 
148
+ - **Component79 ESC Menu**: Customizable pause menu overlay with keydown listeners and touch/mouse badges.
96
149
  - **OPFS Asset Persistence**: Automatically stores selected ROM and BIOS files in the Origin Private File System (`navigator.storage.getDirectory()`), restoring them automatically across page reloads.
97
150
  - **Split Asset Drop Zones**: Independent drop/picker areas for optional **BIOS** firmware and game **ROM** assets.
98
151
  - **Dedicated Launch Trigger**: Prominent launch button that activates once required game assets are selected.
@@ -3,11 +3,16 @@
3
3
  @dragleave="dragging = false"
4
4
  @drop.prevent="onDrop">
5
5
  <strong>{{ prompt }}</strong>
6
- <p :class.file-selected="selectedName">{{ selectedName ? 'Selected: ' + selectedName : assetHint }}</p>
7
- <label class="picker">
8
- {{ selectedName ? 'Change File' : buttonText }}
9
- <input type="file" :accept="accept" @change="onPick" />
10
- </label>
6
+ <p :class.file-selected="selectedName">{{ selectedName ? ((messages.selectedPrefix || 'Selected: ') + selectedName) : assetHint }}</p>
7
+ <div class="picker-actions">
8
+ <label class="picker">
9
+ {{ selectedName ? (messages.changeFileButton || 'Change File') : buttonText }}
10
+ <input type="file" :accept="accept" @change="onPick" />
11
+ </label>
12
+ <button type="button" :if="selectedName" class="remove-btn" @click.stop="onRemove">
13
+ {{ messages.removeFileButton || 'Remove' }}
14
+ </button>
15
+ </div>
11
16
  </section>
12
17
 
13
18
  <script :setup="{
@@ -16,6 +21,7 @@
16
21
  accept,
17
22
  buttonText = 'Pick file',
18
23
  fileName = '',
24
+ messages = {},
19
25
  }">
20
26
  let dragging = false;
21
27
  let selectedName = fileName || "";
@@ -30,7 +36,12 @@
30
36
  };
31
37
 
32
38
  const emitFile = (file) => {
33
- if (!file) return;
39
+ if (!file) {
40
+ selectedName = "";
41
+ $emit("remove");
42
+ $emit("select", null);
43
+ return;
44
+ }
34
45
  selectedName = file.name;
35
46
  $emit("select", file);
36
47
  };
@@ -45,6 +56,12 @@
45
56
  const file = extractFile(event);
46
57
  if (file) emitFile(file);
47
58
  };
59
+
60
+ const onRemove = () => {
61
+ selectedName = "";
62
+ $emit("remove");
63
+ $emit("select", null);
64
+ };
48
65
  </script>
49
66
 
50
67
  <style>
@@ -94,9 +111,15 @@
94
111
  font-weight: 600;
95
112
  }
96
113
 
97
- .picker {
114
+ .picker-actions {
98
115
  display: inline-flex;
116
+ align-items: center;
117
+ gap: 0.5rem;
99
118
  margin-top: 1rem;
119
+ }
120
+
121
+ .picker {
122
+ display: inline-flex;
100
123
  background: var(--demo-picker-bg, rgba(255, 255, 255, 0.5));
101
124
  color: var(--demo-picker-text, var(--demo-text, #0f172a));
102
125
  border: 1px solid var(--demo-picker-border, rgba(255, 255, 255, 0.6));
@@ -121,4 +144,28 @@
121
144
  .picker input {
122
145
  display: none;
123
146
  }
147
+
148
+ .remove-btn {
149
+ display: inline-flex;
150
+ align-items: center;
151
+ background: rgba(239, 68, 68, 0.15);
152
+ color: #ef4444;
153
+ border: 1px solid rgba(239, 68, 68, 0.4);
154
+ border-radius: var(--demo-subpanel-radius, 8px);
155
+ padding: 0.5rem 0.8rem;
156
+ cursor: pointer;
157
+ text-transform: uppercase;
158
+ font-weight: 600;
159
+ font-size: 0.85rem;
160
+ letter-spacing: 0.05em;
161
+ transition: all 0.2s;
162
+ backdrop-filter: blur(var(--demo-subpanel-blur, 12px));
163
+ -webkit-backdrop-filter: blur(var(--demo-subpanel-blur, 12px));
164
+ }
165
+
166
+ .remove-btn:hover {
167
+ background: rgba(239, 68, 68, 0.3);
168
+ border-color: rgba(239, 68, 68, 0.8);
169
+ color: #dc2626;
170
+ }
124
171
  </style>