/**
* Editor Index (Simplified Universal Version)
* -------------------------------------------
* - Loads the active file from localStorage
* - Parses markers or auto-splits into blocks
* - No Ace, overlay, or navigation dependencies
* - Works directly with block_editor.js
*/
(function () {
'use strict';
console.log("[editor_index.js] Loading simplified index module...");
const ACTIVE_FILES_KEY = "sftp_active_files";
// =========================================================================
// LOCALSTORAGE HELPERS
// =========================================================================
function getActiveFileContent() {
try {
const files = JSON.parse(localStorage.getItem(ACTIVE_FILES_KEY) || "[]");
const active = files.find(f => f.active);
return {
content: active?.content || "",
name: active?.name || "Untitled",
path: active?.path || ""
};
} catch (err) {
console.error("[editor_index.js] Failed to load file:", err);
return { content: "", name: "Untitled", path: "" };
}
}
function getLines() {
const { content } = getActiveFileContent();
return content.split("\n");
}
function getLine(index) {
const lines = getLines();
return lines[index] || "";
}
function getLineCount() {
return getLines().length;
}
// =========================================================================
// MARKER & LANGUAGE HELPERS
// =========================================================================
function parseMarkerName(name) {
const cleaned = name.replace(/[\[\]]/g, "").trim();
const parts = cleaned.split("_");
if (parts.length === 1)
return { component: parts[0], language: null, number: null, fullName: cleaned };
if (parts.length === 2)
return { component: parts[0], language: parts[1], number: null, fullName: cleaned };
const num = parseInt(parts[2]);
return { component: parts[0], language: parts[1], number: isNaN(num) ? null : num, fullName: cleaned };
}
function findMarkerEnd(startRow, markerName) {
const lineCount = getLineCount();
for (let row = startRow + 1; row < lineCount; row++) {
const line = getLine(row).trim();
if (line.includes(">")) {
const m = line.match(/(?:<!--|\/\*|\/\/\/|\/\/)\s*([\w\-\[\]_]+)\s*>/);
if (m && m[1].trim() === markerName) return row;
}
}
return startRow;
}
function detectLanguage(fileName) {
const ext = (fileName.split(".").pop() || "").toLowerCase();
if (["js", "jsx", "ts"].includes(ext)) return "javascript";
if (["html", "htm"].includes(ext)) return "html";
if (["css"].includes(ext)) return "css";
if (["php"].includes(ext)) return "php";
return "text";
}
// =========================================================================
// BLOCK / INDEX GENERATION
// =========================================================================
function generateDocumentIndex() {
const { content, name } = getActiveFileContent();
const lines = content.split("\n");
const lineCount = lines.length;
const components = {};
const unmarked = [];
const markerRanges = [];
// pass 1: find explicit markers
for (let row = 0; row < lineCount; row++) {
const line = lines[row].trim();
if (!line) continue;
const open = line.match(/(?:<!--|\/\*|\/\/\/|\/\/)\s*([\w\-\[\]_]+)\s*</);
if (open) {
const markerName = open[1].trim();
const parsed = parseMarkerName(markerName);
const endRow = findMarkerEnd(row, markerName);
const markerItem = {
type: "marker",
row,
endRow,
label: markerName,
parsed,
children: []
};
markerRanges.push({ startRow: row, endRow, markerItem });
if (parsed.language) {
if (!components[parsed.component]) components[parsed.component] = {};
if (!components[parsed.component][parsed.language])
components[parsed.component][parsed.language] = [];
components[parsed.component][parsed.language].push(markerItem);
} else {
unmarked.push(markerItem);
}
}
}
// pass 2: detect functions/tags/classes
for (let row = 0; row < lineCount; row++) {
const line = lines[row].trim();
if (!line || line.match(/(?:<!--|\/\*|\/\/\/|\/\/)\s*[\w\-\[\]_]+\s*[<>]/)) continue;
let match, item = null;
if ((match = line.match(/(?:function|class)\s+(\w+)/))) {
item = { type: "function", row, label: match[1] + "()", icon: "⚙️" };
} else if ((match = line.match(/^\.([\w-]+)\s*\{/))) {
item = { type: "css", row, label: "." + match[1], icon: "🎨" };
} else if ((match = line.match(/^#([\w-]+)\s*\{/))) {
item = { type: "css", row, label: "#" + match[1], icon: "🎨" };
} else if ((match = line.match(/^<(\w+)/))) {
item = { type: "html", row, label: "<" + match[1] + ">", icon: "📦" };
}
if (!item) continue;
let belongsTo = null;
for (const range of markerRanges) {
if (row > range.startRow && row < range.endRow) {
belongsTo = range.markerItem;
break;
}
}
if (belongsTo) belongsTo.children.push(item);
else unmarked.push(item);
}
// fallback: if nothing found, split automatically
if (Object.keys(components).length === 0 && unmarked.length === 0) {
const lang = detectLanguage(name);
const size = 30;
for (let i = 0; i < lineCount; i += size) {
unmarked.push({
label: `Block ${Math.floor(i / size) + 1}`,
row: i,
endRow: Math.min(i + size - 1, lineCount - 1),
type: "auto",
lang
});
}
}
return { components, unmarked };
}
// =========================================================================
// PUBLIC API
// =========================================================================
window.EditorIndex = {
generateDocumentIndex,
findMarkerEnd,
parseMarkerName,
getActiveFileContent,
getLines,
getLine,
getLineCount
};
console.log("[editor_index.js] Ready (simplified).");
})();