diff --git a/_layouts/default.html b/_layouts/default.html index 9bf1301..1c6b8cf 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -53,7 +53,9 @@ {% endif %} +
{{ content }} +
diff --git a/src/docs.js b/src/docs.js index 7e8cc46..bd5caa7 100644 --- a/src/docs.js +++ b/src/docs.js @@ -81,18 +81,6 @@ function activateMenuNesting() { const handleMenuNesting = (e) => { e.preventDefault(); toggleParent(e.currentTarget, "open"); - const elementType = e.currentTarget.tagName.toLowerCase(); - if (elementType === "a") { - const linkElement = e.currentTarget; - const linkElementParent = linkElement.parentNode; - const destination = linkElement.href; - if ( - destination !== window.location.href && - !linkElementParent.classList.contains("active") - ) { - window.location.href = destination; - } - } }; if (menuParents) { [...menuParents].map(elem => { diff --git a/src/ide.ts b/src/ide.ts index 9d9eb45..31087f4 100644 --- a/src/ide.ts +++ b/src/ide.ts @@ -7,23 +7,28 @@ import * as effekt from "./effekt-language"; import * as monaco from "monaco-editor/esm/vs/editor/editor.api"; import type { Diagnostic, DiagnosticSeverity, Position } from "vscode-languageserver-types" -// initialize: -// load all code[module=...] elements and write them to the IDE -document.querySelectorAll("code[module]").forEach( (code: HTMLElement) => { - let module = code.getAttribute("module") - let filename = module + ".effekt" - let content = code.getAttribute("prelude") + code.getAttribute("content") + code.getAttribute("postlude") - effekt.write(filename, content) -}) - export interface IViewModel extends monaco.editor.ITextModel { getFullText(): string modelPosition(pos: Position): Position viewPosition(pos: Position): Position } +// initialize: +// load all code[module=...] elements and write them to the IDE +export function loadModules() { + document.querySelectorAll("code[module]").forEach( (code: HTMLElement) => { + let module = code.getAttribute("module") + let filename = module + ".effekt" + let content = code.getAttribute("prelude") + code.getAttribute("content") + code.getAttribute("postlude") + effekt.write(filename, content) + }) +} + export function createModel(filename: string, contents: string, hiddenPrelude: string, hiddenPostlude: string): IViewModel { - let model = monaco.editor.createModel(contents.trim(), "effekt", monaco.Uri.file(filename)) + let model = monaco.editor.getModel(monaco.Uri.file(filename)); + if (model) model.setValue(contents.trim()) + else model = monaco.editor.createModel(contents.trim(), "effekt", monaco.Uri.file(filename)) + let pre = hiddenPrelude || "" let post = hiddenPostlude || "" let lineOffset = (hiddenPrelude.match(/\n/g) || '').length diff --git a/src/index.ts b/src/index.ts index c15b2bc..220dfc7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ async function enableEditing(code: HTMLElement, run: HTMLElement, coreOut: HTMLE let IDE = await import(/* webpackMode: "lazy", webpackChunkName: "ide" */ "./ide") let editor = await import(/* webpackMode: "lazy", webpackChunkName: "editor" */ "./editor") + IDE.loadModules(); parent.classList.remove("editor-loading") parent.classList.add("editor") @@ -264,17 +265,13 @@ function parseOptions(str: string): CodeOptions { } } - - - -window.addEventListener("DOMContentLoaded", () => { - +function initDOM() { processCode() // let codes = document.querySelectorAll("code") // monacoEditor(codes[codes.length - 1]) hljs.configure({ - languages: ['effekt', 'bash'] + languages: ['effekt', 'bash'] }); // highlight inline code @@ -286,4 +283,77 @@ window.addEventListener("DOMContentLoaded", () => { }) docs.init() -}) +} + +const globalHistory = [{ url: window.location.href, scrollPosition: 0 }]; +let currentHistoryIndex = 0; + +function loadPage(url, addToHistory = true) { + fetch(url) + .then((response) => response.text()) + .then((html) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + const newContent = doc.querySelector("main#content"); + document.querySelector("main#content").innerHTML = newContent.innerHTML; + + initDOM(); + addLinkListeners(); + + document.title = doc.querySelector("head > title").innerHTML; + + if (addToHistory) { + globalHistory.splice(currentHistoryIndex + 1); + globalHistory.push({ url, scrollPosition: 0 }); + currentHistoryIndex = globalHistory.length - 1; + window.history.pushState({ index: currentHistoryIndex }, "", url); + setTimeout(() => { // wait until content is rendered + window.scrollTo({ top: 0 }) + }, 0); + } else { + setTimeout(() => { // wait until content is rendered + window.scrollTo({ top: globalHistory[currentHistoryIndex].scrollPosition }); + }, 0); + } + console.log("Current history:", globalHistory, "Current index:", currentHistoryIndex); + }) + .catch((err) => console.error("Failed to load page", err)); +} + +function addLinkListeners() { + const links = document.querySelectorAll(".sidebar-nav a, a.next-page, a.previous-page"); + + links.forEach((link) => { + link.addEventListener("click", (e) => { + e.preventDefault(); + loadPage(link.getAttribute("href")); + }); + }); +} + +function navigateHistory(step) { + globalHistory[currentHistoryIndex].scrollPosition = window.pageYOffset; + const newIndex = currentHistoryIndex + step; + if (newIndex >= 0 && newIndex < globalHistory.length) { + currentHistoryIndex = newIndex; + loadPage(globalHistory[currentHistoryIndex].url, false); + } +} + +window.addEventListener("DOMContentLoaded", () => { + addLinkListeners(); + initDOM(); + + window.addEventListener('popstate', (event) => { + if (event.state && typeof event.state.index !== 'undefined') { + const step = event.state.index - currentHistoryIndex; + navigateHistory(step); + } + }); + + window.addEventListener('beforeunload', () => { + globalHistory[currentHistoryIndex].scrollPosition = window.pageYOffset; + }); +}); + +window.history.pushState({ index: 0 }, "", window.location.href);