feat(editor): два слота code_js/code_lua в скриптах
Раньше при смене языка код оставался как есть → подсветка кричит ошибками, юзер пишет JS в Lua-режиме и наоборот. Сейчас: - Скрипт хранит code_js и code_lua отдельно (плюс активный code). - При переключении JS↔Lua: текущий code сохраняется в слот ТЕКУЩЕГО языка, достаётся слот ЦЕЛЕВОГО языка. Если слот пустой — шаблон. - При обычном save (печатает юзер) код зеркалится в слот активного языка чтобы не потерять при swap. - Никаких модалок: переключение мгновенное, ничего не пропадает. Сценарии: - Новый проект: js-шаблон в code_js. Клик Lua → подставляется lua-шаблон, code_js сохранён. - Юзер написал JS-код, переключился на Lua: JS улетел в code_js, показывается пустой Lua-шаблон. Клик обратно JS → код возвращается. - Старые проекты (без code_js/code_lua): первое переключение засеет слот текущего языка из code.
This commit is contained in:
parent
3f9f7cd6c7
commit
ea4d759a7c
@ -30,7 +30,7 @@ import BillboardEditorModal from './BillboardEditorModal';
|
||||
import TerrainGenPanel from './TerrainGenPanel';
|
||||
import ScriptConsole from './ScriptConsole';
|
||||
import SceneTabs from './SceneTabs';
|
||||
import ScriptEditor from './ScriptEditor';
|
||||
import ScriptEditor, { LUA_TEMPLATE_PART, LUA_TEMPLATE_GLOBAL, JS_TEMPLATE_GLOBAL } from './ScriptEditor';
|
||||
import GameHud from './GameHud';
|
||||
import MinimapOverlay from './MinimapOverlay';
|
||||
import GuiOverlay from './GuiOverlay';
|
||||
@ -3327,13 +3327,40 @@ const KubikonEditor = () => {
|
||||
language={sc.language || 'js'}
|
||||
flushRef={scriptEditorFlushRef}
|
||||
isSoloRunning={soloScriptId === sc.id}
|
||||
onLanguageChange={(lang, newCode) => {
|
||||
sceneRef.current?.upsertScript(sc.id, newCode, undefined, undefined, lang);
|
||||
onLanguageChange={(lang, currentEditorCode) => {
|
||||
// Два слота: code_js и code_lua живут в самом скрипте.
|
||||
// При переключении: сохраняем текущий код в слот ТЕКУЩЕГО
|
||||
// языка, достаём слот ЦЕЛЕВОГО языка (или шаблон если пусто).
|
||||
const fromLang = sc.language === 'lua' ? 'lua' : 'js';
|
||||
if (fromLang === lang) return;
|
||||
const fromSlotKey = fromLang === 'lua' ? 'code_lua' : 'code_js';
|
||||
const toSlotKey = lang === 'lua' ? 'code_lua' : 'code_js';
|
||||
// Сохраняем текущий редактируемый код в слот текущего языка
|
||||
const savedSlots = {
|
||||
...(sc.code_js !== undefined ? { code_js: sc.code_js } : {}),
|
||||
...(sc.code_lua !== undefined ? { code_lua: sc.code_lua } : {}),
|
||||
[fromSlotKey]: currentEditorCode || '',
|
||||
};
|
||||
// Достаём слот целевого языка или подставляем шаблон
|
||||
let nextCode = savedSlots[toSlotKey];
|
||||
if (nextCode === undefined || nextCode === '') {
|
||||
nextCode = lang === 'lua'
|
||||
? (sc.target ? LUA_TEMPLATE_PART : LUA_TEMPLATE_GLOBAL)
|
||||
: JS_TEMPLATE_GLOBAL;
|
||||
}
|
||||
sceneRef.current?.upsertScript(
|
||||
sc.id, nextCode, undefined, undefined, lang, savedSlots
|
||||
);
|
||||
setScriptsList(sceneRef.current?.getScripts?.() || []);
|
||||
markDirty();
|
||||
}}
|
||||
onSave={(code) => {
|
||||
sceneRef.current?.upsertScript(sc.id, code, sc.target);
|
||||
// Зеркалим в слот активного языка чтобы при swap не потерять.
|
||||
const slotKey = (sc.language === 'lua') ? 'code_lua' : 'code_js';
|
||||
sceneRef.current?.upsertScript(
|
||||
sc.id, code, sc.target, undefined, undefined,
|
||||
{ [slotKey]: code }
|
||||
);
|
||||
setScriptsList(sceneRef.current?.getScripts?.() || []);
|
||||
markDirty();
|
||||
}}
|
||||
|
||||
@ -38,21 +38,21 @@ import ConfirmModal from './ConfirmModal';
|
||||
// командой `python _build_bundle.py` в той же папке.
|
||||
// Дефолтный шаблон Lua-скрипта для нового скрипта (на Part или глобальный).
|
||||
// Используется при смене языка JS→Lua когда текущий код выглядит «пустым».
|
||||
const LUA_TEMPLATE_PART = `-- Скрипт привязан к Part. script.Parent = эта часть.
|
||||
export const LUA_TEMPLATE_PART = `-- Скрипт привязан к Part. script.Parent = эта часть.
|
||||
local part = script.Parent
|
||||
|
||||
part.Touched:Connect(function(hit)
|
||||
print("Касание:", hit.Name)
|
||||
end)
|
||||
`;
|
||||
const LUA_TEMPLATE_GLOBAL = `-- Глобальный Lua-скрипт. Работает на стороне сервера/клиента.
|
||||
export const LUA_TEMPLATE_GLOBAL = `-- Глобальный Lua-скрипт. Работает на стороне сервера/клиента.
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
Players.PlayerAdded:Connect(function(player)
|
||||
print("Игрок зашёл:", player.Name)
|
||||
end)
|
||||
`;
|
||||
const JS_TEMPLATE_GLOBAL = `// Глобальный JS-скрипт. Подробнее: см. game.* API в /справочник.
|
||||
export const JS_TEMPLATE_GLOBAL = `// Глобальный JS-скрипт. Подробнее: см. game.* API в /справочник.
|
||||
game.onPlayerJoined((player) => {
|
||||
game.chat.say('Привет, ' + player.name + '!');
|
||||
});
|
||||
@ -115,6 +115,15 @@ function ScriptEditor({ value, onSave, onRunSolo, isSoloRunning, scriptId, targe
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
// При смене языка — принудительно синхронизируем код со слотом нового языка.
|
||||
// (родитель swap'нул code_js ↔ code_lua и прислал свежий value.)
|
||||
useEffect(() => {
|
||||
if (value !== undefined && value !== localCodeRef.current) {
|
||||
setLocalCode(value || '');
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [language]);
|
||||
|
||||
// Дебаунс-сохранение
|
||||
const scheduleSave = useCallback((code) => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
@ -340,16 +349,9 @@ function ScriptEditor({ value, onSave, onRunSolo, isSoloRunning, scriptId, targe
|
||||
onClick={() => {
|
||||
if (active) return;
|
||||
if (!onLanguageChange) return;
|
||||
if (isCodeLikelyEmptyTemplate(localCode)) {
|
||||
const nextCode = lang === 'lua'
|
||||
? (target ? LUA_TEMPLATE_PART : LUA_TEMPLATE_GLOBAL)
|
||||
: JS_TEMPLATE_GLOBAL;
|
||||
setLocalCode(nextCode);
|
||||
onLanguageChange(lang, nextCode);
|
||||
return;
|
||||
}
|
||||
// Код не пустой — переключаем сразу, без модалки.
|
||||
// Код остаётся как есть, только подсветка синтаксиса меняется.
|
||||
// Логика двух слотов (code_js / code_lua) живёт в родителе.
|
||||
// Здесь только сигналим: «переключи на lang».
|
||||
// Текущий код отдаём чтобы родитель сохранил в слот.
|
||||
onLanguageChange(lang, localCodeRef.current);
|
||||
}}
|
||||
style={{
|
||||
|
||||
@ -6734,7 +6734,7 @@ export class BabylonScene {
|
||||
}
|
||||
|
||||
/** Установить код одного скрипта по id. Если id нет — создать новый. */
|
||||
upsertScript(id, code, target = undefined, name = undefined, language = undefined) {
|
||||
upsertScript(id, code, target = undefined, name = undefined, language = undefined, slots = undefined) {
|
||||
const i = this._scripts.findIndex(s => s.id === id);
|
||||
if (i >= 0) {
|
||||
this._scripts[i] = {
|
||||
@ -6743,6 +6743,10 @@ export class BabylonScene {
|
||||
...(target !== undefined ? { target } : {}),
|
||||
...(name !== undefined ? { name } : {}),
|
||||
...(language !== undefined ? { language } : {}),
|
||||
// Слоты code_js и code_lua — сохраняемый код для каждого языка.
|
||||
// Передаются при переключении языка, чтобы код другого языка
|
||||
// не пропадал.
|
||||
...(slots && typeof slots === 'object' ? slots : {}),
|
||||
};
|
||||
} else {
|
||||
this._scripts.push({
|
||||
@ -6751,6 +6755,7 @@ export class BabylonScene {
|
||||
target: target !== undefined ? target : null,
|
||||
name: name || null,
|
||||
language: language || 'js',
|
||||
...(slots && typeof slots === 'object' ? slots : {}),
|
||||
});
|
||||
}
|
||||
// Скрипты — часть сцены: фиксируем в истории, иначе undo откатит
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user