@series-inc/venus-sdk 3.4.2 → 3.4.3-beta.1
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/{AdsApi-ihIIoDSK.d.ts → AdsApi-Dt9Yx0qb.d.ts} +30 -2
- package/dist/SandboxHost-PG4SXT3M.js +1880 -0
- package/dist/SandboxHost-PG4SXT3M.js.map +1 -0
- package/dist/chunk-MQ4UC45P.js +1890 -0
- package/dist/chunk-MQ4UC45P.js.map +1 -0
- package/dist/{chunk-W74ZI2H3.js → chunk-Q7SNANYR.js} +67 -1872
- package/dist/chunk-Q7SNANYR.js.map +1 -0
- package/dist/index.d.ts +31 -4
- package/dist/index.js +2 -1
- package/dist/venus-api/index.d.ts +2 -2
- package/dist/venus-api/index.js +44 -29
- package/dist/venus-api/index.js.map +1 -1
- package/dist/vite/index.css +19 -0
- package/dist/vite/index.css.map +1 -0
- package/dist/vite/index.d.ts +100 -1
- package/dist/vite/index.js +1165 -3
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-W74ZI2H3.js.map +0 -1
package/dist/vite/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs';
|
|
1
|
+
import path, { join, resolve, dirname } from 'path';
|
|
2
|
+
import fs, { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { createRequire } from 'module';
|
|
3
5
|
|
|
4
6
|
// src/vite/venusLibrariesPlugin.ts
|
|
5
7
|
|
|
@@ -1071,6 +1073,1166 @@ function cdnPlugin() {
|
|
|
1071
1073
|
};
|
|
1072
1074
|
}
|
|
1073
1075
|
|
|
1074
|
-
|
|
1076
|
+
// raw-loader:/Users/pchan/Development/series/venus/venus-sdk/packages/api/src/vite/sandboxToolbarStyle.css
|
|
1077
|
+
var sandboxToolbarStyle_default = {};
|
|
1078
|
+
|
|
1079
|
+
// raw-loader:/Users/pchan/Development/series/venus/venus-sdk/packages/api/src/vite/sandboxToolbarScript.js
|
|
1080
|
+
var sandboxToolbarScript_default = `/**\r
|
|
1081
|
+
* Venus Sandbox Toolbar\r
|
|
1082
|
+
*\r
|
|
1083
|
+
* Floating toolbar that shows auth status and provides sign-in functionality.\r
|
|
1084
|
+
* Auth state is managed by Firebase Auth and persisted in IndexedDB.\r
|
|
1085
|
+
*/\r
|
|
1086
|
+
\r
|
|
1087
|
+
const LOCAL_STORAGE_POS_KEY = 'venus-sandbox-pos'\r
|
|
1088
|
+
\r
|
|
1089
|
+
;(async () => {\r
|
|
1090
|
+
try {\r
|
|
1091
|
+
if (!shouldRenderToolbar()) {\r
|
|
1092
|
+
return\r
|
|
1093
|
+
}\r
|
|
1094
|
+
\r
|
|
1095
|
+
await ensureDomReady()\r
|
|
1096
|
+
\r
|
|
1097
|
+
const sandboxConfig = window.__VENUS_SANDBOX__ || {}\r
|
|
1098
|
+
const sandboxEnabled = isSandboxEnabled(sandboxConfig)\r
|
|
1099
|
+
\r
|
|
1100
|
+
if (!sandboxEnabled) {\r
|
|
1101
|
+
return\r
|
|
1102
|
+
}\r
|
|
1103
|
+
\r
|
|
1104
|
+
// Create minimal FAB with inline styles\r
|
|
1105
|
+
createFloatingToolbar(sandboxConfig)\r
|
|
1106
|
+
} catch (error) {\r
|
|
1107
|
+
console.error('[Venus Sandbox] Failed to render toolbar:', error)\r
|
|
1108
|
+
}\r
|
|
1109
|
+
})()\r
|
|
1110
|
+
\r
|
|
1111
|
+
function shouldRenderToolbar() {\r
|
|
1112
|
+
if (typeof window === 'undefined') {\r
|
|
1113
|
+
return false\r
|
|
1114
|
+
}\r
|
|
1115
|
+
const host = location.hostname\r
|
|
1116
|
+
return host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')\r
|
|
1117
|
+
}\r
|
|
1118
|
+
\r
|
|
1119
|
+
function ensureDomReady() {\r
|
|
1120
|
+
if (document.readyState === 'loading') {\r
|
|
1121
|
+
return new Promise((resolve) => {\r
|
|
1122
|
+
document.addEventListener('DOMContentLoaded', resolve, { once: true })\r
|
|
1123
|
+
})\r
|
|
1124
|
+
}\r
|
|
1125
|
+
return Promise.resolve()\r
|
|
1126
|
+
}\r
|
|
1127
|
+
\r
|
|
1128
|
+
function isSandboxEnabled(sandboxConfig) {\r
|
|
1129
|
+
return Boolean(sandboxConfig && sandboxConfig.enabled === true)\r
|
|
1130
|
+
}\r
|
|
1131
|
+
\r
|
|
1132
|
+
function createFloatingToolbar(sandboxConfig) {\r
|
|
1133
|
+
// Create main FAB element with inline styles\r
|
|
1134
|
+
const fab = document.createElement('div')\r
|
|
1135
|
+
fab.id = 'venus-sandbox-toolbar'\r
|
|
1136
|
+
\r
|
|
1137
|
+
// Get environment for theming\r
|
|
1138
|
+
const target = String(sandboxConfig?.target || 'local').toLowerCase()\r
|
|
1139
|
+
const envGradients = {\r
|
|
1140
|
+
local: 'linear-gradient(120deg, #f59e0b, #d97706)',\r
|
|
1141
|
+
dev: 'linear-gradient(120deg, #0891b2, #10b981)',\r
|
|
1142
|
+
staging: 'linear-gradient(120deg, #8b5cf6, #6366f1)',\r
|
|
1143
|
+
}\r
|
|
1144
|
+
const envLabels = {\r
|
|
1145
|
+
local: 'LOCAL',\r
|
|
1146
|
+
dev: 'DEV',\r
|
|
1147
|
+
staging: 'STAGING',\r
|
|
1148
|
+
}\r
|
|
1149
|
+
const initialGradient = envGradients[target] || envGradients.local\r
|
|
1150
|
+
const envLabel = envLabels[target] || 'LOCAL'\r
|
|
1151
|
+
\r
|
|
1152
|
+
// Get saved position or use defaults\r
|
|
1153
|
+
const savedPos = localStorage.getItem(LOCAL_STORAGE_POS_KEY)\r
|
|
1154
|
+
let posRight = 16\r
|
|
1155
|
+
let posY = Math.round((window.innerHeight - 36) / 2)\r
|
|
1156
|
+
\r
|
|
1157
|
+
if (savedPos) {\r
|
|
1158
|
+
try {\r
|
|
1159
|
+
const parsed = JSON.parse(savedPos)\r
|
|
1160
|
+
posRight = parsed.right\r
|
|
1161
|
+
posY = parsed.y\r
|
|
1162
|
+
} catch (e) {\r
|
|
1163
|
+
// Ignore invalid saved position\r
|
|
1164
|
+
}\r
|
|
1165
|
+
}\r
|
|
1166
|
+
\r
|
|
1167
|
+
// Ensure within bounds\r
|
|
1168
|
+
posRight = Math.min(Math.max(0, posRight), window.innerWidth - 36)\r
|
|
1169
|
+
posY = Math.min(Math.max(0, posY), window.innerHeight - 36)\r
|
|
1170
|
+
\r
|
|
1171
|
+
// Start with "checking" state using env gradient\r
|
|
1172
|
+
fab.style.cssText = \`\r
|
|
1173
|
+
position: fixed;\r
|
|
1174
|
+
right: \${posRight}px;\r
|
|
1175
|
+
top: \${posY}px;\r
|
|
1176
|
+
width: auto;\r
|
|
1177
|
+
min-width: 160px;\r
|
|
1178
|
+
height: 36px;\r
|
|
1179
|
+
padding: 0;\r
|
|
1180
|
+
margin: 0;\r
|
|
1181
|
+
border: none;\r
|
|
1182
|
+
border-radius: 999px;\r
|
|
1183
|
+
background: \${initialGradient};\r
|
|
1184
|
+
color: #ffffff;\r
|
|
1185
|
+
font-family: system-ui, -apple-system, sans-serif;\r
|
|
1186
|
+
font-size: 12px;\r
|
|
1187
|
+
font-weight: 600;\r
|
|
1188
|
+
letter-spacing: 0.02em;\r
|
|
1189
|
+
cursor: grab;\r
|
|
1190
|
+
user-select: none;\r
|
|
1191
|
+
z-index: 2147483601;\r
|
|
1192
|
+
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.35);\r
|
|
1193
|
+
display: flex;\r
|
|
1194
|
+
align-items: center;\r
|
|
1195
|
+
gap: 0;\r
|
|
1196
|
+
pointer-events: auto;\r
|
|
1197
|
+
border: 1px solid rgba(255, 255, 255, 0.2);\r
|
|
1198
|
+
\`\r
|
|
1199
|
+
\r
|
|
1200
|
+
// Store env config on fab for later use\r
|
|
1201
|
+
fab.dataset.target = target\r
|
|
1202
|
+
fab.dataset.envGradient = initialGradient\r
|
|
1203
|
+
\r
|
|
1204
|
+
// Create env badge\r
|
|
1205
|
+
const envBadge = document.createElement('div')\r
|
|
1206
|
+
envBadge.style.cssText = \`\r
|
|
1207
|
+
padding: 0 10px;\r
|
|
1208
|
+
height: 100%;\r
|
|
1209
|
+
display: flex;\r
|
|
1210
|
+
align-items: center;\r
|
|
1211
|
+
font-size: 10px;\r
|
|
1212
|
+
font-weight: 700;\r
|
|
1213
|
+
letter-spacing: 0.05em;\r
|
|
1214
|
+
text-transform: uppercase;\r
|
|
1215
|
+
background: rgba(0, 0, 0, 0.2);\r
|
|
1216
|
+
border-top-left-radius: 999px;\r
|
|
1217
|
+
border-bottom-left-radius: 999px;\r
|
|
1218
|
+
\`\r
|
|
1219
|
+
envBadge.textContent = envLabel\r
|
|
1220
|
+
\r
|
|
1221
|
+
// Create label container\r
|
|
1222
|
+
const labelDiv = document.createElement('div')\r
|
|
1223
|
+
labelDiv.style.cssText = \`\r
|
|
1224
|
+
padding: 0 8px 0 10px;\r
|
|
1225
|
+
height: 100%;\r
|
|
1226
|
+
display: flex;\r
|
|
1227
|
+
align-items: center;\r
|
|
1228
|
+
white-space: nowrap;\r
|
|
1229
|
+
cursor: pointer;\r
|
|
1230
|
+
\`\r
|
|
1231
|
+
labelDiv.textContent = '\u{1FA90} Checking...'\r
|
|
1232
|
+
\r
|
|
1233
|
+
// Create handle for dragging\r
|
|
1234
|
+
const handleDiv = document.createElement('div')\r
|
|
1235
|
+
handleDiv.style.cssText = \`\r
|
|
1236
|
+
width: 38px;\r
|
|
1237
|
+
height: 100%;\r
|
|
1238
|
+
display: flex;\r
|
|
1239
|
+
align-items: center;\r
|
|
1240
|
+
justify-content: center;\r
|
|
1241
|
+
font-size: 18px;\r
|
|
1242
|
+
font-weight: 600;\r
|
|
1243
|
+
background: rgba(0, 0, 0, 0.25);\r
|
|
1244
|
+
border-top-right-radius: 999px;\r
|
|
1245
|
+
border-bottom-right-radius: 999px;\r
|
|
1246
|
+
cursor: grab;\r
|
|
1247
|
+
\`\r
|
|
1248
|
+
handleDiv.textContent = '\u271C'\r
|
|
1249
|
+
\r
|
|
1250
|
+
fab.appendChild(envBadge)\r
|
|
1251
|
+
fab.appendChild(labelDiv)\r
|
|
1252
|
+
fab.appendChild(handleDiv)\r
|
|
1253
|
+
\r
|
|
1254
|
+
const toolbarRoot = document.getElementById('venus-toolbar-root')\r
|
|
1255
|
+
;(toolbarRoot || document.body).appendChild(fab)\r
|
|
1256
|
+
\r
|
|
1257
|
+
// Set up drag handling\r
|
|
1258
|
+
setupDragHandling(fab, handleDiv)\r
|
|
1259
|
+
\r
|
|
1260
|
+
// Set up collapse/expand behavior\r
|
|
1261
|
+
setupCollapseExpand(fab, envBadge, labelDiv)\r
|
|
1262
|
+
\r
|
|
1263
|
+
// Click handler for label to open modal\r
|
|
1264
|
+
labelDiv.addEventListener('click', () => {\r
|
|
1265
|
+
openAuthModal(sandboxConfig, getCurrentAuthState())\r
|
|
1266
|
+
})\r
|
|
1267
|
+
\r
|
|
1268
|
+
// Listen for auth state changes (set up by MockHost)\r
|
|
1269
|
+
window.addEventListener('venus-auth-state-changed', (e) => {\r
|
|
1270
|
+
updateToolbarState(fab, labelDiv, e.detail)\r
|
|
1271
|
+
})\r
|
|
1272
|
+
\r
|
|
1273
|
+
// Check initial auth state after a short delay (give Firebase time to restore session)\r
|
|
1274
|
+
setTimeout(() => {\r
|
|
1275
|
+
checkInitialAuthState(fab, labelDiv, sandboxConfig)\r
|
|
1276
|
+
}, 500)\r
|
|
1277
|
+
}\r
|
|
1278
|
+
\r
|
|
1279
|
+
let currentAuthState = { signedIn: false, user: null }\r
|
|
1280
|
+
\r
|
|
1281
|
+
function getCurrentAuthState() {\r
|
|
1282
|
+
return currentAuthState\r
|
|
1283
|
+
}\r
|
|
1284
|
+
\r
|
|
1285
|
+
function checkInitialAuthState(fab, labelDiv, sandboxConfig) {\r
|
|
1286
|
+
// Dispatch event to check auth state (MockHost will respond)\r
|
|
1287
|
+
window.dispatchEvent(new CustomEvent('venus-check-auth-state'))\r
|
|
1288
|
+
\r
|
|
1289
|
+
// Also set a timeout to show sign-in prompt if no response\r
|
|
1290
|
+
setTimeout(() => {\r
|
|
1291
|
+
if (!currentAuthState.signedIn) {\r
|
|
1292
|
+
updateToolbarState(fab, labelDiv, { signedIn: false, user: null })\r
|
|
1293
|
+
}\r
|
|
1294
|
+
}, 2000)\r
|
|
1295
|
+
}\r
|
|
1296
|
+
\r
|
|
1297
|
+
function updateToolbarState(fab, labelDiv, authState) {\r
|
|
1298
|
+
currentAuthState = authState\r
|
|
1299
|
+
\r
|
|
1300
|
+
// Get stored env gradient\r
|
|
1301
|
+
const envGradient = fab.dataset.envGradient || 'linear-gradient(120deg, #f59e0b, #d97706)'\r
|
|
1302
|
+
\r
|
|
1303
|
+
if (authState.signedIn && authState.user) {\r
|
|
1304
|
+
// Signed in - use env gradient with full opacity\r
|
|
1305
|
+
fab.style.background = envGradient\r
|
|
1306
|
+
fab.style.opacity = '1'\r
|
|
1307
|
+
const displayName = authState.user.displayName || authState.user.email || 'Signed in'\r
|
|
1308
|
+
labelDiv.textContent = \`\u2713 \${truncate(displayName, 14)}\`\r
|
|
1309
|
+
} else {\r
|
|
1310
|
+
// Not signed in - use env gradient but slightly dimmed, with sign-in prompt\r
|
|
1311
|
+
fab.style.background = envGradient\r
|
|
1312
|
+
fab.style.opacity = '0.85'\r
|
|
1313
|
+
labelDiv.textContent = '\u25CB Sign in'\r
|
|
1314
|
+
}\r
|
|
1315
|
+
}\r
|
|
1316
|
+
\r
|
|
1317
|
+
function truncate(str, maxLength) {\r
|
|
1318
|
+
if (str.length <= maxLength) return str\r
|
|
1319
|
+
return str.substring(0, maxLength - 1) + '\u2026'\r
|
|
1320
|
+
}\r
|
|
1321
|
+
\r
|
|
1322
|
+
function setupCollapseExpand(fab, envBadge, labelDiv) {\r
|
|
1323
|
+
let collapseTimeout\r
|
|
1324
|
+
let isCollapsed = false\r
|
|
1325
|
+
\r
|
|
1326
|
+
const collapse = () => {\r
|
|
1327
|
+
fab.style.minWidth = '36px'\r
|
|
1328
|
+
fab.style.opacity = '0.4'\r
|
|
1329
|
+
envBadge.style.display = 'none'\r
|
|
1330
|
+
labelDiv.style.display = 'none'\r
|
|
1331
|
+
isCollapsed = true\r
|
|
1332
|
+
}\r
|
|
1333
|
+
\r
|
|
1334
|
+
const expand = () => {\r
|
|
1335
|
+
clearTimeout(collapseTimeout)\r
|
|
1336
|
+
fab.style.minWidth = '160px'\r
|
|
1337
|
+
fab.style.opacity = '1'\r
|
|
1338
|
+
envBadge.style.display = 'flex'\r
|
|
1339
|
+
labelDiv.style.display = 'flex'\r
|
|
1340
|
+
isCollapsed = false\r
|
|
1341
|
+
}\r
|
|
1342
|
+
\r
|
|
1343
|
+
const scheduleCollapse = () => {\r
|
|
1344
|
+
clearTimeout(collapseTimeout)\r
|
|
1345
|
+
collapseTimeout = setTimeout(collapse, 5000)\r
|
|
1346
|
+
}\r
|
|
1347
|
+
\r
|
|
1348
|
+
// Collapse after 5 seconds\r
|
|
1349
|
+
scheduleCollapse()\r
|
|
1350
|
+
\r
|
|
1351
|
+
fab.addEventListener('mouseenter', expand)\r
|
|
1352
|
+
fab.addEventListener('mouseleave', () => {\r
|
|
1353
|
+
if (!isCollapsed) scheduleCollapse()\r
|
|
1354
|
+
})\r
|
|
1355
|
+
}\r
|
|
1356
|
+
\r
|
|
1357
|
+
function setupDragHandling(fab, handleDiv) {\r
|
|
1358
|
+
let isDragging = false\r
|
|
1359
|
+
let startX, startY, offsetRight, offsetY\r
|
|
1360
|
+
\r
|
|
1361
|
+
fab.addEventListener('mousedown', (e) => {\r
|
|
1362
|
+
isDragging = true\r
|
|
1363
|
+
startX = e.clientX\r
|
|
1364
|
+
startY = e.clientY\r
|
|
1365
|
+
offsetRight = window.innerWidth - fab.offsetLeft - fab.offsetWidth\r
|
|
1366
|
+
offsetY = fab.offsetTop\r
|
|
1367
|
+
fab.style.cursor = 'grabbing'\r
|
|
1368
|
+
e.preventDefault()\r
|
|
1369
|
+
})\r
|
|
1370
|
+
\r
|
|
1371
|
+
document.addEventListener('mousemove', (e) => {\r
|
|
1372
|
+
if (!isDragging) return\r
|
|
1373
|
+
\r
|
|
1374
|
+
let newRight = offsetRight - (e.clientX - startX)\r
|
|
1375
|
+
let newTop = offsetY + (e.clientY - startY)\r
|
|
1376
|
+
\r
|
|
1377
|
+
newRight = Math.min(Math.max(0, newRight), window.innerWidth - 36)\r
|
|
1378
|
+
newTop = Math.min(Math.max(0, newTop), window.innerHeight - 36)\r
|
|
1379
|
+
\r
|
|
1380
|
+
fab.style.right = \`\${newRight}px\`\r
|
|
1381
|
+
fab.style.top = \`\${newTop}px\`\r
|
|
1382
|
+
})\r
|
|
1383
|
+
\r
|
|
1384
|
+
document.addEventListener('mouseup', () => {\r
|
|
1385
|
+
if (!isDragging) return\r
|
|
1386
|
+
isDragging = false\r
|
|
1387
|
+
fab.style.cursor = 'grab'\r
|
|
1388
|
+
const currentRight = window.innerWidth - fab.offsetLeft - fab.offsetWidth\r
|
|
1389
|
+
localStorage.setItem(LOCAL_STORAGE_POS_KEY, JSON.stringify({\r
|
|
1390
|
+
right: currentRight,\r
|
|
1391
|
+
y: fab.offsetTop,\r
|
|
1392
|
+
}))\r
|
|
1393
|
+
})\r
|
|
1394
|
+
}\r
|
|
1395
|
+
\r
|
|
1396
|
+
function openAuthModal(sandboxConfig, authState) {\r
|
|
1397
|
+
const overlay = document.createElement('div')\r
|
|
1398
|
+
overlay.style.cssText = \`\r
|
|
1399
|
+
position: fixed;\r
|
|
1400
|
+
top: 0;\r
|
|
1401
|
+
left: 0;\r
|
|
1402
|
+
right: 0;\r
|
|
1403
|
+
bottom: 0;\r
|
|
1404
|
+
background: rgba(0, 0, 0, 0.6);\r
|
|
1405
|
+
display: flex;\r
|
|
1406
|
+
align-items: center;\r
|
|
1407
|
+
justify-content: center;\r
|
|
1408
|
+
z-index: 2147483602;\r
|
|
1409
|
+
backdrop-filter: blur(4px);\r
|
|
1410
|
+
\`\r
|
|
1411
|
+
\r
|
|
1412
|
+
const content = document.createElement('div')\r
|
|
1413
|
+
content.style.cssText = \`\r
|
|
1414
|
+
background: #111827;\r
|
|
1415
|
+
color: #F3F4F6;\r
|
|
1416
|
+
border: 1px solid rgba(255, 255, 255, 0.1);\r
|
|
1417
|
+
border-radius: 16px;\r
|
|
1418
|
+
width: 90%;\r
|
|
1419
|
+
max-width: 420px;\r
|
|
1420
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);\r
|
|
1421
|
+
font-family: system-ui, -apple-system, sans-serif;\r
|
|
1422
|
+
overflow: hidden;\r
|
|
1423
|
+
\`\r
|
|
1424
|
+
\r
|
|
1425
|
+
const target = String(sandboxConfig?.target || 'local').toLowerCase()\r
|
|
1426
|
+
const targetUpper = target.toUpperCase()\r
|
|
1427
|
+
const gameId = sandboxConfig?.gameId || 'Unknown'\r
|
|
1428
|
+
const isSignedIn = authState.signedIn\r
|
|
1429
|
+
const user = authState.user\r
|
|
1430
|
+
const isLocal = target === 'local'\r
|
|
1431
|
+
\r
|
|
1432
|
+
// Environment-specific theming\r
|
|
1433
|
+
const envConfig = {\r
|
|
1434
|
+
local: {\r
|
|
1435
|
+
gradient: 'linear-gradient(120deg, #f59e0b, #d97706)',\r
|
|
1436
|
+
badge: '#f59e0b',\r
|
|
1437
|
+
badgeBg: 'rgba(245, 158, 11, 0.15)',\r
|
|
1438
|
+
label: 'LOCAL',\r
|
|
1439
|
+
subtitle: 'Firebase Emulator',\r
|
|
1440
|
+
},\r
|
|
1441
|
+
dev: {\r
|
|
1442
|
+
gradient: 'linear-gradient(120deg, #0891b2, #10b981)',\r
|
|
1443
|
+
badge: '#10b981',\r
|
|
1444
|
+
badgeBg: 'rgba(16, 185, 129, 0.15)',\r
|
|
1445
|
+
label: 'DEV',\r
|
|
1446
|
+
subtitle: 'Development Server',\r
|
|
1447
|
+
},\r
|
|
1448
|
+
staging: {\r
|
|
1449
|
+
gradient: 'linear-gradient(120deg, #8b5cf6, #6366f1)',\r
|
|
1450
|
+
badge: '#8b5cf6',\r
|
|
1451
|
+
badgeBg: 'rgba(139, 92, 246, 0.15)',\r
|
|
1452
|
+
label: 'STAGING',\r
|
|
1453
|
+
subtitle: 'Staging Server',\r
|
|
1454
|
+
},\r
|
|
1455
|
+
}\r
|
|
1456
|
+
\r
|
|
1457
|
+
const env = envConfig[target] || envConfig.local\r
|
|
1458
|
+
const statusIcon = isSignedIn ? '\u2713' : '\u25CB'\r
|
|
1459
|
+
const statusText = isSignedIn ? 'Signed in' : 'Not signed in'\r
|
|
1460
|
+
const statusColor = isSignedIn ? '#10b981' : '#6b7280'\r
|
|
1461
|
+
const userDisplay = user ? (user.displayName || user.email || 'Unknown') : '\u2014'\r
|
|
1462
|
+
const userIdShort = user?.uid ? user.uid.slice(0, 8) + '...' : ''\r
|
|
1463
|
+
\r
|
|
1464
|
+
content.innerHTML = \`\r
|
|
1465
|
+
<div style="\r
|
|
1466
|
+
background: \${env.gradient};\r
|
|
1467
|
+
padding: 20px 24px;\r
|
|
1468
|
+
display: flex;\r
|
|
1469
|
+
justify-content: space-between;\r
|
|
1470
|
+
align-items: center;\r
|
|
1471
|
+
">\r
|
|
1472
|
+
<div style="display: flex; align-items: center; gap: 10px;">\r
|
|
1473
|
+
<span style="font-size: 24px;">\u{1FA90}</span>\r
|
|
1474
|
+
<div>\r
|
|
1475
|
+
<div style="font-size: 16px; font-weight: 700; letter-spacing: 0.02em;">Venus Sandbox</div>\r
|
|
1476
|
+
<div style="font-size: 11px; opacity: 0.85; margin-top: 2px;">\${env.subtitle}</div>\r
|
|
1477
|
+
</div>\r
|
|
1478
|
+
</div>\r
|
|
1479
|
+
<button id="close-modal" style="\r
|
|
1480
|
+
background: rgba(0, 0, 0, 0.2);\r
|
|
1481
|
+
border: none;\r
|
|
1482
|
+
color: white;\r
|
|
1483
|
+
cursor: pointer;\r
|
|
1484
|
+
font-size: 18px;\r
|
|
1485
|
+
width: 32px;\r
|
|
1486
|
+
height: 32px;\r
|
|
1487
|
+
border-radius: 8px;\r
|
|
1488
|
+
display: flex;\r
|
|
1489
|
+
align-items: center;\r
|
|
1490
|
+
justify-content: center;\r
|
|
1491
|
+
transition: background 0.2s;\r
|
|
1492
|
+
">×</button>\r
|
|
1493
|
+
</div>\r
|
|
1494
|
+
\r
|
|
1495
|
+
<div style="padding: 20px 24px; display: flex; flex-direction: column; gap: 16px;">\r
|
|
1496
|
+
\r
|
|
1497
|
+
<div style="\r
|
|
1498
|
+
display: flex;\r
|
|
1499
|
+
align-items: center;\r
|
|
1500
|
+
gap: 12px;\r
|
|
1501
|
+
padding: 14px 16px;\r
|
|
1502
|
+
background: \${env.badgeBg};\r
|
|
1503
|
+
border: 1px solid \${env.badge}33;\r
|
|
1504
|
+
border-radius: 10px;\r
|
|
1505
|
+
">\r
|
|
1506
|
+
<div style="\r
|
|
1507
|
+
width: 10px;\r
|
|
1508
|
+
height: 10px;\r
|
|
1509
|
+
border-radius: 50%;\r
|
|
1510
|
+
background: \${env.badge};\r
|
|
1511
|
+
box-shadow: 0 0 8px \${env.badge};\r
|
|
1512
|
+
"></div>\r
|
|
1513
|
+
<div style="flex: 1;">\r
|
|
1514
|
+
<div style="font-size: 14px; font-weight: 600; color: \${env.badge};">\${env.label}\${isLocal ? ' (Emulator)' : ''}</div>\r
|
|
1515
|
+
<div style="font-size: 11px; color: #9CA3AF; margin-top: 2px;">Game: \${gameId}</div>\r
|
|
1516
|
+
</div>\r
|
|
1517
|
+
</div>\r
|
|
1518
|
+
\r
|
|
1519
|
+
<div style="\r
|
|
1520
|
+
padding: 14px 16px;\r
|
|
1521
|
+
background: rgba(255, 255, 255, 0.03);\r
|
|
1522
|
+
border: 1px solid rgba(255, 255, 255, 0.06);\r
|
|
1523
|
+
border-radius: 10px;\r
|
|
1524
|
+
">\r
|
|
1525
|
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: \${isSignedIn ? '10px' : '0'};">\r
|
|
1526
|
+
<span style="\r
|
|
1527
|
+
display: inline-flex;\r
|
|
1528
|
+
align-items: center;\r
|
|
1529
|
+
justify-content: center;\r
|
|
1530
|
+
width: 20px;\r
|
|
1531
|
+
height: 20px;\r
|
|
1532
|
+
border-radius: 50%;\r
|
|
1533
|
+
background: \${isSignedIn ? 'rgba(16, 185, 129, 0.2)' : 'rgba(107, 114, 128, 0.2)'};\r
|
|
1534
|
+
color: \${statusColor};\r
|
|
1535
|
+
font-size: 12px;\r
|
|
1536
|
+
font-weight: 600;\r
|
|
1537
|
+
">\${statusIcon}</span>\r
|
|
1538
|
+
<span style="font-size: 13px; color: \${statusColor}; font-weight: 500;">\${statusText}</span>\r
|
|
1539
|
+
</div>\r
|
|
1540
|
+
\${isSignedIn ? \`\r
|
|
1541
|
+
<div style="\r
|
|
1542
|
+
display: flex;\r
|
|
1543
|
+
align-items: center;\r
|
|
1544
|
+
gap: 10px;\r
|
|
1545
|
+
padding-top: 10px;\r
|
|
1546
|
+
border-top: 1px solid rgba(255, 255, 255, 0.06);\r
|
|
1547
|
+
">\r
|
|
1548
|
+
<div style="\r
|
|
1549
|
+
width: 36px;\r
|
|
1550
|
+
height: 36px;\r
|
|
1551
|
+
border-radius: 50%;\r
|
|
1552
|
+
background: linear-gradient(135deg, #6366f1, #8b5cf6);\r
|
|
1553
|
+
display: flex;\r
|
|
1554
|
+
align-items: center;\r
|
|
1555
|
+
justify-content: center;\r
|
|
1556
|
+
font-size: 14px;\r
|
|
1557
|
+
font-weight: 600;\r
|
|
1558
|
+
color: white;\r
|
|
1559
|
+
">\${(userDisplay[0] || '?').toUpperCase()}</div>\r
|
|
1560
|
+
<div style="flex: 1; min-width: 0;">\r
|
|
1561
|
+
<div style="font-size: 13px; font-weight: 500; color: #F3F4F6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">\${userDisplay}</div>\r
|
|
1562
|
+
<div style="font-size: 11px; color: #6B7280; font-family: monospace;">\${userIdShort}</div>\r
|
|
1563
|
+
</div>\r
|
|
1564
|
+
</div>\r
|
|
1565
|
+
\` : \`\r
|
|
1566
|
+
<div style="font-size: 12px; color: #6B7280; margin-top: 8px; line-height: 1.5;">\r
|
|
1567
|
+
Sign in to use sandbox features. Your session persists across reloads.\r
|
|
1568
|
+
</div>\r
|
|
1569
|
+
\`}\r
|
|
1570
|
+
</div>\r
|
|
1571
|
+
\r
|
|
1572
|
+
<div style="\r
|
|
1573
|
+
padding: 12px 14px;\r
|
|
1574
|
+
background: rgba(99, 102, 241, 0.08);\r
|
|
1575
|
+
border: 1px solid rgba(99, 102, 241, 0.15);\r
|
|
1576
|
+
border-radius: 8px;\r
|
|
1577
|
+
font-size: 12px;\r
|
|
1578
|
+
color: #a5b4fc;\r
|
|
1579
|
+
line-height: 1.5;\r
|
|
1580
|
+
">\r
|
|
1581
|
+
<div style="font-weight: 600; margin-bottom: 4px; color: #c7d2fe;">\u{1F4A1} Change environment:</div>\r
|
|
1582
|
+
<code style="\r
|
|
1583
|
+
display: block;\r
|
|
1584
|
+
background: rgba(0, 0, 0, 0.3);\r
|
|
1585
|
+
padding: 8px 10px;\r
|
|
1586
|
+
border-radius: 5px;\r
|
|
1587
|
+
font-family: 'SF Mono', Monaco, monospace;\r
|
|
1588
|
+
font-size: 11px;\r
|
|
1589
|
+
color: #e0e7ff;\r
|
|
1590
|
+
margin-top: 6px;\r
|
|
1591
|
+
">venus set-env <local|dev|staging></code>\r
|
|
1592
|
+
</div>\r
|
|
1593
|
+
\r
|
|
1594
|
+
\${isLocal ? \`\r
|
|
1595
|
+
<div style="\r
|
|
1596
|
+
padding: 10px 14px;\r
|
|
1597
|
+
background: rgba(245, 158, 11, 0.08);\r
|
|
1598
|
+
border: 1px solid rgba(245, 158, 11, 0.15);\r
|
|
1599
|
+
border-radius: 8px;\r
|
|
1600
|
+
font-size: 11px;\r
|
|
1601
|
+
color: #fcd34d;\r
|
|
1602
|
+
line-height: 1.5;\r
|
|
1603
|
+
">\r
|
|
1604
|
+
<strong>Emulator mode:</strong> Profiles are auto-created on first sign-in.\r
|
|
1605
|
+
<a href="http://localhost:4000" target="_blank" style="color: #fbbf24; text-decoration: underline; margin-left: 4px;">Open Emulator UI \u2192</a>\r
|
|
1606
|
+
</div>\r
|
|
1607
|
+
\` : ''}\r
|
|
1608
|
+
\r
|
|
1609
|
+
\${isSignedIn ? \`\r
|
|
1610
|
+
<div style="\r
|
|
1611
|
+
padding: 12px 14px;\r
|
|
1612
|
+
background: rgba(255, 255, 255, 0.03);\r
|
|
1613
|
+
border: 1px solid rgba(255, 255, 255, 0.06);\r
|
|
1614
|
+
border-radius: 8px;\r
|
|
1615
|
+
">\r
|
|
1616
|
+
<div style="font-size: 11px; font-weight: 600; color: #9CA3AF; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.05em;">Storage Debug</div>\r
|
|
1617
|
+
<div style="display: flex; gap: 8px;">\r
|
|
1618
|
+
<button id="copy-app-storage" style="\r
|
|
1619
|
+
flex: 1;\r
|
|
1620
|
+
background: rgba(99, 102, 241, 0.1);\r
|
|
1621
|
+
color: #a5b4fc;\r
|
|
1622
|
+
border: 1px solid rgba(99, 102, 241, 0.2);\r
|
|
1623
|
+
border-radius: 6px;\r
|
|
1624
|
+
padding: 8px 12px;\r
|
|
1625
|
+
font-size: 12px;\r
|
|
1626
|
+
font-weight: 500;\r
|
|
1627
|
+
cursor: pointer;\r
|
|
1628
|
+
transition: background 0.2s;\r
|
|
1629
|
+
">\u{1F4CB} App Storage</button>\r
|
|
1630
|
+
<button id="copy-global-storage" style="\r
|
|
1631
|
+
flex: 1;\r
|
|
1632
|
+
background: rgba(99, 102, 241, 0.1);\r
|
|
1633
|
+
color: #a5b4fc;\r
|
|
1634
|
+
border: 1px solid rgba(99, 102, 241, 0.2);\r
|
|
1635
|
+
border-radius: 6px;\r
|
|
1636
|
+
padding: 8px 12px;\r
|
|
1637
|
+
font-size: 12px;\r
|
|
1638
|
+
font-weight: 500;\r
|
|
1639
|
+
cursor: pointer;\r
|
|
1640
|
+
transition: background 0.2s;\r
|
|
1641
|
+
">\u{1F4CB} Global Storage</button>\r
|
|
1642
|
+
</div>\r
|
|
1643
|
+
</div>\r
|
|
1644
|
+
\` : ''}\r
|
|
1645
|
+
\r
|
|
1646
|
+
<div style="display: flex; gap: 10px; margin-top: 4px;">\r
|
|
1647
|
+
\${!isSignedIn ? \`\r
|
|
1648
|
+
<button id="sign-in-google" style="\r
|
|
1649
|
+
flex: 1;\r
|
|
1650
|
+
background: \${env.gradient};\r
|
|
1651
|
+
color: white;\r
|
|
1652
|
+
border: none;\r
|
|
1653
|
+
border-radius: 8px;\r
|
|
1654
|
+
padding: 12px 16px;\r
|
|
1655
|
+
font-size: 14px;\r
|
|
1656
|
+
font-weight: 600;\r
|
|
1657
|
+
cursor: pointer;\r
|
|
1658
|
+
transition: opacity 0.2s, transform 0.1s;\r
|
|
1659
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\r
|
|
1660
|
+
">Sign In</button>\r
|
|
1661
|
+
\` : \`\r
|
|
1662
|
+
<button id="sign-out" style="\r
|
|
1663
|
+
flex: 1;\r
|
|
1664
|
+
background: rgba(239, 68, 68, 0.1);\r
|
|
1665
|
+
color: #f87171;\r
|
|
1666
|
+
border: 1px solid rgba(239, 68, 68, 0.2);\r
|
|
1667
|
+
border-radius: 8px;\r
|
|
1668
|
+
padding: 12px 16px;\r
|
|
1669
|
+
font-size: 14px;\r
|
|
1670
|
+
font-weight: 600;\r
|
|
1671
|
+
cursor: pointer;\r
|
|
1672
|
+
transition: background 0.2s;\r
|
|
1673
|
+
">Sign Out</button>\r
|
|
1674
|
+
\`}\r
|
|
1675
|
+
<button id="reload-page" style="\r
|
|
1676
|
+
background: rgba(255, 255, 255, 0.05);\r
|
|
1677
|
+
color: #9CA3AF;\r
|
|
1678
|
+
border: 1px solid rgba(255, 255, 255, 0.1);\r
|
|
1679
|
+
border-radius: 8px;\r
|
|
1680
|
+
padding: 12px 16px;\r
|
|
1681
|
+
font-size: 14px;\r
|
|
1682
|
+
font-weight: 600;\r
|
|
1683
|
+
cursor: pointer;\r
|
|
1684
|
+
transition: background 0.2s;\r
|
|
1685
|
+
">\u21BB Reload</button>\r
|
|
1686
|
+
</div>\r
|
|
1687
|
+
</div>\r
|
|
1688
|
+
\`\r
|
|
1689
|
+
\r
|
|
1690
|
+
overlay.appendChild(content)\r
|
|
1691
|
+
document.body.appendChild(overlay)\r
|
|
1692
|
+
\r
|
|
1693
|
+
// Event handlers\r
|
|
1694
|
+
const closeModal = () => overlay.remove()\r
|
|
1695
|
+
\r
|
|
1696
|
+
document.getElementById('close-modal').addEventListener('click', closeModal)\r
|
|
1697
|
+
overlay.addEventListener('click', (e) => {\r
|
|
1698
|
+
if (e.target === overlay) closeModal()\r
|
|
1699
|
+
})\r
|
|
1700
|
+
\r
|
|
1701
|
+
document.getElementById('reload-page')?.addEventListener('click', () => {\r
|
|
1702
|
+
window.location.reload()\r
|
|
1703
|
+
})\r
|
|
1704
|
+
\r
|
|
1705
|
+
const signInBtn = document.getElementById('sign-in-google')\r
|
|
1706
|
+
if (signInBtn) {\r
|
|
1707
|
+
const originalGradient = env.gradient\r
|
|
1708
|
+
signInBtn.addEventListener('click', async () => {\r
|
|
1709
|
+
try {\r
|
|
1710
|
+
signInBtn.textContent = 'Signing in...'\r
|
|
1711
|
+
signInBtn.disabled = true\r
|
|
1712
|
+
\r
|
|
1713
|
+
if (window.__VENUS_SANDBOX_SIGN_IN__) {\r
|
|
1714
|
+
await window.__VENUS_SANDBOX_SIGN_IN__()\r
|
|
1715
|
+
// Sign-in successful - show success and reload\r
|
|
1716
|
+
signInBtn.textContent = '\u2713 Success!'\r
|
|
1717
|
+
signInBtn.style.background = 'linear-gradient(120deg, #10b981, #059669)'\r
|
|
1718
|
+
setTimeout(() => {\r
|
|
1719
|
+
closeModal()\r
|
|
1720
|
+
window.location.reload()\r
|
|
1721
|
+
}, 500)\r
|
|
1722
|
+
} else {\r
|
|
1723
|
+
throw new Error('Sign-in function not available. Is the SDK initialized?')\r
|
|
1724
|
+
}\r
|
|
1725
|
+
} catch (error) {\r
|
|
1726
|
+
console.error('[Venus Sandbox] Sign-in failed:', error)\r
|
|
1727
|
+
signInBtn.textContent = 'Sign-in failed'\r
|
|
1728
|
+
signInBtn.style.background = '#ef4444'\r
|
|
1729
|
+
setTimeout(() => {\r
|
|
1730
|
+
signInBtn.textContent = 'Sign In'\r
|
|
1731
|
+
signInBtn.style.background = originalGradient\r
|
|
1732
|
+
signInBtn.disabled = false\r
|
|
1733
|
+
}, 2000)\r
|
|
1734
|
+
}\r
|
|
1735
|
+
})\r
|
|
1736
|
+
}\r
|
|
1737
|
+
\r
|
|
1738
|
+
const signOutBtn = document.getElementById('sign-out')\r
|
|
1739
|
+
if (signOutBtn) {\r
|
|
1740
|
+
signOutBtn.addEventListener('click', async () => {\r
|
|
1741
|
+
try {\r
|
|
1742
|
+
signOutBtn.textContent = 'Signing out...'\r
|
|
1743
|
+
signOutBtn.disabled = true\r
|
|
1744
|
+
\r
|
|
1745
|
+
if (window.__VENUS_SANDBOX_SIGN_OUT__) {\r
|
|
1746
|
+
await window.__VENUS_SANDBOX_SIGN_OUT__()\r
|
|
1747
|
+
signOutBtn.textContent = '\u2713 Signed out'\r
|
|
1748
|
+
setTimeout(() => {\r
|
|
1749
|
+
closeModal()\r
|
|
1750
|
+
window.location.reload()\r
|
|
1751
|
+
}, 500)\r
|
|
1752
|
+
}\r
|
|
1753
|
+
} catch (error) {\r
|
|
1754
|
+
console.error('[Venus Sandbox] Sign-out failed:', error)\r
|
|
1755
|
+
signOutBtn.textContent = 'Failed'\r
|
|
1756
|
+
setTimeout(() => {\r
|
|
1757
|
+
signOutBtn.textContent = 'Sign out'\r
|
|
1758
|
+
signOutBtn.disabled = false\r
|
|
1759
|
+
}, 2000)\r
|
|
1760
|
+
}\r
|
|
1761
|
+
})\r
|
|
1762
|
+
}\r
|
|
1763
|
+
\r
|
|
1764
|
+
// Storage debug copy handlers\r
|
|
1765
|
+
const copyAppStorageBtn = document.getElementById('copy-app-storage')\r
|
|
1766
|
+
if (copyAppStorageBtn) {\r
|
|
1767
|
+
copyAppStorageBtn.addEventListener('click', async () => {\r
|
|
1768
|
+
await copyStorageToClipboard(copyAppStorageBtn, 'appStorage')\r
|
|
1769
|
+
})\r
|
|
1770
|
+
}\r
|
|
1771
|
+
\r
|
|
1772
|
+
const copyGlobalStorageBtn = document.getElementById('copy-global-storage')\r
|
|
1773
|
+
if (copyGlobalStorageBtn) {\r
|
|
1774
|
+
copyGlobalStorageBtn.addEventListener('click', async () => {\r
|
|
1775
|
+
await copyStorageToClipboard(copyGlobalStorageBtn, 'globalStorage')\r
|
|
1776
|
+
})\r
|
|
1777
|
+
}\r
|
|
1778
|
+
}\r
|
|
1779
|
+
\r
|
|
1780
|
+
async function copyStorageToClipboard(btn, storageType) {\r
|
|
1781
|
+
const originalText = btn.textContent\r
|
|
1782
|
+
try {\r
|
|
1783
|
+
btn.textContent = 'Loading...'\r
|
|
1784
|
+
btn.disabled = true\r
|
|
1785
|
+
\r
|
|
1786
|
+
// Call the SDK directly - VenusAPI is available globally\r
|
|
1787
|
+
const api = window.VenusAPI\r
|
|
1788
|
+
if (!api) {\r
|
|
1789
|
+
throw new Error('VenusAPI not available')\r
|
|
1790
|
+
}\r
|
|
1791
|
+
\r
|
|
1792
|
+
const storage = storageType === 'appStorage' ? api.appStorage : api.globalStorage\r
|
|
1793
|
+
if (!storage?.getAllData) {\r
|
|
1794
|
+
throw new Error(\`\${storageType} not available or getAllData not supported\`)\r
|
|
1795
|
+
}\r
|
|
1796
|
+
\r
|
|
1797
|
+
const data = await storage.getAllData()\r
|
|
1798
|
+
const json = JSON.stringify(data, null, 2)\r
|
|
1799
|
+
\r
|
|
1800
|
+
await navigator.clipboard.writeText(json)\r
|
|
1801
|
+
\r
|
|
1802
|
+
const count = Object.keys(data).length\r
|
|
1803
|
+
btn.textContent = \`\u2713 Copied (\${count} keys)\`\r
|
|
1804
|
+
btn.style.background = 'rgba(16, 185, 129, 0.2)'\r
|
|
1805
|
+
btn.style.color = '#34d399'\r
|
|
1806
|
+
btn.style.borderColor = 'rgba(16, 185, 129, 0.3)'\r
|
|
1807
|
+
\r
|
|
1808
|
+
setTimeout(() => {\r
|
|
1809
|
+
btn.textContent = originalText\r
|
|
1810
|
+
btn.style.background = 'rgba(99, 102, 241, 0.1)'\r
|
|
1811
|
+
btn.style.color = '#a5b4fc'\r
|
|
1812
|
+
btn.style.borderColor = 'rgba(99, 102, 241, 0.2)'\r
|
|
1813
|
+
btn.disabled = false\r
|
|
1814
|
+
}, 2000)\r
|
|
1815
|
+
} catch (error) {\r
|
|
1816
|
+
console.error('[Venus Sandbox] Failed to copy storage:', error)\r
|
|
1817
|
+
btn.textContent = 'Failed'\r
|
|
1818
|
+
btn.style.background = 'rgba(239, 68, 68, 0.2)'\r
|
|
1819
|
+
btn.style.color = '#f87171'\r
|
|
1820
|
+
\r
|
|
1821
|
+
setTimeout(() => {\r
|
|
1822
|
+
btn.textContent = originalText\r
|
|
1823
|
+
btn.style.background = 'rgba(99, 102, 241, 0.1)'\r
|
|
1824
|
+
btn.style.color = '#a5b4fc'\r
|
|
1825
|
+
btn.style.borderColor = 'rgba(99, 102, 241, 0.2)'\r
|
|
1826
|
+
btn.disabled = false\r
|
|
1827
|
+
}, 2000)\r
|
|
1828
|
+
}\r
|
|
1829
|
+
}\r
|
|
1830
|
+
`;
|
|
1831
|
+
|
|
1832
|
+
// src/vite/sandboxToolbar.ts
|
|
1833
|
+
function createSandboxToolbarTags() {
|
|
1834
|
+
const styleTag = {
|
|
1835
|
+
tag: "style",
|
|
1836
|
+
attrs: { id: "venus-sandbox-style" },
|
|
1837
|
+
children: sandboxToolbarStyle_default,
|
|
1838
|
+
injectTo: "head"
|
|
1839
|
+
};
|
|
1840
|
+
const scriptTag = {
|
|
1841
|
+
tag: "script",
|
|
1842
|
+
attrs: {
|
|
1843
|
+
type: "module",
|
|
1844
|
+
id: "venus-sandbox-script",
|
|
1845
|
+
"data-venus-ignore": "true"
|
|
1846
|
+
},
|
|
1847
|
+
children: sandboxToolbarScript_default,
|
|
1848
|
+
injectTo: "body"
|
|
1849
|
+
};
|
|
1850
|
+
return [styleTag, scriptTag];
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
// src/firebase/firebaseConfigs.ts
|
|
1854
|
+
var FIREBASE_CONFIGS = {
|
|
1855
|
+
// Local mode uses dev Firebase credentials (auth emulator doesn't support Google OAuth)
|
|
1856
|
+
local: {
|
|
1857
|
+
apiKey: "AIzaSyCSYBs5QrFUeaE8QC2Jfp6834enCzbCJqQ",
|
|
1858
|
+
authDomain: "dev-venus-app.firebaseapp.com",
|
|
1859
|
+
projectId: "dev-venus-app",
|
|
1860
|
+
storageBucket: "dev-venus-app.appspot.com"
|
|
1861
|
+
},
|
|
1862
|
+
dev: {
|
|
1863
|
+
apiKey: "AIzaSyCSYBs5QrFUeaE8QC2Jfp6834enCzbCJqQ",
|
|
1864
|
+
authDomain: "dev-venus-app.firebaseapp.com",
|
|
1865
|
+
projectId: "dev-venus-app",
|
|
1866
|
+
storageBucket: "dev-venus-app.appspot.com"
|
|
1867
|
+
},
|
|
1868
|
+
staging: {
|
|
1869
|
+
apiKey: "AIzaSyCfv4HGtNjStUsN0IL-TUNLYSNSZ3KhXN4",
|
|
1870
|
+
authDomain: "staging-venus-app.firebaseapp.com",
|
|
1871
|
+
projectId: "staging-venus-app",
|
|
1872
|
+
storageBucket: "staging-venus-app.appspot.com"
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
var FUNCTIONS_REGIONS = {
|
|
1876
|
+
local: "us-central1",
|
|
1877
|
+
dev: "us-central1",
|
|
1878
|
+
staging: "us-central1"
|
|
1879
|
+
};
|
|
1880
|
+
var DEFAULT_EMULATOR_HOSTS = {
|
|
1881
|
+
functions: "localhost:5001",
|
|
1882
|
+
firestore: "localhost:8080",
|
|
1883
|
+
auth: "localhost:9099"
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
// src/vite/venusSandboxPlugin.ts
|
|
1887
|
+
var REQUIRED_FIREBASE_VERSION = "^11.0.0";
|
|
1888
|
+
var GAME_CONFIG_FILENAME = "game.config.json";
|
|
1889
|
+
var VENUS_CONFIG_PATH = join(homedir(), ".venus", "config.json");
|
|
1890
|
+
function venusSandboxPlugin(options = {}) {
|
|
1891
|
+
let projectRoot = process.cwd();
|
|
1892
|
+
let isServeCommand = false;
|
|
1893
|
+
let warningLogged = false;
|
|
1894
|
+
let firebaseMissing = false;
|
|
1895
|
+
const logWarning = (message) => {
|
|
1896
|
+
if (!warningLogged) {
|
|
1897
|
+
warningLogged = true;
|
|
1898
|
+
console.warn(`[Venus Sandbox] ${message}`);
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
return {
|
|
1902
|
+
name: "venus-sandbox",
|
|
1903
|
+
apply: "serve",
|
|
1904
|
+
enforce: "pre",
|
|
1905
|
+
// Run before other plugins to set up optimizeDeps exclusions
|
|
1906
|
+
config(config, env) {
|
|
1907
|
+
projectRoot = resolveProjectRoot(config);
|
|
1908
|
+
isServeCommand = env.command === "serve";
|
|
1909
|
+
if (isServeCommand && !options.disabled) {
|
|
1910
|
+
const hasFirebase = checkFirebaseDependency(projectRoot);
|
|
1911
|
+
if (!hasFirebase) {
|
|
1912
|
+
firebaseMissing = true;
|
|
1913
|
+
logFirebaseMissing();
|
|
1914
|
+
return {
|
|
1915
|
+
...config,
|
|
1916
|
+
optimizeDeps: {
|
|
1917
|
+
...config?.optimizeDeps,
|
|
1918
|
+
exclude: [
|
|
1919
|
+
...config?.optimizeDeps?.exclude || [],
|
|
1920
|
+
"@series-inc/venus-sdk",
|
|
1921
|
+
"firebase",
|
|
1922
|
+
"firebase/app",
|
|
1923
|
+
"firebase/auth",
|
|
1924
|
+
"firebase/firestore"
|
|
1925
|
+
]
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1928
|
+
} else {
|
|
1929
|
+
let sandboxConfig = null;
|
|
1930
|
+
try {
|
|
1931
|
+
sandboxConfig = buildSandboxConfig(projectRoot, options);
|
|
1932
|
+
} catch {
|
|
1933
|
+
}
|
|
1934
|
+
const proxyConfig = buildProxyConfig(sandboxConfig);
|
|
1935
|
+
return {
|
|
1936
|
+
...config,
|
|
1937
|
+
define: {
|
|
1938
|
+
...config?.define,
|
|
1939
|
+
"import.meta.env.VITE_APP_MODE": JSON.stringify("live")
|
|
1940
|
+
},
|
|
1941
|
+
optimizeDeps: {
|
|
1942
|
+
...config?.optimizeDeps,
|
|
1943
|
+
include: [
|
|
1944
|
+
...config?.optimizeDeps?.include || [],
|
|
1945
|
+
"firebase/app",
|
|
1946
|
+
"firebase/auth",
|
|
1947
|
+
"firebase/firestore"
|
|
1948
|
+
]
|
|
1949
|
+
},
|
|
1950
|
+
server: {
|
|
1951
|
+
...config?.server,
|
|
1952
|
+
proxy: {
|
|
1953
|
+
...config?.server?.proxy,
|
|
1954
|
+
...proxyConfig
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return config;
|
|
1961
|
+
},
|
|
1962
|
+
async transformIndexHtml(html) {
|
|
1963
|
+
if (!isServeCommand || options.disabled) {
|
|
1964
|
+
return html;
|
|
1965
|
+
}
|
|
1966
|
+
if (firebaseMissing) {
|
|
1967
|
+
const errorMessage = `Firebase is not installed.
|
|
1968
|
+
|
|
1969
|
+
Add "firebase": "${REQUIRED_FIREBASE_VERSION}" to your package.json devDependencies, then run npm install.
|
|
1970
|
+
|
|
1971
|
+
Firebase is only needed for sandbox mode (local development) and is excluded from production builds.`;
|
|
1972
|
+
const errorScript = buildErrorOverlayScript(errorMessage, "local");
|
|
1973
|
+
return {
|
|
1974
|
+
html,
|
|
1975
|
+
tags: [
|
|
1976
|
+
{
|
|
1977
|
+
tag: "script",
|
|
1978
|
+
attrs: { type: "module" },
|
|
1979
|
+
children: errorScript,
|
|
1980
|
+
injectTo: "body"
|
|
1981
|
+
}
|
|
1982
|
+
]
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
try {
|
|
1986
|
+
const config = buildSandboxConfig(projectRoot, options);
|
|
1987
|
+
if (!config) {
|
|
1988
|
+
return html;
|
|
1989
|
+
}
|
|
1990
|
+
const injectedHtml = injectVenusShell(html);
|
|
1991
|
+
const configScript = `window.__VENUS_SANDBOX__ = ${JSON.stringify(config)};`;
|
|
1992
|
+
console.log(`[Venus Sandbox] Enabled for ${config.target} environment (gameId: ${config.gameId})`);
|
|
1993
|
+
return {
|
|
1994
|
+
html: injectedHtml,
|
|
1995
|
+
tags: [
|
|
1996
|
+
{
|
|
1997
|
+
tag: "script",
|
|
1998
|
+
attrs: { type: "module" },
|
|
1999
|
+
children: configScript,
|
|
2000
|
+
injectTo: "head"
|
|
2001
|
+
},
|
|
2002
|
+
...createSandboxToolbarTags()
|
|
2003
|
+
]
|
|
2004
|
+
};
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2007
|
+
logWarning(message);
|
|
2008
|
+
const target = options.target ?? "local";
|
|
2009
|
+
const errorScript = buildErrorOverlayScript(message, target);
|
|
2010
|
+
return {
|
|
2011
|
+
html,
|
|
2012
|
+
tags: [
|
|
2013
|
+
{
|
|
2014
|
+
tag: "script",
|
|
2015
|
+
attrs: { type: "module" },
|
|
2016
|
+
children: errorScript,
|
|
2017
|
+
injectTo: "body"
|
|
2018
|
+
}
|
|
2019
|
+
]
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
function buildSandboxConfig(projectRoot, options) {
|
|
2026
|
+
const gameConfigPath = findGameConfigPath(projectRoot);
|
|
2027
|
+
if (!gameConfigPath) {
|
|
2028
|
+
throw new Error(
|
|
2029
|
+
`Unable to locate ${GAME_CONFIG_FILENAME} starting from ${projectRoot}. Run "venus configure-game" to generate one.`
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
const gameConfig = readGameConfig(gameConfigPath);
|
|
2033
|
+
const gameId = gameConfig.gameId?.trim();
|
|
2034
|
+
if (!gameId) {
|
|
2035
|
+
throw new Error(
|
|
2036
|
+
`Missing "gameId" inside ${gameConfigPath}. Run "venus configure-game" to regenerate it.`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
const cliConfig = readVenusCliConfig();
|
|
2040
|
+
const target = options.target ?? coerceTarget(cliConfig?.activeEnv);
|
|
2041
|
+
const envConfig = cliConfig?.sandbox?.[target];
|
|
2042
|
+
if (envConfig?.enabled === false) {
|
|
2043
|
+
return null;
|
|
2044
|
+
}
|
|
2045
|
+
const firebaseConfig = FIREBASE_CONFIGS[target];
|
|
2046
|
+
const config = {
|
|
2047
|
+
enabled: true,
|
|
2048
|
+
target,
|
|
2049
|
+
gameId,
|
|
2050
|
+
firebaseConfig,
|
|
2051
|
+
functionsRegion: FUNCTIONS_REGIONS[target],
|
|
2052
|
+
pollingInterval: envConfig?.pollingInterval ?? 2e3,
|
|
2053
|
+
rpcMaxRetries: envConfig?.rpcMaxRetries ?? 0,
|
|
2054
|
+
rpcRetryDelayMs: envConfig?.rpcRetryDelayMs ?? 1e3
|
|
2055
|
+
};
|
|
2056
|
+
if (target === "local") {
|
|
2057
|
+
config.functionsEmulatorHost = DEFAULT_EMULATOR_HOSTS.functions;
|
|
2058
|
+
config.firestoreEmulatorHost = DEFAULT_EMULATOR_HOSTS.firestore;
|
|
2059
|
+
config.authEmulatorHost = DEFAULT_EMULATOR_HOSTS.auth;
|
|
2060
|
+
}
|
|
2061
|
+
if (envConfig?.backendUrl) {
|
|
2062
|
+
config.backendUrl = envConfig.backendUrl;
|
|
2063
|
+
}
|
|
2064
|
+
return config;
|
|
2065
|
+
}
|
|
2066
|
+
function readVenusCliConfig() {
|
|
2067
|
+
try {
|
|
2068
|
+
if (!existsSync(VENUS_CONFIG_PATH)) {
|
|
2069
|
+
return null;
|
|
2070
|
+
}
|
|
2071
|
+
const raw = readFileSync(VENUS_CONFIG_PATH, "utf-8");
|
|
2072
|
+
return JSON.parse(raw);
|
|
2073
|
+
} catch {
|
|
2074
|
+
return null;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
function readGameConfig(pathname) {
|
|
2078
|
+
try {
|
|
2079
|
+
const raw = readFileSync(pathname, "utf-8");
|
|
2080
|
+
return JSON.parse(raw);
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2083
|
+
throw new Error(`Failed to parse ${pathname}: ${message}`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
function findGameConfigPath(startDir) {
|
|
2087
|
+
let current = resolve(startDir);
|
|
2088
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2089
|
+
while (true) {
|
|
2090
|
+
const candidate = join(current, GAME_CONFIG_FILENAME);
|
|
2091
|
+
if (existsSync(candidate)) {
|
|
2092
|
+
return candidate;
|
|
2093
|
+
}
|
|
2094
|
+
if (visited.has(current)) {
|
|
2095
|
+
break;
|
|
2096
|
+
}
|
|
2097
|
+
visited.add(current);
|
|
2098
|
+
const parent = dirname(current);
|
|
2099
|
+
if (parent === current) {
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
current = parent;
|
|
2103
|
+
}
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
function resolveProjectRoot(config) {
|
|
2107
|
+
if (config?.root && config.root.length > 0) {
|
|
2108
|
+
return resolve(config.root);
|
|
2109
|
+
}
|
|
2110
|
+
return process.cwd();
|
|
2111
|
+
}
|
|
2112
|
+
function coerceTarget(value) {
|
|
2113
|
+
if (value === "dev" || value === "staging") {
|
|
2114
|
+
return value;
|
|
2115
|
+
}
|
|
2116
|
+
return "local";
|
|
2117
|
+
}
|
|
2118
|
+
function injectVenusShell(html) {
|
|
2119
|
+
const match = html.match(/<body([^>]*)>([\s\S]*)<\/body>/i);
|
|
2120
|
+
if (!match) {
|
|
2121
|
+
return html;
|
|
2122
|
+
}
|
|
2123
|
+
const [, attrs, contents] = match;
|
|
2124
|
+
const serialized = JSON.stringify(contents).replace(/<\/script>/gi, "<\\/script>");
|
|
2125
|
+
const shell = `
|
|
2126
|
+
<body${attrs}>
|
|
2127
|
+
<div id="venus-toolbar-root" aria-live="polite"></div>
|
|
2128
|
+
<div id="venus-app-root" data-venus-app-root style="display:contents;">
|
|
2129
|
+
${contents}
|
|
2130
|
+
</div>
|
|
2131
|
+
<noscript id="venus-app-placeholder">
|
|
2132
|
+
Venus sandbox requires JavaScript enabled.
|
|
2133
|
+
</noscript>
|
|
2134
|
+
<script id="venus-app-capture">
|
|
2135
|
+
window.__VENUS_APP_HTML = ${serialized};
|
|
2136
|
+
</script>
|
|
2137
|
+
</body>
|
|
2138
|
+
`;
|
|
2139
|
+
return html.replace(/<body([\s\S]*)<\/body>/i, shell);
|
|
2140
|
+
}
|
|
2141
|
+
function checkFirebaseDependency(projectRoot) {
|
|
2142
|
+
try {
|
|
2143
|
+
const require2 = createRequire(join(projectRoot, "package.json"));
|
|
2144
|
+
require2.resolve("firebase/app");
|
|
2145
|
+
return true;
|
|
2146
|
+
} catch {
|
|
2147
|
+
return false;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
function logFirebaseMissing() {
|
|
2151
|
+
console.error(`
|
|
2152
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
2153
|
+
\u2551 \u2551
|
|
2154
|
+
\u2551 IMPORTANT: The Venus sandbox plugin requires Firebase. \u2551
|
|
2155
|
+
\u2551 \u2551
|
|
2156
|
+
\u2551 Add the following to your package.json devDependencies: \u2551
|
|
2157
|
+
\u2551 \u2551
|
|
2158
|
+
\u2551 "firebase": "${REQUIRED_FIREBASE_VERSION}" \u2551
|
|
2159
|
+
\u2551 \u2551
|
|
2160
|
+
\u2551 Then run: \u2551
|
|
2161
|
+
\u2551 \u2551
|
|
2162
|
+
\u2551 npm install \u2551
|
|
2163
|
+
\u2551 \u2551
|
|
2164
|
+
\u2551 WHY devDependencies? \u2551
|
|
2165
|
+
\u2551 Firebase is only used during local development (sandbox mode). It is \u2551
|
|
2166
|
+
\u2551 dynamically imported and excluded from production builds, so it should \u2551
|
|
2167
|
+
\u2551 be a devDependency to keep production bundles small. \u2551
|
|
2168
|
+
\u2551 \u2551
|
|
2169
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
2170
|
+
`);
|
|
2171
|
+
}
|
|
2172
|
+
function buildErrorOverlayScript(message, target) {
|
|
2173
|
+
const escapedMessage = message.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
|
|
2174
|
+
return `
|
|
2175
|
+
(function() {
|
|
2176
|
+
var overlay = document.createElement('div');
|
|
2177
|
+
overlay.id = 'venus-sandbox-error';
|
|
2178
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:999999;background:rgba(0,0,0,0.92);color:#fff;font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;padding:20px;';
|
|
2179
|
+
|
|
2180
|
+
var content = document.createElement('div');
|
|
2181
|
+
content.style.cssText = 'max-width:600px;text-align:center;';
|
|
2182
|
+
|
|
2183
|
+
var icon = document.createElement('div');
|
|
2184
|
+
icon.style.cssText = 'font-size:48px;margin-bottom:16px;';
|
|
2185
|
+
icon.textContent = '\u26A0\uFE0F';
|
|
2186
|
+
|
|
2187
|
+
var title = document.createElement('h1');
|
|
2188
|
+
title.style.cssText = 'font-size:24px;font-weight:600;margin:0 0 16px;color:#ff6b6b;';
|
|
2189
|
+
title.textContent = 'Venus Sandbox Error';
|
|
2190
|
+
|
|
2191
|
+
var msg = document.createElement('p');
|
|
2192
|
+
msg.style.cssText = 'font-size:14px;line-height:1.6;margin:0 0 24px;color:#ccc;';
|
|
2193
|
+
msg.textContent = '${escapedMessage}';
|
|
2194
|
+
|
|
2195
|
+
var hint = document.createElement('div');
|
|
2196
|
+
hint.style.cssText = 'background:rgba(255,255,255,0.1);border-radius:8px;padding:16px;font-size:13px;color:#aaa;';
|
|
2197
|
+
hint.innerHTML = '<strong style="color:#fff;">Environment:</strong> ${target}<br>Check your game.config.json and ~/.venus/config.json';
|
|
2198
|
+
|
|
2199
|
+
content.appendChild(icon);
|
|
2200
|
+
content.appendChild(title);
|
|
2201
|
+
content.appendChild(msg);
|
|
2202
|
+
content.appendChild(hint);
|
|
2203
|
+
overlay.appendChild(content);
|
|
2204
|
+
|
|
2205
|
+
if (document.body) {
|
|
2206
|
+
document.body.appendChild(overlay);
|
|
2207
|
+
} else {
|
|
2208
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
2209
|
+
document.body.appendChild(overlay);
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
})();
|
|
2213
|
+
`;
|
|
2214
|
+
}
|
|
2215
|
+
var VENUS_API_PROXY_PATH = "/__venusapi";
|
|
2216
|
+
function buildProxyConfig(config) {
|
|
2217
|
+
if (!config || config.target === "local") {
|
|
2218
|
+
return {};
|
|
2219
|
+
}
|
|
2220
|
+
const projectId = config.firebaseConfig?.projectId;
|
|
2221
|
+
const region = config.functionsRegion || "us-central1";
|
|
2222
|
+
if (!projectId) {
|
|
2223
|
+
return {};
|
|
2224
|
+
}
|
|
2225
|
+
const targetUrl = `https://${region}-${projectId}.cloudfunctions.net`;
|
|
2226
|
+
console.log(`[Venus Sandbox] Setting up CORS proxy: ${VENUS_API_PROXY_PATH}/* -> ${targetUrl}/*`);
|
|
2227
|
+
return {
|
|
2228
|
+
[VENUS_API_PROXY_PATH]: {
|
|
2229
|
+
target: targetUrl,
|
|
2230
|
+
changeOrigin: true,
|
|
2231
|
+
rewrite: (path3) => path3.replace(new RegExp(`^${VENUS_API_PROXY_PATH}`), "")
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
export { cdnPlugin, venusLibrariesPlugin, venusSandboxPlugin };
|
|
1075
2237
|
//# sourceMappingURL=index.js.map
|
|
1076
2238
|
//# sourceMappingURL=index.js.map
|