mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Add Technical Changelog page
This commit is contained in:
30
package-lock.json
generated
30
package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"deepslate": "^0.9.0-beta.6",
|
||||
"deepslate-rs": "^0.1.6",
|
||||
"howler": "^2.2.3",
|
||||
"marked": "^3.0.8",
|
||||
"rfdc": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -25,6 +26,7 @@
|
||||
"@rollup/plugin-html": "^0.2.3",
|
||||
"@types/google.analytics": "0.0.40",
|
||||
"@types/howler": "^2.2.4",
|
||||
"@types/marked": "^3.0.2",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
@@ -514,6 +516,12 @@
|
||||
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/marked": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-3.0.2.tgz",
|
||||
"integrity": "sha512-mGYI9qFs+i5eYaytWKBbtEMbIkrXGKuhsDpDcj70ogKS2gk1NmgEy9Z3VEKz922Lfms6eITXXqv5idlX7C/irw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||
@@ -1920,6 +1928,17 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz",
|
||||
"integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==",
|
||||
"bin": {
|
||||
"marked": "bin/marked"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
@@ -3128,6 +3147,12 @@
|
||||
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/marked": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-3.0.2.tgz",
|
||||
"integrity": "sha512-mGYI9qFs+i5eYaytWKBbtEMbIkrXGKuhsDpDcj70ogKS2gk1NmgEy9Z3VEKz922Lfms6eITXXqv5idlX7C/irw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||
@@ -4170,6 +4195,11 @@
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz",
|
||||
"integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw=="
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"deepslate": "^0.9.0-beta.6",
|
||||
"deepslate-rs": "^0.1.6",
|
||||
"howler": "^2.2.3",
|
||||
"marked": "^3.0.8",
|
||||
"rfdc": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -31,6 +32,7 @@
|
||||
"@rollup/plugin-html": "^0.2.3",
|
||||
"@types/google.analytics": "0.0.40",
|
||||
"@types/howler": "^2.2.4",
|
||||
"@types/marked": "^3.0.2",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
|
||||
@@ -43,7 +43,6 @@ export namespace Analytics {
|
||||
|
||||
export function setGenerator(generator: string) {
|
||||
dimension(DIM_GENERATOR, generator)
|
||||
console.log(generator)
|
||||
}
|
||||
|
||||
export function generatorEvent(action: string, label?: string) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../styles/nodes.css'
|
||||
import { Analytics } from './Analytics'
|
||||
import { Header } from './components'
|
||||
import { loadLocale, locale, Locales } from './Locales'
|
||||
import { Generator, Home, Sounds, Worldgen } from './pages'
|
||||
import { Changelog, Generator, Home, Sounds, Worldgen } from './pages'
|
||||
import type { VersionId } from './Schemas'
|
||||
import { Store } from './Store'
|
||||
import { cleanUrl } from './Utils'
|
||||
@@ -72,6 +72,7 @@ function Main() {
|
||||
<Home path="/" {...{lang, changeTitle}} />
|
||||
<Worldgen path="/worldgen" {...{lang, changeTitle}} />
|
||||
<Sounds path="/sounds" {...{lang, version, changeTitle, changeVersion}} />
|
||||
<Changelog path="/changelog" {...{lang, changeTitle}} />
|
||||
<Generator default {...{lang, version, changeTitle, changeVersion}} />
|
||||
</Router>
|
||||
</>
|
||||
|
||||
87
src/app/pages/Changelog.tsx
Normal file
87
src/app/pages/Changelog.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import marked from 'marked'
|
||||
import { useEffect, useMemo, useState } from 'preact/hooks'
|
||||
import { Ad, ErrorPanel, TextInput } from '../components'
|
||||
import { locale } from '../Locales'
|
||||
import type { VersionId } from '../Schemas'
|
||||
import type { ChangelogEntry } from '../services/Changelogs'
|
||||
import { getChangelogs } from '../services/Changelogs'
|
||||
import { hashString } from '../Utils'
|
||||
|
||||
type ChangelogProps = {
|
||||
path?: string,
|
||||
lang: string,
|
||||
changeTitle: (title: string, versions?: VersionId[]) => unknown,
|
||||
}
|
||||
export function Changelog({ lang, changeTitle }: ChangelogProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
changeTitle(loc('title.changelog'))
|
||||
|
||||
const [changelogs, setChangelogs] = useState<ChangelogEntry[]>([])
|
||||
useEffect(() => {
|
||||
getChangelogs()
|
||||
.then(changelogs => setChangelogs(changelogs))
|
||||
.catch(e => { console.error(e); setError(e) })
|
||||
}, [])
|
||||
|
||||
const [search, setSearch] = useState('')
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
const addTag = (tag: string) => {
|
||||
if (!tags.includes(tag)) {
|
||||
setTags([...tags, tag])
|
||||
}
|
||||
}
|
||||
|
||||
const filteredChangelogs = useMemo(() => {
|
||||
const query = search.split(' ').map(q => q.trim().toLowerCase()).filter(q => q.length > 0)
|
||||
if (query.length === 0 && tags.length === 0) return changelogs
|
||||
return changelogs.filter(change => {
|
||||
if (!tags.every(tag => change.tags.includes(tag))) {
|
||||
return false
|
||||
}
|
||||
const content = change.tags.join(' ') + ' ' + change.content.toLowerCase()
|
||||
return query.every(q => content.includes(q))
|
||||
})
|
||||
}, [changelogs, search, tags])
|
||||
|
||||
return <main>
|
||||
<Ad type="text" id="changelog" />
|
||||
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
||||
<div class="changelog-controls">
|
||||
<TextInput class="btn btn-input changelog-search" list="sound-list" placeholder={loc('changelog.search')}
|
||||
value={search} onChange={setSearch} />
|
||||
{tags.length > 0 && <div class="changelog-tags">
|
||||
{tags.map(tag => <Tag label={tag} onClick={() => setTags(tags.filter(t => t !== tag))} />)}
|
||||
</div>}
|
||||
</div>
|
||||
<div class="changelog">
|
||||
{filteredChangelogs.map(change =>
|
||||
<Change change={change} activeTags={tags} addTag={addTag} />)}
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
|
||||
type ChangeProps = {
|
||||
change: ChangelogEntry,
|
||||
activeTags: string[],
|
||||
addTag: (tag: string) => unknown,
|
||||
}
|
||||
function Change({ change, activeTags, addTag }: ChangeProps) {
|
||||
return <div class="changelog-entry">
|
||||
<div class="changelog-tags">
|
||||
{change.tags.map(tag => <Tag label={tag} onClick={() => addTag(tag)} active={activeTags.includes(tag)} />)}
|
||||
<a class="changelog-version" href={`https://www.minecraft.net/en-us/article/minecraft-snapshot-${change.version}`}>{change.version}</a>
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(change.content) }} />
|
||||
</div>
|
||||
}
|
||||
|
||||
type TagProps = {
|
||||
label: string,
|
||||
active?: boolean,
|
||||
onClick?: () => unknown,
|
||||
}
|
||||
function Tag({ label, active, onClick }: TagProps) {
|
||||
const color = hashString(label) % 360
|
||||
return <div class={`changelog-tag${active ? ' active' : ''}${onClick ? ' clickable' : ''}`} style={`--tint: ${color}`} onClick={onClick}>{label}</div>
|
||||
}
|
||||
@@ -27,6 +27,7 @@ export function Home({ lang, changeTitle }: HomeProps) {
|
||||
<ToolCard title="Data Pack Upgrader" link="https://misode.github.io/upgrader/">
|
||||
<p>Convert your 1.16 data packs to 1.17</p>
|
||||
</ToolCard>
|
||||
<ToolCard title="Technical Changelog" link="/changelog/" />
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './Changelog'
|
||||
export * from './Generator'
|
||||
export * from './Home'
|
||||
export * from './Sounds'
|
||||
|
||||
47
src/app/services/Changelogs.ts
Normal file
47
src/app/services/Changelogs.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
const repo = 'https://raw.githubusercontent.com/misode/technical-changes/main'
|
||||
|
||||
export type ChangelogEntry = {
|
||||
group: string,
|
||||
version: string,
|
||||
tags: string[],
|
||||
content: string,
|
||||
}
|
||||
|
||||
let Changelogs: ChangelogEntry[] | Promise<ChangelogEntry[]> | null = null
|
||||
|
||||
export async function getChangelogs() {
|
||||
if (!Changelogs) {
|
||||
const index = await (await fetch(`${repo}/index.json`)).json() as string[]
|
||||
Changelogs = (await Promise.all(
|
||||
index.map(group => fetchGroup(group))
|
||||
)).flat()
|
||||
}
|
||||
return Changelogs
|
||||
}
|
||||
|
||||
async function fetchGroup(group: string) {
|
||||
const index = await (await fetch(`${repo}/${group}/index.json`)).json() as string[]
|
||||
return (await Promise.all(
|
||||
index.map(version => fetchChangelog(group, version))
|
||||
)).flat()
|
||||
}
|
||||
|
||||
async function fetchChangelog(group: string, version: string) {
|
||||
const text = await (await fetch(`${repo}/${group}/${version}.md`)).text()
|
||||
return parseChangelog(text).map(change => ({
|
||||
version,
|
||||
group,
|
||||
...change,
|
||||
}))
|
||||
}
|
||||
|
||||
function parseChangelog(text: string) {
|
||||
return text.split('\n\n')
|
||||
.map(entry => {
|
||||
const i = entry.indexOf('|')
|
||||
return {
|
||||
tags: entry.substring(0, i).trim().split(' '),
|
||||
content: entry.slice(i + 1).trim(),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"add_bottom": "Add to bottom",
|
||||
"add_top": "Add to top",
|
||||
"advancement": "Advancement",
|
||||
"changelog.search": "Search changes",
|
||||
"collapse": "Collapse",
|
||||
"collapse_all": "Hold %0% to collapse all",
|
||||
"configure_layers": "Configure layers",
|
||||
@@ -63,6 +64,7 @@
|
||||
"theme.dark": "Dark",
|
||||
"theme.light": "Light",
|
||||
"theme.system": "System",
|
||||
"title.changelog": "Technical Changelog",
|
||||
"title.generator": "%0% Generator",
|
||||
"title.generator_category": "%0% Generators",
|
||||
"title.home": "Data Pack Generators",
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
--errors-background: #62190f;
|
||||
--errors-text: #ffffffcc;
|
||||
--invalid-text: #fd7951;
|
||||
--text-saturation: 60%;
|
||||
--text-lightness: 45%;
|
||||
}
|
||||
|
||||
:root[data-theme=light] {
|
||||
@@ -48,6 +50,8 @@
|
||||
--errors-background: #f66653;
|
||||
--errors-text: #000000cc;
|
||||
--invalid-text: #a32600;
|
||||
--text-saturation: 100%;
|
||||
--text-lightness: 30%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
@@ -75,6 +79,8 @@
|
||||
--errors-background: #f66653;
|
||||
--errors-text: #000000cc;
|
||||
--invalid-text: #a32600;
|
||||
--text-saturation: 100%;
|
||||
--text-lightness: 35%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,11 +385,18 @@ main.has-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.btn-menu.menu-sw .btn-group {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.btn-menu.menu-se .btn-group {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 7px -2px #000;
|
||||
@@ -975,6 +988,83 @@ hr {
|
||||
color: var(--invalid-text);
|
||||
}
|
||||
|
||||
.changelog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.changelog-entry {
|
||||
background: var(--background-2);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
|
||||
.changelog-tags {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.changelog-tag {
|
||||
--color: hsl(var(--tint, 0), var(--text-saturation), var(--text-lightness));
|
||||
margin-right: 8px;
|
||||
border: 1.5px solid var(--color);
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
padding: 0 8px;
|
||||
color: var(--color);
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.changelog-tag.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.changelog-tag.active {
|
||||
background-color: var(--color);
|
||||
color: var(--background-2);
|
||||
}
|
||||
|
||||
.changelog-version {
|
||||
margin-left: auto;
|
||||
font-size: 15px;
|
||||
color: var(--text-3);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.changelog-version:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.changelog-entry code {
|
||||
background-color: var(--background-5);
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-1);
|
||||
}
|
||||
|
||||
.changelog-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.changelog-search {
|
||||
flex-basis: 100%;
|
||||
padding: 8px;
|
||||
background-color: var(--background-2);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.changelog-controls .changelog-tags {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
.sound-search-group {
|
||||
margin-bottom: 8px;
|
||||
|
||||
@@ -21,6 +21,11 @@ export default defineConfig({
|
||||
title: getTitle({ id: 'title.sounds', page: true }),
|
||||
template: template,
|
||||
}),
|
||||
html({
|
||||
fileName: `changelog/index.html`,
|
||||
title: getTitle({ id: 'title.changelog', page: true }),
|
||||
template: template,
|
||||
}),
|
||||
...config.generators.map(m => html({
|
||||
fileName: `${m.url}/index.html`,
|
||||
title: getTitle(m),
|
||||
|
||||
Reference in New Issue
Block a user