@khorsheed/dsh-game 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.en.md +53 -0
- package/README.i18n.yaml +6 -0
- package/README.md +52 -2
- package/cordis.patch.yml +23 -0
- package/lib/client.js +30348 -0
- package/lib/index.js +4154 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +3769 -0
- package/lib/typert.remote-client.d.ts +101 -0
- package/lib/typert.remote-client.js +2082 -0
- package/lib/types/agent.d.ts +20 -0
- package/lib/types/agent.js +42 -0
- package/lib/types/checks.d.ts +44 -0
- package/lib/types/checks.js +267 -0
- package/lib/types/client/DesignView.d.ts +22 -0
- package/lib/types/client/DesignView.js +134 -0
- package/lib/types/client/ElementsView.d.ts +21 -0
- package/lib/types/client/ElementsView.js +187 -0
- package/lib/types/client/GameTab.d.ts +14 -0
- package/lib/types/client/GameTab.js +256 -0
- package/lib/types/client/PlayFrame.d.ts +54 -0
- package/lib/types/client/PlayFrame.js +129 -0
- package/lib/types/client/PlayView.d.ts +19 -0
- package/lib/types/client/PlayView.js +161 -0
- package/lib/types/client/Preview3D.d.ts +50 -0
- package/lib/types/client/Preview3D.js +176 -0
- package/lib/types/client/ProjectSwitcher.d.ts +20 -0
- package/lib/types/client/ProjectSwitcher.js +75 -0
- package/lib/types/client/Recipe3DEditor.d.ts +19 -0
- package/lib/types/client/Recipe3DEditor.js +44 -0
- package/lib/types/client/StyleGallery.d.ts +18 -0
- package/lib/types/client/StyleGallery.js +27 -0
- package/lib/types/client/TurnCard.d.ts +62 -0
- package/lib/types/client/TurnCard.js +100 -0
- package/lib/types/client/VersionsView.d.ts +17 -0
- package/lib/types/client/VersionsView.js +68 -0
- package/lib/types/client/chat-node.d.ts +42 -0
- package/lib/types/client/chat-node.js +77 -0
- package/lib/types/client/contract.d.ts +125 -0
- package/lib/types/client/contract.js +6 -0
- package/lib/types/client/definition.d.ts +22 -0
- package/lib/types/client/definition.js +28 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/client/index.js +229 -0
- package/lib/types/client/locales.d.ts +376 -0
- package/lib/types/client/locales.js +727 -0
- package/lib/types/client/looks.d.ts +36 -0
- package/lib/types/client/looks.js +105 -0
- package/lib/types/client/markdown.d.ts +11 -0
- package/lib/types/client/markdown.js +52 -0
- package/lib/types/client/preset-visibility.d.ts +94 -0
- package/lib/types/client/preset-visibility.js +115 -0
- package/lib/types/client/preview-entry.d.ts +24 -0
- package/lib/types/client/preview-entry.js +107 -0
- package/lib/types/client/refs.d.ts +36 -0
- package/lib/types/client/refs.js +44 -0
- package/lib/types/client/style-samples.d.ts +5 -0
- package/lib/types/client/style-samples.js +196 -0
- package/lib/types/fsutil.d.ts +26 -0
- package/lib/types/fsutil.js +104 -0
- package/lib/types/hooks.d.ts +83 -0
- package/lib/types/hooks.js +108 -0
- package/lib/types/index.d.ts +30 -0
- package/lib/types/index.js +22 -0
- package/lib/types/kits/canvas2d.d.ts +6 -0
- package/lib/types/kits/canvas2d.js +60 -0
- package/lib/types/kits/ids.d.ts +11 -0
- package/lib/types/kits/ids.js +6 -0
- package/lib/types/kits/index.d.ts +26 -0
- package/lib/types/kits/index.js +28 -0
- package/lib/types/kits/three3d.d.ts +12 -0
- package/lib/types/kits/three3d.js +188 -0
- package/lib/types/kits/types.d.ts +52 -0
- package/lib/types/kits/types.js +6 -0
- package/lib/types/look3d.d.ts +20 -0
- package/lib/types/look3d.js +175 -0
- package/lib/types/panel-session.d.ts +21 -0
- package/lib/types/panel-session.js +16 -0
- package/lib/types/project.d.ts +132 -0
- package/lib/types/project.js +494 -0
- package/lib/types/remote.d.ts +182 -0
- package/lib/types/remote.js +256 -0
- package/lib/types/skills.d.ts +13 -0
- package/lib/types/skills.js +140 -0
- package/lib/types/store.d.ts +325 -0
- package/lib/types/store.js +1285 -0
- package/lib/types/tools.d.ts +12 -0
- package/lib/types/tools.js +301 -0
- package/lib/types/trash.d.ts +5 -0
- package/lib/types/trash.js +64 -0
- package/lib/types/types.d.ts +522 -0
- package/lib/types/types.js +85 -0
- package/lib/types/vfs.d.ts +92 -0
- package/lib/types/vfs.js +217 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +132 -3
- package/runtime/dsh3d.js +1279 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The in-sandbox loader (probe P0-1, proposal §5): a tiny HTML shell that
|
|
3
|
+
* receives a project's files, mints blob URLs inside its OWN opaque origin,
|
|
4
|
+
* rewrites relative module specifiers and CSS `url()`, shims `fetch` and
|
|
5
|
+
* element `src`, then writes the project's `index.html` over itself.
|
|
6
|
+
*
|
|
7
|
+
* Shared by both halves: the panel puts the shell in a
|
|
8
|
+
* `sandbox="allow-scripts"` iframe and posts the files in; the host's checks
|
|
9
|
+
* load the same shell in headless Chrome with the files pre-set on
|
|
10
|
+
* `window.__VFS_FILES__`. Everything that runs in the frame is kept as
|
|
11
|
+
* STRINGS here — never `fn.toString()` — so a bundler's renaming can never
|
|
12
|
+
* break it.
|
|
13
|
+
*
|
|
14
|
+
* The bridge script is installed twice, in the shell and again in the written
|
|
15
|
+
* document, because `document.open()` drops every window listener. It reports
|
|
16
|
+
* errors to the parent, and stands in for `localStorage` (an opaque origin
|
|
17
|
+
* has none): saves start from what the parent sends with the files and each
|
|
18
|
+
* write is posted back for the parent to keep.
|
|
19
|
+
* @module @khorsheed/dsh-game/vfs
|
|
20
|
+
*/
|
|
21
|
+
/** The frame's CSP: nothing leaves the frame; scripts and assets come from blobs only. */
|
|
22
|
+
export declare const SANDBOX_CSP: string;
|
|
23
|
+
/**
|
|
24
|
+
* The shell document. `extraHead` lets the headless runner add a marker;
|
|
25
|
+
* the panel passes nothing.
|
|
26
|
+
* @param extraHead - raw HTML placed before the bridge.
|
|
27
|
+
* @returns the complete HTML string.
|
|
28
|
+
*/
|
|
29
|
+
export declare function shellHtml(extraHead?: string): string;
|
|
30
|
+
/** Messages the frame posts to its parent. */
|
|
31
|
+
export type FrameMessage = {
|
|
32
|
+
readonly gamedev: 'error';
|
|
33
|
+
readonly kind: string;
|
|
34
|
+
readonly msg: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly gamedev: 'save';
|
|
37
|
+
readonly saves: Readonly<Record<string, string>>;
|
|
38
|
+
} | {
|
|
39
|
+
readonly gamedev: 'inspect';
|
|
40
|
+
readonly ready: boolean;
|
|
41
|
+
readonly state: unknown;
|
|
42
|
+
readonly errors: readonly {
|
|
43
|
+
kind: string;
|
|
44
|
+
msg: string;
|
|
45
|
+
}[];
|
|
46
|
+
} | {
|
|
47
|
+
readonly gamedev: 'picked';
|
|
48
|
+
readonly id: string | null;
|
|
49
|
+
readonly ref: PickRef | null;
|
|
50
|
+
readonly hover: boolean;
|
|
51
|
+
readonly supported: boolean;
|
|
52
|
+
readonly x: number;
|
|
53
|
+
readonly y: number;
|
|
54
|
+
} | {
|
|
55
|
+
readonly gamedev: 'preview';
|
|
56
|
+
readonly png: string;
|
|
57
|
+
} | {
|
|
58
|
+
readonly gamedev: 'preview-clips';
|
|
59
|
+
readonly names: readonly string[];
|
|
60
|
+
readonly parts: ModelParts | null;
|
|
61
|
+
};
|
|
62
|
+
/** What a loaded glTF model can be tuned by (the preview reports it once loaded). */
|
|
63
|
+
export interface ModelParts {
|
|
64
|
+
readonly materials: readonly {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly color: string;
|
|
67
|
+
readonly vertexColors: boolean;
|
|
68
|
+
}[];
|
|
69
|
+
readonly bones: readonly string[];
|
|
70
|
+
readonly morphs: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
/** What a game's `pick` may return beyond a bare id (dsh3d returns this). */
|
|
73
|
+
export interface PickRef {
|
|
74
|
+
readonly id: string | null;
|
|
75
|
+
readonly inst?: string | null;
|
|
76
|
+
readonly name?: string | null;
|
|
77
|
+
readonly state?: string | null;
|
|
78
|
+
readonly ground?: boolean;
|
|
79
|
+
readonly point?: {
|
|
80
|
+
readonly x: number;
|
|
81
|
+
readonly y: number;
|
|
82
|
+
readonly z: number;
|
|
83
|
+
};
|
|
84
|
+
readonly region?: string | null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A single self-contained HTML file for a build (decision: sharing starts as
|
|
88
|
+
* a file export). The same shell, with the files embedded; opened from disk
|
|
89
|
+
* it has a real origin, so the game's own localStorage works natively.
|
|
90
|
+
*/
|
|
91
|
+
export declare function exportHtml(title: string, files: Readonly<Record<string, unknown>>): string;
|
|
92
|
+
//# sourceMappingURL=vfs.d.ts.map
|
package/lib/types/vfs.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The in-sandbox loader (probe P0-1, proposal §5): a tiny HTML shell that
|
|
3
|
+
* receives a project's files, mints blob URLs inside its OWN opaque origin,
|
|
4
|
+
* rewrites relative module specifiers and CSS `url()`, shims `fetch` and
|
|
5
|
+
* element `src`, then writes the project's `index.html` over itself.
|
|
6
|
+
*
|
|
7
|
+
* Shared by both halves: the panel puts the shell in a
|
|
8
|
+
* `sandbox="allow-scripts"` iframe and posts the files in; the host's checks
|
|
9
|
+
* load the same shell in headless Chrome with the files pre-set on
|
|
10
|
+
* `window.__VFS_FILES__`. Everything that runs in the frame is kept as
|
|
11
|
+
* STRINGS here — never `fn.toString()` — so a bundler's renaming can never
|
|
12
|
+
* break it.
|
|
13
|
+
*
|
|
14
|
+
* The bridge script is installed twice, in the shell and again in the written
|
|
15
|
+
* document, because `document.open()` drops every window listener. It reports
|
|
16
|
+
* errors to the parent, and stands in for `localStorage` (an opaque origin
|
|
17
|
+
* has none): saves start from what the parent sends with the files and each
|
|
18
|
+
* write is posted back for the parent to keep.
|
|
19
|
+
* @module @khorsheed/dsh-game/vfs
|
|
20
|
+
*/
|
|
21
|
+
/** The frame's CSP: nothing leaves the frame; scripts and assets come from blobs only. */
|
|
22
|
+
export const SANDBOX_CSP = "default-src 'none'; script-src 'unsafe-inline' blob:; style-src 'unsafe-inline' blob:; "
|
|
23
|
+
+ "img-src blob: data:; media-src blob: data:; font-src blob: data:; connect-src blob: data:";
|
|
24
|
+
/** The bridge (runs in the frame). */
|
|
25
|
+
const BRIDGE = `(function(){
|
|
26
|
+
var errors = window.__gamedevErrors || (window.__gamedevErrors = []);
|
|
27
|
+
function post(m){ try { if (parent !== window) parent.postMessage(m, '*') } catch (e) {} }
|
|
28
|
+
function report(kind, msg){ var t = String(msg); if (errors.length < 50) errors.push({ kind: kind, msg: t }); post({ gamedev: 'error', kind: kind, msg: t }) }
|
|
29
|
+
addEventListener('error', function(e){
|
|
30
|
+
if (e.target && e.target !== window && e.target.tagName) { report('resource', (e.target.tagName || '') + ' ' + (e.target.src || e.target.href || '').slice(0, 60)); return }
|
|
31
|
+
report('error', (e.message || '') + (e.lineno ? ' (line ' + e.lineno + ')' : ''))
|
|
32
|
+
}, true);
|
|
33
|
+
addEventListener('unhandledrejection', function(e){ report('rejection', e.reason && e.reason.message || e.reason) });
|
|
34
|
+
if (!console.__gd) { var ce = console.error; console.error = function(){ report('console', [].join.call(arguments, ' ')); return ce.apply(console, arguments) }; console.__gd = 1 }
|
|
35
|
+
var saves = window.__gdSaves || (window.__gdSaves = {});
|
|
36
|
+
var nativeOk = false;
|
|
37
|
+
try { nativeOk = !!window.localStorage && (window.localStorage.getItem('__gd'), true) } catch (e) {}
|
|
38
|
+
if (!nativeOk) {
|
|
39
|
+
var store = {
|
|
40
|
+
getItem: function(k){ k = String(k); return Object.prototype.hasOwnProperty.call(saves, k) ? saves[k] : null },
|
|
41
|
+
setItem: function(k, v){ saves[String(k)] = String(v); post({ gamedev: 'save', saves: saves }) },
|
|
42
|
+
removeItem: function(k){ delete saves[String(k)]; post({ gamedev: 'save', saves: saves }) },
|
|
43
|
+
clear: function(){ for (var k in saves) delete saves[k]; post({ gamedev: 'save', saves: saves }) },
|
|
44
|
+
key: function(i){ return Object.keys(saves)[i] || null },
|
|
45
|
+
get length(){ return Object.keys(saves).length }
|
|
46
|
+
};
|
|
47
|
+
try { Object.defineProperty(window, 'localStorage', { configurable: true, get: function(){ return store } }) } catch (e) {}
|
|
48
|
+
}
|
|
49
|
+
window.dshParams = window.dshParams || {};
|
|
50
|
+
window.dshElements = window.dshElements || {};
|
|
51
|
+
addEventListener('message', function(e){
|
|
52
|
+
var d = e.data || {}, g = window.dshGame;
|
|
53
|
+
if (d.gamedev === 'inspect') post({ gamedev: 'inspect', ready: !!(g && g.ready), state: g && g.state ? g.state() : null, errors: errors });
|
|
54
|
+
if (d.gamedev === 'params' && d.values) {
|
|
55
|
+
for (var k in d.values) window.dshParams[k] = Number(d.values[k]);
|
|
56
|
+
dispatchEvent(new CustomEvent('dshparams', { detail: window.dshParams }));
|
|
57
|
+
}
|
|
58
|
+
if (d.gamedev === 'element' && d.element && d.element.id) {
|
|
59
|
+
window.dshElements[d.element.id] = d.element;
|
|
60
|
+
dispatchEvent(new CustomEvent('dshelements', { detail: d.element }));
|
|
61
|
+
}
|
|
62
|
+
if (d.gamedev === 'pick') {
|
|
63
|
+
var cv = document.querySelector('canvas'), id = null, ref = null, cx = 0, cy = 0;
|
|
64
|
+
if (cv) {
|
|
65
|
+
var rect = cv.getBoundingClientRect(), px = d.x * innerWidth, py = d.y * innerHeight;
|
|
66
|
+
cx = (px - rect.left) * cv.width / rect.width; cy = (py - rect.top) * cv.height / rect.height;
|
|
67
|
+
if (g && typeof g.pick === 'function' && cx >= 0 && cy >= 0 && cx <= cv.width && cy <= cv.height) {
|
|
68
|
+
try { var got = g.pick(cx, cy); if (got && typeof got === 'object') { ref = got; id = got.id || null } else id = got || null } catch (err) { report('error', 'pick: ' + err.message) }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// A string id (2D games) or a structured reference (dsh3d: id, inst, name, state, point, region).
|
|
72
|
+
post({ gamedev: 'picked', id: id, ref: ref, hover: !!d.hover, supported: !!(g && typeof g.pick === 'function'), x: d.x, y: d.y });
|
|
73
|
+
}
|
|
74
|
+
// The panel's pick mode freezes the game; a candidate's changes get ringed. Both optional on the game side.
|
|
75
|
+
// The panel sends them as soon as the files are posted, often before the game's modules ran: the latest
|
|
76
|
+
// of each is kept and applied once window.dshGame appears (polled briefly; installs share one record).
|
|
77
|
+
if (d.gamedev === 'pause' || d.gamedev === 'highlight') {
|
|
78
|
+
var pend = window.__gdPend || (window.__gdPend = {});
|
|
79
|
+
pend[d.gamedev] = d;
|
|
80
|
+
var apply = function () {
|
|
81
|
+
var game = window.dshGame;
|
|
82
|
+
if (!game) return false;
|
|
83
|
+
var p = window.__gdPend || {};
|
|
84
|
+
if (p.pause && typeof game.pause === 'function') { try { game.pause(!!p.pause.on) } catch (err) { report('error', 'pause: ' + err.message) } }
|
|
85
|
+
if (p.highlight && typeof game.highlight === 'function') { try { game.highlight(Array.isArray(p.highlight.list) ? p.highlight.list : []) } catch (err) { report('error', 'highlight: ' + err.message) } }
|
|
86
|
+
window.__gdPend = {};
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
if (!apply() && !window.__gdPendTimer) {
|
|
90
|
+
var tries = 0;
|
|
91
|
+
window.__gdPendTimer = setInterval(function () { if (apply() || ++tries > 150) { clearInterval(window.__gdPendTimer); window.__gdPendTimer = 0 } }, 100);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
})();`;
|
|
96
|
+
/** The loader (runs in the frame): defines `window.__boot(files)`. */
|
|
97
|
+
const LOADER = `window.__boot = function (files) {
|
|
98
|
+
var urls = {};
|
|
99
|
+
function norm(base, rel) {
|
|
100
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(rel) || rel.indexOf('//') === 0) return null;
|
|
101
|
+
var clean = rel.split(/[?#]/)[0], out = [];
|
|
102
|
+
var parts = ((clean.charAt(0) === '/' ? '' : base) + clean).split('/');
|
|
103
|
+
for (var i = 0; i < parts.length; i++) { var s = parts[i]; if (!s || s === '.') continue; if (s === '..') out.pop(); else out.push(s) }
|
|
104
|
+
return out.join('/');
|
|
105
|
+
}
|
|
106
|
+
function dir(p) { return p.indexOf('/') >= 0 ? p.slice(0, p.lastIndexOf('/') + 1) : '' }
|
|
107
|
+
function bytes(p) {
|
|
108
|
+
var f = files[p];
|
|
109
|
+
if (f.text != null) return new TextEncoder().encode(f.text);
|
|
110
|
+
var bin = atob(f.b64), u = new Uint8Array(bin.length);
|
|
111
|
+
for (var i = 0; i < bin.length; i++) u[i] = bin.charCodeAt(i);
|
|
112
|
+
return u;
|
|
113
|
+
}
|
|
114
|
+
function text(p) { return files[p].text != null ? files[p].text : new TextDecoder().decode(bytes(p)) }
|
|
115
|
+
function mint(body, type) { return URL.createObjectURL(new Blob([body], { type: type })) }
|
|
116
|
+
function rewriteCss(css, base) {
|
|
117
|
+
return css.replace(/url\\(\\s*(['"]?)([^'")]+)\\1\\s*\\)/g, function (m, q, u) { var r = norm(base, u); return r && files[r] ? 'url("' + urlFor(r) + '")' : m });
|
|
118
|
+
}
|
|
119
|
+
var busy = {};
|
|
120
|
+
// An exact bare name, else the longest prefix key ending in '/' (import-map style).
|
|
121
|
+
function bareOf(spec) {
|
|
122
|
+
if (bare[spec]) return bare[spec];
|
|
123
|
+
var best = '';
|
|
124
|
+
for (var k in bare) if (k.charAt(k.length - 1) === '/' && spec.indexOf(k) === 0 && k.length > best.length) best = k;
|
|
125
|
+
return best ? bare[best] + spec.slice(best.length) : undefined;
|
|
126
|
+
}
|
|
127
|
+
function modUrl(p) {
|
|
128
|
+
if (urls[p]) return urls[p];
|
|
129
|
+
if (busy[p]) throw new Error('module cycle at ' + p);
|
|
130
|
+
busy[p] = 1;
|
|
131
|
+
var src = text(p).replace(/(\\bimport\\s*\\(\\s*|\\bfrom\\s*|\\bimport\\s+)(['"])([^'"]+)\\2/g, function (m, pre, q, spec) {
|
|
132
|
+
var r = /^\\.{0,2}\\//.test(spec) ? norm(dir(p), spec) : bareOf(spec);
|
|
133
|
+
return r && files[r] ? pre + q + modUrl(r) + q : m;
|
|
134
|
+
});
|
|
135
|
+
delete busy[p];
|
|
136
|
+
return (urls[p] = mint(src, 'text/javascript'));
|
|
137
|
+
}
|
|
138
|
+
function urlFor(p) {
|
|
139
|
+
if (urls[p]) return urls[p];
|
|
140
|
+
if (/\\.m?js$/.test(p)) return modUrl(p);
|
|
141
|
+
if (/\\.css$/.test(p)) return (urls[p] = mint(rewriteCss(text(p), dir(p)), 'text/css'));
|
|
142
|
+
return (urls[p] = mint(bytes(p), files[p].type));
|
|
143
|
+
}
|
|
144
|
+
function rel(v) { var r = typeof v === 'string' && norm('', v); return r && files[r] ? urlFor(r) : null }
|
|
145
|
+
// A project path's blob URL (null when the project has no such file): three's loading
|
|
146
|
+
// manager maps model and texture paths through it, since a relative Request has no usable base here.
|
|
147
|
+
window.__dshFileUrl = rel;
|
|
148
|
+
var nativeFetch = window.fetch.bind(window);
|
|
149
|
+
window.fetch = function (input, init) {
|
|
150
|
+
var u = typeof input === 'string' ? input : input && input.url;
|
|
151
|
+
var r = typeof u === 'string' && norm('', u);
|
|
152
|
+
if (r && files[r]) return Promise.resolve(new Response(bytes(r), { status: 200, headers: { 'content-type': files[r].type } }));
|
|
153
|
+
return nativeFetch(input, init);
|
|
154
|
+
};
|
|
155
|
+
[[HTMLImageElement.prototype, 'src'], [HTMLMediaElement.prototype, 'src'], [HTMLSourceElement.prototype, 'src']].forEach(function (pair) {
|
|
156
|
+
var d = Object.getOwnPropertyDescriptor(pair[0], pair[1]);
|
|
157
|
+
Object.defineProperty(pair[0], pair[1], { configurable: true, enumerable: d.enumerable, get: function () { return d.get.call(this) }, set: function (v) { d.set.call(this, rel(v) || v) } });
|
|
158
|
+
});
|
|
159
|
+
// The kit's bare module map (e.g. 'three' → '_dsh/three.module.js').
|
|
160
|
+
var bare = {};
|
|
161
|
+
try { if (files['_dsh/imports.json']) bare = JSON.parse(text('_dsh/imports.json')) || {} } catch (e) {}
|
|
162
|
+
if (!files['index.html']) { document.body.textContent = '这个版本没有 index.html'; return }
|
|
163
|
+
try {
|
|
164
|
+
var manifest = files['game.json'] ? JSON.parse(text('game.json')) : {};
|
|
165
|
+
window.dshManifest = manifest;
|
|
166
|
+
(manifest.params || []).forEach(function (p) { if (p && p.key && !(p.key in window.dshParams)) window.dshParams[p.key] = Number(p.value) });
|
|
167
|
+
} catch (e) {}
|
|
168
|
+
Object.keys(files).forEach(function (p) {
|
|
169
|
+
if (p.indexOf('elements/') !== 0 || !/\.json$/.test(p)) return;
|
|
170
|
+
try { var raw = JSON.parse(text(p)); (Array.isArray(raw) ? raw : [raw]).forEach(function (el) { if (el && el.id) window.dshElements[el.id] = el }) } catch (e) {}
|
|
171
|
+
});
|
|
172
|
+
var doc = new DOMParser().parseFromString(text('index.html'), 'text/html');
|
|
173
|
+
doc.querySelectorAll('[src]').forEach(function (el) { var u = rel(el.getAttribute('src')); if (u) el.setAttribute('src', u) });
|
|
174
|
+
doc.querySelectorAll('link[href]').forEach(function (el) { var u = rel(el.getAttribute('href')); if (u) el.setAttribute('href', u) });
|
|
175
|
+
doc.querySelectorAll('style').forEach(function (st) { st.textContent = rewriteCss(st.textContent, '') });
|
|
176
|
+
var meta = doc.createElement('meta'); meta.httpEquiv = 'Content-Security-Policy'; meta.content = window.__GD_CSP;
|
|
177
|
+
var bridge = doc.createElement('script'); bridge.textContent = window.__GD_BRIDGE;
|
|
178
|
+
doc.head.prepend(meta, bridge);
|
|
179
|
+
// The kit runtime runs right after the bridge, before any of the game's own scripts.
|
|
180
|
+
if (files['_dsh/runtime.js']) { var kit = doc.createElement('script'); kit.src = urlFor('_dsh/runtime.js'); bridge.after(kit) }
|
|
181
|
+
document.open(); document.write('<!doctype html>' + doc.documentElement.outerHTML); document.close();
|
|
182
|
+
};`;
|
|
183
|
+
/** Starts the boot: pre-set files (headless) or the first `files` message (panel). */
|
|
184
|
+
const START = `if (window.__VFS_FILES__) window.__boot(window.__VFS_FILES__);
|
|
185
|
+
else { var once = false; addEventListener('message', function (e) {
|
|
186
|
+
var d = e.data;
|
|
187
|
+
if (once || !d || d.gamedev !== 'files') return;
|
|
188
|
+
once = true;
|
|
189
|
+
if (d.saves && typeof d.saves === 'object') { var s = window.__gdSaves || (window.__gdSaves = {}); for (var k in d.saves) s[k] = String(d.saves[k]) }
|
|
190
|
+
if (d.params && typeof d.params === 'object') { window.dshParams = window.dshParams || {}; for (var pk in d.params) window.dshParams[pk] = Number(d.params[pk]) }
|
|
191
|
+
window.__boot(d.files);
|
|
192
|
+
}); }`;
|
|
193
|
+
/** Escape a string for embedding inside an inline `<script>` (a literal `</script>` would end it). */
|
|
194
|
+
function scriptSafe(json) {
|
|
195
|
+
return json.replace(/</g, '\\u003c');
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The shell document. `extraHead` lets the headless runner add a marker;
|
|
199
|
+
* the panel passes nothing.
|
|
200
|
+
* @param extraHead - raw HTML placed before the bridge.
|
|
201
|
+
* @returns the complete HTML string.
|
|
202
|
+
*/
|
|
203
|
+
export function shellHtml(extraHead = '') {
|
|
204
|
+
const globals = `window.__GD_CSP=${scriptSafe(JSON.stringify(SANDBOX_CSP))};window.__GD_BRIDGE=${scriptSafe(JSON.stringify(BRIDGE))};`;
|
|
205
|
+
return `<!doctype html><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${SANDBOX_CSP}">`
|
|
206
|
+
+ `<style>html,body{margin:0;height:100%;background:#111;color:#ccc;font:13px system-ui}</style>`
|
|
207
|
+
+ `${extraHead}<script>${BRIDGE}</script><script>${globals}${LOADER}</script><script>${START}</script>`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A single self-contained HTML file for a build (decision: sharing starts as
|
|
211
|
+
* a file export). The same shell, with the files embedded; opened from disk
|
|
212
|
+
* it has a real origin, so the game's own localStorage works natively.
|
|
213
|
+
*/
|
|
214
|
+
export function exportHtml(title, files) {
|
|
215
|
+
const embedded = `<title>${title.replace(/[<&]/g, '')}</title><script>window.__VFS_FILES__=${scriptSafe(JSON.stringify(files))}</script>`;
|
|
216
|
+
return shellHtml(embedded);
|
|
217
|
+
}
|
package/locale/en.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Game Development",
|
|
4
|
+
"description": "Build and play a small 2D web game with the agent: a right-sidebar design workbench (tunable params, look editor, chaptered design doc, versions and rollback) with end-of-turn checks; tools and skills are granted per session."
|
|
5
|
+
}
|
|
6
|
+
}
|
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,135 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khorsheed/dsh-game",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Game-development mode: build and play a small 2D web game with the agent — a right-sidebar game-design workbench (live-tuned params, element look editor with 3-variant proposals, chaptered design doc, versions with candidate confirm, rollback and single-file export), projects in the session workspace, end-of-turn checks (data validation, headless smoke run, the project's own test scripts) that steer failures back into the same turn and deploy passing versions, and panel edits attached to the next send; tools and skills ship as the `./agent` entry the game preset grants",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./agent": {
|
|
14
|
+
"types": "./lib/types/agent.d.ts",
|
|
15
|
+
"default": "./lib/types/agent.js"
|
|
16
|
+
},
|
|
17
|
+
"./types": {
|
|
18
|
+
"types": "./lib/types/types.d.ts",
|
|
19
|
+
"default": "./lib/types.js"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./lib/types/client/index.d.ts",
|
|
23
|
+
"default": "./lib/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./typert": {
|
|
26
|
+
"types": "./lib/typert.host.d.ts",
|
|
27
|
+
"default": "./lib/typert.host.js"
|
|
28
|
+
},
|
|
29
|
+
"./remote": {
|
|
30
|
+
"types": "./lib/typert.remote-client.d.ts",
|
|
31
|
+
"default": "./lib/typert.remote-client.js"
|
|
32
|
+
},
|
|
33
|
+
"./src/*": "./src/*",
|
|
34
|
+
"./package.json": "./package.json",
|
|
35
|
+
"./locale/*.json": "./locale/*.json",
|
|
36
|
+
"./agent/locale/*.json": "./locale/*.json"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"lib",
|
|
40
|
+
"runtime",
|
|
41
|
+
"lib/typert.host.d.ts",
|
|
42
|
+
"lib/typert.host.js",
|
|
43
|
+
"lib/typert.remote-client.d.ts",
|
|
44
|
+
"lib/typert.remote-client.js",
|
|
45
|
+
"cordis.patch.yml",
|
|
46
|
+
"locale/*.json",
|
|
47
|
+
"CHANGELOG.md"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"engines": {
|
|
51
|
+
"dsh": ">=0.2.0-rc.1 <0.3.0-0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"puppeteer-core": "^25.11.0",
|
|
55
|
+
"three": "0.170.0",
|
|
56
|
+
"zod": "^4.4.3"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
60
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-fs": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
74
|
+
"react": "^18.2.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-chat": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@deepseek-ai/dsh-tools": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"react": {
|
|
99
|
+
"optional": true
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"keywords": [
|
|
103
|
+
"dsh",
|
|
104
|
+
"dsh-plugin",
|
|
105
|
+
"cordis",
|
|
106
|
+
"game",
|
|
107
|
+
"gamedev"
|
|
108
|
+
],
|
|
109
|
+
"dsh": {
|
|
110
|
+
"bundle": {
|
|
111
|
+
"patch": "./cordis.patch.yml"
|
|
112
|
+
},
|
|
113
|
+
"client": {
|
|
114
|
+
"inject": [
|
|
115
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
116
|
+
"@deepseek-ai/dsh-client-locale",
|
|
117
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
118
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
119
|
+
"@deepseek-ai/dsh-client-ui-primitives",
|
|
120
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
121
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right"
|
|
122
|
+
],
|
|
123
|
+
"platform": "web"
|
|
124
|
+
},
|
|
125
|
+
"runtimeDependencies": [
|
|
126
|
+
"puppeteer-core",
|
|
127
|
+
"three"
|
|
128
|
+
],
|
|
129
|
+
"compat": {
|
|
130
|
+
"minHost": "0.2.0-rc.1",
|
|
131
|
+
"notes": "The game tab is a page-type right-Sidebar tab (sidebar.right.pane.tab + sidebarRightTabs). The play area is a sandbox=\"allow-scripts allow-pointer-lock\" iframe (opaque origin) fed by postMessage; the host serves no HTTP route. The end-of-turn loop rides agent/turn-stopping + agent.steer (0.2.0 line); on a host without that hook the checks never run and nothing deploys. The headless checks drive the machine's installed Chrome through puppeteer-core (config chromePath / CHROME_PATH); without Chrome only the data checks run and the browser part reports itself skipped. Check findings steered into the turn are dsh-game-sourced messages (panel edits are committed from the panel as new versions; the agent reads them in game_read); the plugin projects them into its own conversation.chat.node kind (uiConversation.events Definition, the message-tools pattern) because the 0.2.0 chat view hides context nodes — the host folds that kind into the turn's step groups (its standalone-kind list is not extensible). element references (play-area picks) go into the composer as reference chips on the input-trigger seam (a headless source carrying the codec, the quote precedent), falling back to the session input's setDraft text without it. The 3d-three kit reads three.js from the pinned three dependency at runtime (kept in the dist manifest through runtimeDependencies). A per-turn change card rides ui-chat's conversation.chat.turnTail seat with a uiConversation.events Definition tracking turn/start and turn/end (the canvas-novel precedent); without that seat the card never registers. Header switch and tags use @deepseek-ai/dsh-client-ui-primitives (Switch, Tag).",
|
|
132
|
+
"verifiedHost": "0.2.1-rc.1"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
6
135
|
}
|