diff --git a/chat_gui.lua b/chat_gui.lua index c293ff2..d1b1ee2 100644 --- a/chat_gui.lua +++ b/chat_gui.lua @@ -35,7 +35,7 @@ end -- ============================================================ -- Privilege helpers --- llm_root is a superrole: implies llm + llm_dev + llm_worldedit +-- llm_root is a super-role: implies llm + llm_dev + llm_worldedit -- ============================================================ local function raw_priv(name, priv) @@ -132,7 +132,7 @@ function M.show(name) table.insert(fs, "box[0,0;" .. W .. "," .. HEADER_H .. ";#202020]") table.insert(fs, "label[" .. PAD .. ",0.30;LLM Chat - " .. core.formspec_escape(name) .. "]") - -- ── Header Zeile 1 rechts: Config (root) + IDE (dev) ──── + -- ── Header row 1 right side: Config (root) + IDE (dev) ──── local right_x = W - PAD if can_config(name) then right_x = right_x - 2.0 @@ -147,7 +147,7 @@ function M.show(name) table.insert(fs, "tooltip[open_ide;Open Smart Lua IDE (llm_dev)]") end - -- Header Zeile 2: drei direkte WE-Mode Buttons nebeneinander + -- Header row 2: three direct WE-Mode buttons side by side local mode = session.we_mode or "chat" if can_worldedit(name) then local we_ok = we_available() @@ -206,7 +206,7 @@ end -- ============================================================ function M.handle_fields(name, formname, fields) - -- Material Picker weiterleiten + -- Forward to Material Picker if formname:match("^llm_connect:material_picker") then if material_picker then local result = material_picker.handle_fields(name, formname, fields) @@ -223,7 +223,7 @@ function M.handle_fields(name, formname, fields) local session = get_session(name) local updated = false - -- ── WE-Buttons (privilege-geprüft) ────────────────────── + -- ── WE-Buttons (privilege-checked) ────────────────────── if fields.we_btn_chat then if can_worldedit(name) then session.we_mode = "chat"; updated = true end @@ -246,7 +246,7 @@ function M.handle_fields(name, formname, fields) updated = true end - -- ── IDE / Config (privilege-geprüft) ──────────────────── + -- ── IDE / Config (privilege-checked) ──────────────────── elseif fields.open_ide then if can_ide(name) and _G.ide_gui then @@ -313,9 +313,9 @@ function M.handle_fields(name, formname, fields) M.show(name) end) - -- Normal Chat (immer erlaubt wenn llm) + -- Normal Chat (always allowed if llm privilege is present) else - -- WE-Mode zurücksetzen wenn kein Privileg + -- Reset WE-mode if player lacks privilege if session.we_mode ~= "chat" and not can_worldedit(name) then session.we_mode = "chat" end diff --git a/code_executor.lua b/code_executor.lua index 1b1b4e2..43d0e6b 100644 --- a/code_executor.lua +++ b/code_executor.lua @@ -20,7 +20,7 @@ local function player_has_priv(name, priv) return privs[priv] == true end --- llm_root ist Superrolle: impliziert llm_dev und alle anderen +-- llm_root is a super-role: implies llm_dev and all others local function has_llm_priv(name, priv) if player_has_priv(name, "llm_root") then return true end return player_has_priv(name, priv) @@ -159,7 +159,7 @@ function M.execute(player_name, code, options) local use_sandbox = options.sandbox ~= false local allow_persist = options.allow_persist or is_root - -- Prüfen ob der Player überhaupt Ausführungsrechte hat + -- Check whether the player has execution rights at all if not has_llm_priv(player_name, "llm_dev") then result.error = "Missing privilege: llm_dev (or llm_root)" return result diff --git a/config_gui.lua b/config_gui.lua index df6adee..cbcee2c 100644 --- a/config_gui.lua +++ b/config_gui.lua @@ -124,7 +124,7 @@ function M.show(name) table.insert(fs, "checkbox[" .. PAD .. "," .. y .. ";wea_enabled;" .. core.formspec_escape(wea_label) .. ";" .. (wea_val and "true" or "false") .. "]") y = y + 0.55 + PAD - -- 4 Buttons gleichmaessig verteilt: Save, Reload, Test, Close + -- 4 buttons evenly distributed: Save, Reload, Test, Close local btn_count = 4 local btn_spacing = 0.2 local btn_w = (W - PAD * 2 - btn_spacing * (btn_count - 1)) / btn_count @@ -240,7 +240,7 @@ function M.handle_fields(name, formname, fields) return true elseif fields.close or fields.quit then - -- Zurueck zur chat_gui + -- Return to chat_gui if _G.chat_gui then _G.chat_gui.show(name) else diff --git a/llm_worldedit.lua b/llm_worldedit.lua index f067e99..dee1621 100644 --- a/llm_worldedit.lua +++ b/llm_worldedit.lua @@ -47,7 +47,7 @@ end function M.is_available() if type(worldedit) ~= "table" then return false end - -- Erweiterte Prüfung – verschiedene WE-Versionen haben unterschiedliche Funktionsnamen + -- Extended check – different WE versions use different function names return type(worldedit.set) == "function" or type(worldedit.set_node) == "function" or type(worldedit.manip_helpers) == "table" diff --git a/material_picker.lua b/material_picker.lua index ed2df26..75c7123 100644 --- a/material_picker.lua +++ b/material_picker.lua @@ -1,31 +1,31 @@ -- material_picker.lua v2.0 --- Inventar-style Materialauswahl für LLM WorldEdit Kontext +-- Inventory-style material selection for the LLM WorldEdit context -- --- UI: Kacheln mit Item-Icons (item_image) + farbiger Markierung wenn aktiv --- Suchfilter oben, Toggle-All Button, Remove-All Button --- Kacheln sind Buttons → Klick togglet Selektion +-- UI: Tiles with item icons (item_image) + colored highlight when active +-- Search filter at the top, Toggle-All button, Remove-All button +-- Tiles are buttons → click toggles selection -- --- PUBLIC API (genutzt von chat_gui.lua / llm_worldedit.lua): --- M.get_materials(player_name) → sortierte Liste von Node-Strings +-- PUBLIC API (used by chat_gui.lua / llm_worldedit.lua): +-- M.get_materials(player_name) → sorted list of node name strings -- M.has_materials(player_name) → bool --- M.build_material_context(player_name) → String für LLM-Systemprompt --- M.show(player_name) → Formspec öffnen +-- M.build_material_context(player_name) → string for LLM system prompt +-- M.show(player_name) → open formspec -- M.handle_fields(player_name, formname, fields) → bool local core = core local M = {} -- ============================================================ --- Konfiguration +-- Configuration -- ============================================================ -local COLS = 8 -- Kacheln pro Zeile -local TILE_SIZE = 1.4 -- Breite/Höhe einer Kachel in Formspec-Einheiten -local TILE_PAD = 0.08 -- Abstand zwischen Kacheln -local MAX_NODES = 128 -- max. Kandidaten die gerendert werden +local COLS = 8 -- tiles per row +local TILE_SIZE = 1.4 -- tile width/height in formspec units +local TILE_PAD = 0.08 -- spacing between tiles +local MAX_NODES = 128 -- max candidates to render -- ============================================================ --- Session-State +-- Session state -- ============================================================ local sessions = {} @@ -35,7 +35,7 @@ local function get_session(name) sessions[name] = { materials = {}, -- [node_name] = true filter = "", - page = 1, -- aktuelle Seite (Paginierung) + page = 1, -- current page (pagination) } end return sessions[name] @@ -78,7 +78,7 @@ function M.build_material_context(player_name) end -- ============================================================ --- Registry-Filter +-- Registry filter -- ============================================================ local function build_candidate_list(filter) @@ -103,10 +103,10 @@ local function build_candidate_list(filter) end -- ============================================================ --- Formspec Builder +-- Formspec builder -- ============================================================ --- Berechnet Seitenzahl +-- Calculates page count local function get_page_info(total, per_page, current_page) local total_pages = math.max(1, math.ceil(total / per_page)) current_page = math.max(1, math.min(current_page, total_pages)) @@ -115,7 +115,7 @@ local function get_page_info(total, per_page, current_page) return current_page, total_pages, first, last end -local ITEMS_PER_PAGE = COLS * 6 -- 6 Zeilen = 48 Kacheln pro Seite +local ITEMS_PER_PAGE = COLS * 6 -- 6 rows = 48 tiles per page function M.show(player_name) local sess = get_session(player_name) @@ -125,12 +125,12 @@ function M.show(player_name) local page, total_pages, first, last = get_page_info(total, ITEMS_PER_PAGE, sess.page) - sess.page = page -- korrigierte Seite zurückschreiben + sess.page = page -- write corrected page back local selected_count = 0 for _ in pairs(sess.materials) do selected_count = selected_count + 1 end - -- ── Dimensionen ──────────────────────────────────────── + -- ── Dimensions ──────────────────────────────────────── local W = COLS * (TILE_SIZE + TILE_PAD) + 0.5 local HDR_H = 0.9 local SRCH_H = 0.7 @@ -158,7 +158,7 @@ function M.show(player_name) local y = HDR_H + PAD - -- ── Suchfeld ──────────────────────────────────────────── + -- ── Search field ──────────────────────────────────────── local field_w = W - PAD * 2 - 2.6 table.insert(fs, "field[" .. PAD .. "," .. y .. ";" .. string.format("%.2f", field_w) .. "," .. SRCH_H .. ";filter;;" .. core.formspec_escape(filter) .. "]") @@ -169,7 +169,7 @@ function M.show(player_name) .. ";2.4," .. SRCH_H .. ";do_filter;⟳ Search]") y = y + SRCH_H + PAD - -- ── Info-Zeile + Toggle-All ───────────────────────────── + -- ── Info row + Toggle-All ───────────────────────────── local info_str if total == 0 then info_str = "No nodes found" @@ -178,7 +178,7 @@ function M.show(player_name) end table.insert(fs, "label[" .. PAD .. "," .. (y + 0.05) .. ";" .. core.formspec_escape(info_str) .. "]") - -- Toggle-All Button (alle auf dieser Seite ein/aus) + -- Toggle-All button (select/deselect all on this page) local page_nodes = {} for i = first, last do table.insert(page_nodes, candidates[i]) @@ -194,8 +194,8 @@ function M.show(player_name) .. ";3.5," .. INFO_H+0.1 .. ";toggle_page;" .. toggle_label .. "]") y = y + INFO_H + PAD - -- ── Kachel-Grid ──────────────────────────────────────── - -- Jede Kachel = item_image_button (Icon) + farbiger Hintergrund wenn aktiv + -- ── Tile grid ──────────────────────────────────────── + -- Each tile = item_image_button (icon) + colored background when active local col = 0 local row = 0 local IMG = TILE_SIZE - 0.25 @@ -207,13 +207,13 @@ function M.show(player_name) local is_sel = sess.materials[node_name] == true - -- Hintergrund-Box: grün wenn selektiert, dunkel wenn nicht + -- Background box: green if selected, dark if not local bg_color = is_sel and "#1a3a1a" or "#1a1a1a" table.insert(fs, "box[" .. string.format("%.2f,%.2f;%.2f,%.2f", tx, ty, TILE_SIZE, TILE_SIZE) .. ";" .. bg_color .. "]") - -- Item-Image-Button (klickbar, zeigt Icon) - -- button-name enkodiert den Kandidaten-Index: "tile_N" + -- Item image button (clickable, shows icon) + -- button name encodes the candidate index: "tile_N" local btn_name = "tile_" .. tostring((page - 1) * ITEMS_PER_PAGE + idx) -- item_image_button[x,y;w,h;item;name;label] table.insert(fs, "item_image_button[" @@ -221,14 +221,14 @@ function M.show(player_name) .. ";" .. core.formspec_escape(node_name) .. ";" .. btn_name .. ";]") - -- Checkmark-Label oben rechts wenn selektiert + -- Checkmark label top-right when selected if is_sel then table.insert(fs, "label[" .. string.format("%.2f,%.2f", tx + TILE_SIZE - 0.38, ty + 0.18) .. ";§(c=#00ff00)✔]") end - -- Tooltip: Node-Name + -- Tooltip: node name local def = core.registered_nodes[node_name] local desc = (def and def.description and def.description ~= "") and def.description or node_name @@ -255,7 +255,7 @@ function M.show(player_name) end y = y + NAV_H + PAD - -- ── Bottom Buttons ────────────────────────────────────── + -- ── Bottom buttons ────────────────────────────────────── local b_w = (W - PAD * 3) / 2 table.insert(fs, "style[clear_all;bgcolor=#3a1a1a;textcolor=#ff8888]") table.insert(fs, "button[" .. PAD .. "," .. y .. ";" .. string.format("%.2f", b_w) .. "," .. BTN_H @@ -269,7 +269,7 @@ function M.show(player_name) end -- ============================================================ --- Formspec Handler +-- Formspec handler -- ============================================================ function M.handle_fields(player_name, formname, fields) @@ -281,19 +281,19 @@ function M.handle_fields(player_name, formname, fields) local candidates = build_candidate_list(sess.filter) local total = #candidates - -- Filter aktualisieren (live) + -- Update filter (live) if fields.filter ~= nil then sess.filter = fields.filter end - -- ── Search / Filter ────────────────────────────────── + -- ── Search / filter ────────────────────────────────── if fields.do_filter or fields.key_enter_field == "filter" then sess.page = 1 M.show(player_name) return true end - -- ── Paginierung ────────────────────────────────────── + -- ── Pagination ────────────────────────────────────── local page, total_pages = get_page_info(total, ITEMS_PER_PAGE, sess.page) if fields.page_prev then @@ -308,10 +308,10 @@ function M.handle_fields(player_name, formname, fields) return true end - -- ── Toggle Page ────────────────────────────────────── + -- ── Toggle page ────────────────────────────────────── if fields.toggle_page then local _, _, first, last = get_page_info(total, ITEMS_PER_PAGE, sess.page) - -- Prüfen ob alle selektiert + -- Check if all are selected local all_sel = true for i = first, last do if not sess.materials[candidates[i]] then all_sel = false; break end @@ -328,22 +328,22 @@ function M.handle_fields(player_name, formname, fields) return true end - -- ── Clear All ──────────────────────────────────────── + -- ── Clear all ──────────────────────────────────────── if fields.clear_all then sess.materials = {} M.show(player_name) return true end - -- ── Close / Done ───────────────────────────────────── + -- ── Close / done ───────────────────────────────────── if fields.close_picker or fields.close_and_back or fields.quit then - -- Signalisierung ans chat_gui: Picker schließen → Chat-GUI wieder öffnen - -- (wird in handle_fields von chat_gui.lua / init.lua gehandelt) + -- Signal to chat_gui: picker closed → re-open chat GUI + -- (handled in handle_fields of chat_gui.lua / init.lua) return true end - -- ── Kachel-Buttons: tile_N ──────────────────────────── - -- Format: tile_ (1-basiert über alle Seiten) + -- ── Tile buttons: tile_N ──────────────────────────── + -- Format: tile_ (1-based across all pages) for field_name, _ in pairs(fields) do local global_idx = field_name:match("^tile_(%d+)$") if global_idx then