From fde304f76516527b105caa8fae5ba56db4cff936 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 28 Sep 2024 00:42:36 +0200 Subject: [PATCH 1/8] Initial experiments with single page navigation --- _layouts/default.html | 2 ++ src/ide.ts | 25 +++++++++++++--------- src/index.ts | 48 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 20 deletions(-) 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/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..01131ce 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,26 +265,53 @@ 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 document.querySelectorAll("code").forEach(code => { // it is a block code if (code.parentElement.tagName == "pre") return; - + hljs.highlightBlock(code) }) - + docs.init() -}) +} + +function addLinkListeners() { + const links = document.querySelectorAll(".sidebar-nav a"); + + const loadPage = (url) => { + 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(); + + window.history.pushState(null, '', url); + }) + .catch(err => console.error("Failed to load page", err)); + } + + links.forEach((link) => { + link.addEventListener("click", (e) => { + e.preventDefault(); + loadPage(link.getAttribute("href")); + }); + }); + + initDOM(); +} + +window.addEventListener("DOMContentLoaded", () => addLinkListeners()); From 5ba313b9ca9e4edda33de832765fa82d08749cdd Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 28 Sep 2024 12:37:08 +0200 Subject: [PATCH 2/8] Support more links --- src/docs.js | 12 ------------ src/index.ts | 41 ++++++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 31 deletions(-) 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/index.ts b/src/index.ts index 01131ce..242a7e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -273,45 +273,48 @@ function initDOM() { hljs.configure({ languages: ['effekt', 'bash'] }); - + // highlight inline code document.querySelectorAll("code").forEach(code => { // it is a block code if (code.parentElement.tagName == "pre") return; - + hljs.highlightBlock(code) }) - + docs.init() } -function addLinkListeners() { - const links = document.querySelectorAll(".sidebar-nav a"); - - const loadPage = (url) => { +function loadPage(url, callback) { fetch(url) - .then(response => response.text()) - .then(html => { + .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(); + callback(); - window.history.pushState(null, '', url); + window.history.pushState(null, "", url); }) - .catch(err => console.error("Failed to load page", err)); - } + .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")); + e.preventDefault(); + loadPage(link.getAttribute("href"), () => { + initDOM(); + addLinkListeners(); + }); }); }); - - initDOM(); } -window.addEventListener("DOMContentLoaded", () => addLinkListeners()); +window.addEventListener("DOMContentLoaded", () => { + addLinkListeners() + initDOM(); +}); From 1095e8430928e96d198a76a951ba4afc66ee4a8f Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 28 Sep 2024 12:50:47 +0200 Subject: [PATCH 3/8] Scroll up after dynamic reload --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 242a7e7..a0e5beb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -307,6 +307,7 @@ function addLinkListeners() { link.addEventListener("click", (e) => { e.preventDefault(); loadPage(link.getAttribute("href"), () => { + window.scrollTo({ top: 0, behavior: 'smooth' }); initDOM(); addLinkListeners(); }); From d318807fbc96291162276015306f119465bd6f85 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 2 Oct 2024 14:05:41 +0200 Subject: [PATCH 4/8] Add support for back button --- src/index.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index a0e5beb..4211822 100644 --- a/src/index.ts +++ b/src/index.ts @@ -285,10 +285,14 @@ function initDOM() { docs.init() } +const globalHistory = []; + function loadPage(url, callback) { fetch(url) .then((response) => response.text()) .then((html) => { + globalHistory.push(window.location.href); + const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const newContent = doc.querySelector("main#content"); @@ -307,9 +311,9 @@ function addLinkListeners() { link.addEventListener("click", (e) => { e.preventDefault(); loadPage(link.getAttribute("href"), () => { - window.scrollTo({ top: 0, behavior: 'smooth' }); - initDOM(); - addLinkListeners(); + window.scrollTo({ top: 0, behavior: 'smooth' }); + initDOM(); + addLinkListeners(); }); }); }); @@ -318,4 +322,10 @@ function addLinkListeners() { window.addEventListener("DOMContentLoaded", () => { addLinkListeners() initDOM(); + + window.addEventListener('popstate', (event) => { + const previous = globalHistory.pop(); + loadPage(previous); + event.preventDefault() + }); }); From dfb92499a905c50932b8836b1f38e29ccb396d4e Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 2 Oct 2024 15:08:19 +0200 Subject: [PATCH 5/8] Make back button more reliable --- src/index.ts | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4211822..2ffe9dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -285,47 +285,51 @@ function initDOM() { docs.init() } -const globalHistory = []; +const globalHistory = [window.location.href]; -function loadPage(url, callback) { +function loadPage(url, addToHistory = true) { fetch(url) .then((response) => response.text()) .then((html) => { - globalHistory.push(window.location.href); - const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const newContent = doc.querySelector("main#content"); document.querySelector("main#content").innerHTML = newContent.innerHTML; - callback(); - window.history.pushState(null, "", url); + window.scrollTo({ top: 0, behavior: 'smooth' }); + initDOM(); + addLinkListeners(); + + document.title = doc.querySelector("head > title").innerHTML; + + if (addToHistory) { + window.history.pushState({}, "", url); + globalHistory.push(url); + } }) .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"), () => { - window.scrollTo({ top: 0, behavior: 'smooth' }); - initDOM(); - addLinkListeners(); - }); + loadPage(link.getAttribute("href")); }); }); } window.addEventListener("DOMContentLoaded", () => { - addLinkListeners() - initDOM(); - - window.addEventListener('popstate', (event) => { - const previous = globalHistory.pop(); - loadPage(previous); - event.preventDefault() - }); + addLinkListeners(); + initDOM(); + + window.addEventListener('popstate', (event) => { + if (globalHistory.length > 1) { + globalHistory.pop(); + const previousUrl = globalHistory[globalHistory.length - 1]; + loadPage(previousUrl, false); + } + }); }); From 414535ea30a13d50d1fab58b7946f9fa7703be55 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 2 Oct 2024 15:28:07 +0200 Subject: [PATCH 6/8] Also support forward button? --- src/index.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2ffe9dd..5815913 100644 --- a/src/index.ts +++ b/src/index.ts @@ -286,6 +286,7 @@ function initDOM() { } const globalHistory = [window.location.href]; +let currentHistoryIndex = 0; function loadPage(url, addToHistory = true) { fetch(url) @@ -303,8 +304,10 @@ function loadPage(url, addToHistory = true) { document.title = doc.querySelector("head > title").innerHTML; if (addToHistory) { - window.history.pushState({}, "", url); + globalHistory.splice(currentHistoryIndex + 1); globalHistory.push(url); + currentHistoryIndex = globalHistory.length - 1; + window.history.pushState({ index: currentHistoryIndex }, "", url); } }) .catch((err) => console.error("Failed to load page", err)); @@ -321,15 +324,24 @@ function addLinkListeners() { }); } +function navigateHistory(step) { + const newIndex = currentHistoryIndex + step; + if (newIndex >= 0 && newIndex < globalHistory.length) { + currentHistoryIndex = newIndex; + loadPage(globalHistory[currentHistoryIndex], false); + } +} + window.addEventListener("DOMContentLoaded", () => { addLinkListeners(); initDOM(); window.addEventListener('popstate', (event) => { - if (globalHistory.length > 1) { - globalHistory.pop(); - const previousUrl = globalHistory[globalHistory.length - 1]; - loadPage(previousUrl, false); + if (event.state && "index" in event.state) { + const step = event.state.index - currentHistoryIndex; + navigateHistory(step); } }); }); + +window.history.pushState({ index: 0 }, "", window.location.href); From 6fee322210597d4451e517d85b4b18cfbfd1b50c Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 2 Oct 2024 16:07:08 +0200 Subject: [PATCH 7/8] Hacky scroll position fix --- src/index.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5815913..f192b29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -285,7 +285,7 @@ function initDOM() { docs.init() } -const globalHistory = [window.location.href]; +const globalHistory = [{ url: window.location.href, scrollPosition: 0 }]; let currentHistoryIndex = 0; function loadPage(url, addToHistory = true) { @@ -297,25 +297,32 @@ function loadPage(url, addToHistory = true) { const newContent = doc.querySelector("main#content"); document.querySelector("main#content").innerHTML = newContent.innerHTML; - window.scrollTo({ top: 0, behavior: 'smooth' }); initDOM(); addLinkListeners(); document.title = doc.querySelector("head > title").innerHTML; - + if (addToHistory) { globalHistory.splice(currentHistoryIndex + 1); - globalHistory.push(url); + 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 }) + }, 100); + } else { + setTimeout(() => { // wait until content is rendered + window.scrollTo({ top: globalHistory[currentHistoryIndex].scrollPosition }); + }, 100); } + 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(); @@ -325,10 +332,11 @@ function addLinkListeners() { } function navigateHistory(step) { + globalHistory[currentHistoryIndex].scrollPosition = window.pageYOffset; const newIndex = currentHistoryIndex + step; if (newIndex >= 0 && newIndex < globalHistory.length) { currentHistoryIndex = newIndex; - loadPage(globalHistory[currentHistoryIndex], false); + loadPage(globalHistory[currentHistoryIndex].url, false); } } @@ -337,11 +345,15 @@ window.addEventListener("DOMContentLoaded", () => { initDOM(); window.addEventListener('popstate', (event) => { - if (event.state && "index" in event.state) { + 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); From d67db14131f5e8c3ec13871546baa01c56634346 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 2 Oct 2024 16:19:37 +0200 Subject: [PATCH 8/8] Reduce scroll timeout --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index f192b29..220dfc7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -309,11 +309,11 @@ function loadPage(url, addToHistory = true) { window.history.pushState({ index: currentHistoryIndex }, "", url); setTimeout(() => { // wait until content is rendered window.scrollTo({ top: 0 }) - }, 100); + }, 0); } else { setTimeout(() => { // wait until content is rendered window.scrollTo({ top: globalHistory[currentHistoryIndex].scrollPosition }); - }, 100); + }, 0); } console.log("Current history:", globalHistory, "Current index:", currentHistoryIndex); })