diff --git a/src/pages/changelog/changelog.js b/src/pages/changelog/changelog.js index fb5a21b85..e016df1bd 100644 --- a/src/pages/changelog/changelog.js +++ b/src/pages/changelog/changelog.js @@ -1,36 +1,186 @@ +import fsOperation from "fileSystem"; import "./style.scss"; +import Contextmenu from "components/contextmenu"; import Page from "components/page"; +import Ref from "html-tag-js/ref"; import actionStack from "lib/actionStack"; import markdownIt from "markdown-it"; import markdownItTaskLists from "markdown-it-task-lists"; import helpers from "utils/helpers"; export default async function Changelog() { - const CHANGELOG_URL = - "https://raw.githubusercontent.com/deadlyjack/Acode/main/CHANGELOG.md"; - const $page = Page(strings["changelog"]); - const $content =
; + const GITHUB_API_URL = + "https://api.github.com/repos/Acode-Foundation/Acode/releases"; + const CHANGELOG_FILE_URL = + "https://raw.githubusercontent.com/Acode-Foundation/Acode/main/CHANGELOG.md"; + const currentVersion = BuildInfo.version; - $content.innerHTML = '
Loading changelog...
'; + let selectedVersion = currentVersion; + let selectedStatus = "current"; + const versionIndicatorRef = new Ref(); + const versionTextRef = new Ref(); + + const versionSelector = ( +
+ + {selectedVersion} +
+ ); + const $page = Page(strings["changelog"], { + tail: versionSelector, + }); + + const versionSelectorMenu = Contextmenu({ + top: "36px", + right: "5px", + toggler: versionSelector, + transformOrigin: "top right", + innerHTML: () => { + return ` +
  • + Current Version (${currentVersion}) +
  • +
  • + Latest Release +
  • +
  • + Beta Version +
  • +
  • + Full Changelog +
  • + `; + }, + }); + + const $content =
    ; + $content.innerHTML = '
    Loading changelog...
    '; $page.content = $content; app.append($page); - try { - const changeLog = await fetch(CHANGELOG_URL); - const changeLogText = await changeLog.text(); + async function loadLatestRelease() { + try { + const releases = await fsOperation(`${GITHUB_API_URL}/latest`).readFile( + "json", + ); + selectedVersion = releases.tag_name.replace("v", ""); + selectedStatus = "latest"; + updateVersionSelector(); + return renderChangelog(releases.body); + } catch (error) { + $content.innerHTML = + '
    Failed to load latest release notes
    '; + } + } - const cleanedText = changeLogText.replace(/^#\s*Change\s*Log\s*\n*/i, ""); + async function loadBetaRelease() { + try { + const releases = await fsOperation(GITHUB_API_URL).readFile("json"); + const betaRelease = releases.find((r) => r.prerelease); + if (!betaRelease) { + $content.innerHTML = '
    No beta release found
    '; + return; + } + selectedVersion = betaRelease.tag_name.replace("v", ""); + selectedStatus = "prerelease"; + updateVersionSelector(); + return renderChangelog(betaRelease.body); + } catch (error) { + $content.innerHTML = + '
    Failed to load beta release notes
    '; + } + } - const htmlContent = markdownIt({ html: true }) - .use(markdownItTaskLists) - .render(cleanedText); + async function loadFullChangelog() { + try { + const changeLogText = + await fsOperation(CHANGELOG_FILE_URL).readFile("utf8"); + const cleanedText = changeLogText.replace(/^#\s*Change\s*Log\s*\n*/i, ""); + selectedVersion = "Changelogs.md"; + selectedStatus = "current"; + updateVersionSelector(); + return renderChangelog(cleanedText); + } catch (error) { + $content.innerHTML = + '
    Failed to load full changelog
    '; + } + } + + async function loadVersionChangelog() { + try { + const releases = await fsOperation(GITHUB_API_URL).readFile("json"); + const currentRelease = releases.find( + (r) => r.tag_name.replace("v", "") === currentVersion, + ); + selectedVersion = currentVersion; + selectedStatus = "current"; + updateVersionSelector(); + if (currentRelease) { + return renderChangelog(currentRelease.body); + } else { + return loadLatestRelease(); + } + } catch (error) { + $content.innerHTML = + '
    Failed to load version changelog
    '; + } + } + function renderChangelog(text) { + const md = markdownIt({ html: true, linkify: true }); + const REPO_URL = "https://github.com/Acode-Foundation/Acode"; + let processedText = text + // Convert full PR URLs to #number format with links preserved in markdown + .replace( + /https:\/\/github\.com\/Acode-Foundation\/Acode\/pull\/(\d+)/g, + "[#$1](https://github.com/Acode-Foundation/Acode/pull/$1)", + ) + // Convert existing #number references to links if they aren't already + .replace( + /(?Failed to load changelog'; } + function updateVersionSelector() { + versionTextRef.textContent = selectedVersion; + versionIndicatorRef.className = "status-indicator status-" + selectedStatus; + } + + versionSelectorMenu.onclick = async function (e) { + const action = e.target.closest("li")?.getAttribute("action"); + if (!action) return; + versionSelectorMenu.hide(); + + switch (action) { + case "current": + await loadVersionChangelog(); + break; + case "latest": + await loadLatestRelease(); + break; + case "beta": + await loadBetaRelease(); + break; + case "full": + await loadFullChangelog(); + break; + } + }; + + // Load current version changelog by default + loadVersionChangelog(); + $page.onhide = function () { actionStack.remove("changelog"); }; diff --git a/src/pages/changelog/style.scss b/src/pages/changelog/style.scss index ed74854b6..e5bc0ddf8 100644 --- a/src/pages/changelog/style.scss +++ b/src/pages/changelog/style.scss @@ -5,6 +5,40 @@ padding: 0 1rem; } +.changelog-version-selector { + display: flex; + align-items: center; + gap: 8px; + background-color: var(--popup-background-color); + border: none; + border-radius: 8px; + padding: 8px 16px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + margin-right: 6px; + + &:hover { + background-color: var(--secondary-color); + } +} + +.status-indicator { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; +} +.status-latest { + background-color: #10b981; +} +.status-prerelease { + background-color: var(--danger-color); +} +.status-current { + background-color: var(--active-icon-color); +} + .loading { display: flex; justify-content: center;