@chocodrop/mcp 0.1.0-alpha.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/bin/chocodrop-mcp.js +13 -0
  4. package/package.json +42 -0
  5. package/runtime/.chocodrop-mcp-runtime +1 -0
  6. package/runtime/asset-bridge.js +460 -0
  7. package/runtime/mcp-server.js +107 -0
  8. package/runtime/site/examples/basic/README.md +38 -0
  9. package/runtime/site/examples/basic/index.html +1730 -0
  10. package/runtime/site/examples/bookmarklet-v2.html +141 -0
  11. package/runtime/site/examples/bookmarklet.html +126 -0
  12. package/runtime/site/examples/lofi-room/index.html +2570 -0
  13. package/runtime/site/examples/lofi-room/sandbox-lite.html +124 -0
  14. package/runtime/site/examples/music-garden/index.html +2150 -0
  15. package/runtime/site/examples/pixel-ocean/index.html +1780 -0
  16. package/runtime/site/examples/react-three-fiber/AdvancedExample.jsx +338 -0
  17. package/runtime/site/examples/react-three-fiber/App.jsx +97 -0
  18. package/runtime/site/examples/react-three-fiber/README.md +294 -0
  19. package/runtime/site/examples/react-three-fiber/package.json +25 -0
  20. package/runtime/site/examples/sdk-test/README.md +70 -0
  21. package/runtime/site/examples/space/index.html +1975 -0
  22. package/runtime/site/examples/toy-city/index.html +2288 -0
  23. package/runtime/site/examples/wabi-sabi/index.html +2515 -0
  24. package/runtime/site/getting-started.html +84 -0
  25. package/runtime/site/index.html +1370 -0
  26. package/runtime/site/public/CommandUI.js +6341 -0
  27. package/runtime/site/public/LiveCommandClient.js +432 -0
  28. package/runtime/site/public/SceneManager.js +5493 -0
  29. package/runtime/site/public/bookmarklet.js +206 -0
  30. package/runtime/site/public/bootstrap.js +50 -0
  31. package/runtime/site/public/chocodrop-demo.umd.js +24020 -0
  32. package/runtime/site/public/chocodrop-demo.umd.min.js +24020 -0
  33. package/runtime/site/public/chocodrop.esm.js +2 -0
  34. package/runtime/site/public/html-sandbox/frame.js +865 -0
  35. package/runtime/site/public/icons/icon-192.png +0 -0
  36. package/runtime/site/public/icons/icon-512.png +0 -0
  37. package/runtime/site/public/icons/icon-maskable-512.png +0 -0
  38. package/runtime/site/public/immersive-launcher.js +175 -0
  39. package/runtime/site/public/immersive.html +165 -0
  40. package/runtime/site/public/index.html +1011 -0
  41. package/runtime/site/public/index.js +9 -0
  42. package/runtime/site/public/load-chocodrop.js +25 -0
  43. package/runtime/site/public/load-three.js +26 -0
  44. package/runtime/site/public/manifest.webmanifest +59 -0
  45. package/runtime/site/public/pwa-bootstrap.js +128 -0
  46. package/runtime/site/public/sample-card.svg +15 -0
  47. package/runtime/site/public/service-worker.js +117 -0
  48. package/runtime/site/public/translation-dictionary.js +257 -0
  49. package/runtime/site/public/xr/xr-ui.css +338 -0
  50. package/runtime/site/public/xr-viewer.html +263 -0
  51. package/runtime/site/src/client/local-bridge.js +83 -0
@@ -0,0 +1,206 @@
1
+ const DEFAULT_BASE = 'http://127.0.0.1:43110';
2
+ const DEFAULT_RUNTIME = 'https://nyukicorn.github.io/chocodrop/runtime/ui.global.js';
3
+
4
+ export function normalizeDaemonBase(value = DEFAULT_BASE) {
5
+ const url = new URL(value);
6
+ if (url.protocol !== 'http:' || url.hostname !== '127.0.0.1' || url.port !== '43110') {
7
+ throw new Error('ChocoDrop daemon must use http://127.0.0.1:43110');
8
+ }
9
+ return url.origin;
10
+ }
11
+
12
+ function normalizeRuntimeUrl(value = DEFAULT_RUNTIME) {
13
+ const url = new URL(value);
14
+ const isLocalPreview = url.protocol === 'http:' && ['127.0.0.1', 'localhost'].includes(url.hostname);
15
+ if (url.protocol !== 'https:' && !isLocalPreview) {
16
+ throw new Error('ChocoDrop runtime must use HTTPS or a local preview URL');
17
+ }
18
+ return url.href;
19
+ }
20
+
21
+ export function createBookmarklet(value = DEFAULT_BASE, runtimeValue = DEFAULT_RUNTIME) {
22
+ const base = normalizeDaemonBase(value);
23
+ const runtimeUrl = normalizeRuntimeUrl(runtimeValue);
24
+
25
+ async function run(daemonBase, uiRuntimeUrl) {
26
+ const ids = {
27
+ notice: '__chocodrop_bookmarklet_notice__',
28
+ runtime: '__chocodrop_bookmarklet_runtime__'
29
+ };
30
+
31
+ function notice(title, message, command = '') {
32
+ document.getElementById(ids.notice)?.remove();
33
+ const root = document.createElement('div');
34
+ root.id = ids.notice;
35
+ root.setAttribute('role', 'status');
36
+ Object.assign(root.style, {
37
+ position: 'fixed',
38
+ right: '18px',
39
+ bottom: '18px',
40
+ zIndex: '2147483647',
41
+ width: 'min(390px, calc(100vw - 36px))',
42
+ padding: '16px',
43
+ border: '1px solid rgba(214, 170, 239, .35)',
44
+ borderRadius: '16px',
45
+ background: '#211d28',
46
+ color: '#f7eff5',
47
+ boxShadow: '0 18px 60px rgba(9, 7, 13, .45)',
48
+ font: '13px/1.7 system-ui, -apple-system, sans-serif'
49
+ });
50
+ const heading = document.createElement('strong');
51
+ heading.textContent = `🍫 ${title}`;
52
+ heading.style.display = 'block';
53
+ heading.style.paddingRight = '32px';
54
+ const body = document.createElement('p');
55
+ body.textContent = message;
56
+ body.style.margin = '7px 0 0';
57
+ body.style.color = '#d3c5d8';
58
+ const close = document.createElement('button');
59
+ close.type = 'button';
60
+ close.textContent = '×';
61
+ close.setAttribute('aria-label', '閉じる');
62
+ Object.assign(close.style, {
63
+ position: 'absolute',
64
+ top: '9px',
65
+ right: '10px',
66
+ border: '0',
67
+ background: 'transparent',
68
+ color: '#f7eff5',
69
+ fontSize: '22px',
70
+ cursor: 'pointer'
71
+ });
72
+ close.onclick = () => root.remove();
73
+ root.append(heading, body, close);
74
+ if (command) {
75
+ const copy = document.createElement('button');
76
+ copy.type = 'button';
77
+ copy.textContent = '起動コマンドをコピー';
78
+ Object.assign(copy.style, {
79
+ marginTop: '12px',
80
+ padding: '9px 14px',
81
+ border: '0',
82
+ borderRadius: '999px',
83
+ background: '#cda8ef',
84
+ color: '#281935',
85
+ fontWeight: '700',
86
+ cursor: 'pointer'
87
+ });
88
+ copy.onclick = async () => {
89
+ try {
90
+ await navigator.clipboard.writeText(command);
91
+ copy.textContent = 'コピーしました';
92
+ } catch {
93
+ copy.textContent = command;
94
+ }
95
+ };
96
+ root.append(copy);
97
+ }
98
+ document.body.append(root);
99
+ return root;
100
+ }
101
+
102
+ async function daemonIsReady() {
103
+ const controller = new AbortController();
104
+ const timer = setTimeout(() => controller.abort(), 1800);
105
+ try {
106
+ const response = await fetch(`${daemonBase}/v1/health`, {
107
+ cache: 'no-store',
108
+ signal: controller.signal
109
+ });
110
+ return response.ok;
111
+ } catch {
112
+ return false;
113
+ } finally {
114
+ clearTimeout(timer);
115
+ }
116
+ }
117
+
118
+ function findThreeObjects() {
119
+ let scene = null;
120
+ let camera = null;
121
+ let renderer = null;
122
+ const preferred = ['scene', 'camera', 'renderer'];
123
+ const keys = [...new Set([...preferred, ...Object.keys(window)])];
124
+ for (const key of keys) {
125
+ let value;
126
+ try {
127
+ value = window[key];
128
+ } catch {
129
+ continue;
130
+ }
131
+ if (!scene && value?.isScene) scene = value;
132
+ if (!camera && value?.isCamera) camera = value;
133
+ if (!renderer && value?.isWebGLRenderer) renderer = value;
134
+ if (scene && camera && renderer) break;
135
+ }
136
+ return { scene, camera, renderer };
137
+ }
138
+
139
+ async function ensureThree() {
140
+ if (window.THREE) return;
141
+ const threeUrl = new URL('../vendor/three-0.170.0.min.js', uiRuntimeUrl).href;
142
+ window.THREE = await import(threeUrl);
143
+ }
144
+
145
+ async function attach(three) {
146
+ if (!window.ChocoDropUI?.attach) throw new Error('ChocoDrop UI API was not found');
147
+ await window.ChocoDropUI.attach(three.scene, {
148
+ camera: three.camera,
149
+ renderer: three.renderer
150
+ });
151
+ window.__chocodropBookmarkletAttached = true;
152
+ notice('接続しました', 'Three.jsシーンを検出しました。右下のChocoDropを開いてください。');
153
+ }
154
+
155
+ if (window.__chocodropBookmarkletAttached) {
156
+ notice('接続済みです', '右下のChocoDropを開いてください。');
157
+ return;
158
+ }
159
+
160
+ if (!(await daemonIsReady())) {
161
+ notice(
162
+ 'ローカルdaemonを起動してください',
163
+ 'ターミナルで起動した後、このブックマークをもう一度押してください。',
164
+ 'npx --yes @chocodrop/daemon@alpha'
165
+ );
166
+ return;
167
+ }
168
+
169
+ const three = findThreeObjects();
170
+ if (!three.scene) {
171
+ notice('Three.jsシーンが見つかりません', 'このページでは素材を配置できません。シーンをグローバルに公開したページでお試しください。');
172
+ return;
173
+ }
174
+
175
+ try {
176
+ await ensureThree();
177
+ } catch {
178
+ notice('Three.jsを準備できませんでした', 'このページのセキュリティ設定が、必要なモジュールの読込を禁止している可能性があります。');
179
+ return;
180
+ }
181
+
182
+ const existing = document.getElementById(ids.runtime);
183
+ if (existing && window.ChocoDropUI) {
184
+ try {
185
+ await attach(three);
186
+ } catch (error) {
187
+ notice('接続できませんでした', error?.message || 'ページを再読み込みして、もう一度お試しください。');
188
+ }
189
+ return;
190
+ }
191
+
192
+ const script = document.createElement('script');
193
+ script.id = ids.runtime;
194
+ script.src = uiRuntimeUrl;
195
+ script.crossOrigin = 'anonymous';
196
+ script.onload = () => attach(three).catch(error => {
197
+ notice('接続できませんでした', error?.message || 'ページを再読み込みして、もう一度お試しください。');
198
+ });
199
+ script.onerror = () => {
200
+ notice('ChocoDropを読み込めませんでした', 'このページのセキュリティ設定が、ローカルスクリプトを禁止している可能性があります。');
201
+ };
202
+ document.head.append(script);
203
+ }
204
+
205
+ return `javascript:(${run.toString()})(${JSON.stringify(base)},${JSON.stringify(runtimeUrl)})`;
206
+ }
@@ -0,0 +1,50 @@
1
+ import { ChocoDropClient, ChocoDroClient, LiveCommandClient } from './LiveCommandClient.js';
2
+ import { SceneManager } from './SceneManager.js';
3
+ import { CommandUI } from './CommandUI.js';
4
+
5
+ export function createChocoDrop(scene, options = {}) {
6
+ if (!scene) {
7
+ throw new Error('THREE.Scene インスタンスが必要です');
8
+ }
9
+
10
+ const {
11
+ camera = null,
12
+ renderer = null,
13
+ serverUrl = null,
14
+ client = null,
15
+ onControlsToggle = () => {},
16
+ sceneOptions = {},
17
+ uiOptions = {}
18
+ } = options;
19
+
20
+ const resolvedServerUrl = serverUrl || sceneOptions.serverUrl || null;
21
+ const chocoDropClient = client || new ChocoDropClient(resolvedServerUrl);
22
+
23
+ const sceneManager = new SceneManager(scene, {
24
+ camera,
25
+ renderer,
26
+ serverUrl: resolvedServerUrl,
27
+ client: chocoDropClient,
28
+ ...sceneOptions
29
+ });
30
+
31
+ const commandUI = new CommandUI({
32
+ sceneManager,
33
+ client: chocoDropClient,
34
+ onControlsToggle,
35
+ ...uiOptions
36
+ });
37
+
38
+ return {
39
+ client: chocoDropClient,
40
+ sceneManager,
41
+ ui: commandUI,
42
+ dispose() {
43
+ commandUI.dispose?.();
44
+ sceneManager.dispose?.();
45
+ }
46
+ };
47
+ }
48
+
49
+ export const createChocoDro = createChocoDrop;
50
+ export const createLiveCommand = createChocoDrop;