Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve slashing + base href behavior #842

Merged
merged 10 commits into from
Dec 6, 2023
Merged
32 changes: 21 additions & 11 deletions website/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,34 @@ import AstroPWA from '@vite-pwa/astro';

const site = `https://strudel.cc/`; // root url without a path
const base = '/'; // base path of the strudel site
const baseNoTrailing = base.endsWith('/') ? base.slice(0, -1) : base;

// this rehype plugin converts relative anchor links to absolute ones
// it wokrs by prepending the absolute page path to anchor links
// example: #gain -> /learn/effects/#gain
// this rehype plugin fixes relative links
// it works by prepending the base + page path to anchor links
// and by prepending the base path to other relative links starting with /
// this is necessary when using a base href like <base href={base} />
// in this setup, relative anchor links will always link to base, instead of the current page
function absoluteAnchors() {
// examples with base as "mybase":
// #gain -> /mybase/learn/effects/#gain
// /some/page -> /mybase/some/page
function relativeURLFix() {
return (tree, file) => {
const chunks = file.history[0].split('/src/pages/'); // file.history[0] is the file path
const path = chunks[chunks.length - 1].slice(0, -4); // only path inside src/pages, without .mdx
return rehypeUrls((url) => {
if (!url.href.startsWith('#')) {
let newHref = baseNoTrailing;
if (url.href.startsWith('#')) {
// special case: a relative anchor link to the current page
newHref += `/${path}/${url.href}`;
} else if (url.href.startsWith('/')) {
// any other relative url starting with /
// NOTE: this does strip off serialized queries and fragments
newHref += url.pathname.endsWith('/') ? url.pathname : url.pathname + '/';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed today that using url.pathname here does cause at least one tiny regression since it strips fragments off the ends of relative links (which I didn't think was an issue at first): in this excerpt from docs

You might now be able to see this properly here: [open in REPL](/#YXdhaXQgaW5pdEh5ZHJhKCkKbGV0IHBhdHRlcm4gPSAiMyA0IDUgWzYgN10qMiIKc2hhcGUoSChwYXR0ZXJuKSkub3V0KG8wKQpuKHBhdHRlcm4pLnNjYWxlKCJBOm1pbm9yIikucGlhbm8oKS5yb29tKDEpIA%3D%3D)

the fragment needs to stay. So I think quick rework of this to keep fragment is needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh darn, it's merged.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I haven't noticed that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hotfixed by #845, thanks!

} else {
// leave this URL alone
return;
}
const baseWithSlash = base.endsWith('/') ? base : base + '/';
const absoluteUrl = baseWithSlash + path + url.href;
// console.log(url.href + ' -> ', absoluteUrl);
return absoluteUrl;
// console.log(url.href + ' -> ', newHref);
return newHref;
})(tree);
};
}
Expand All @@ -40,7 +50,7 @@ const options = {
remarkToc,
// E.g. `remark-frontmatter`
],
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }], absoluteAnchors],
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }], relativeURLFix],
};

// https://astro.build/config
Expand Down
12 changes: 6 additions & 6 deletions website/src/components/HeadCommon.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@ import { pwaInfo } from 'virtual:pwa-info';
import '../styles/index.css';

const { BASE_URL } = import.meta.env;
const base = BASE_URL;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
---

<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />

<link rel="icon" type="image/svg+xml" href="favicon.ico" />
<link rel="icon" type="image/svg+xml" href={`${baseNoTrailing}/favicon.ico`} />

<meta
name="description"
content="Strudel is a music live coding environment for the browser, porting the TidalCycles pattern language to JavaScript."
/>
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/icons/apple-icon-180.png" sizes="180x180" />
<link rel="icon" href={`${baseNoTrailing}/favicon.ico`} />
<link rel="apple-touch-icon" href={`${baseNoTrailing}/icons/apple-icon-180.png`} sizes="180x180" />
<meta name="theme-color" content="#222222" />

<base href={base} />
<base href={BASE_URL} />

<!-- Scrollable a11y code helper -->
<script src="./make-scrollable-code-focusable.js" is:inline></script>
<script src{`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></script>

<script src="/src/pwa.ts"></script>
<!-- this does not work for some reason: -->
Expand Down
7 changes: 5 additions & 2 deletions website/src/components/Header/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ const { currentPage } = Astro.props as Props;
// const lang = getLanguageFromURL(currentPage);
const langCode = 'en'; // getLanguageFromURL(currentPage);
const sidebar = SIDEBAR[langCode];

const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
---

<nav
class="flex justify-between py-2 px-4 items-center h-14 max-h-14 bg-lineHighlight text-foreground"
title="Top Navigation"
>
<div class="flex overflow-visible items-center grow" style="overflow:visible">
<a href="/" class="flex items-center text-2xl space-x-2">
<a href={`${baseNoTrailing}/`} class="flex items-center text-2xl space-x-2">
<h1 class="font-bold flex space-x-2 items-baseline text-xl">
<span>🌀</span>
<div class="flex space-x-1 items-baseline">
Expand All @@ -37,7 +40,7 @@ const sidebar = SIDEBAR[langCode];
<div class="search-item h-10">
<Search client:idle />
</div>
<a href="./" class="hidden md:flex cursor-pointer items-center space-x-1"
<a href={`${baseNoTrailing}/`} class="hidden md:flex cursor-pointer items-center space-x-1"
><CommandLineIcon className="w-5 h-5" /><span>go to REPL</span>
</a>
<div class="md:hidden">
Expand Down
8 changes: 7 additions & 1 deletion website/src/components/Header/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import './Search.css';

import { createPortal } from 'react-dom';
import * as docSearchReact from '@docsearch/react';
const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;

/** FIXME: This is still kinda nasty, but DocSearch is not ESM ready. */
const DocSearchModal = docSearchReact.DocSearchModal || (docSearchReact as any).default.DocSearchModal;
Expand Down Expand Up @@ -89,9 +91,13 @@ export default function Search() {
const a = document.createElement('a');
a.href = item.url;
const hash = a.hash === '#overview' ? '' : a.hash;
let pathname = a.pathname;
pathname = pathname.startsWith('/') ? pathname.slice(1) : pathname;
pathname = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
const url = `${baseNoTrailing}/${pathname}/${hash}`;
return {
...item,
url: `${a.pathname}${hash}`,
url,
};
});
}}
Expand Down
3 changes: 2 additions & 1 deletion website/src/components/LeftSidebar/LeftSidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Props = {
const { currentPage } = Astro.props as Props;
const { BASE_URL } = import.meta.env;
let currentPageMatch = currentPage.slice(BASE_URL.length, currentPage.endsWith('/') ? -1 : undefined);
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;

const langCode = getLanguageFromURL(currentPage) || 'en';
const sidebar = SIDEBAR[langCode];
Expand All @@ -23,7 +24,7 @@ const sidebar = SIDEBAR[langCode];
<h2>{header}</h2>
<ul>
{children.map((child) => {
const url = Astro.site?.pathname + child.link;
const url = `${baseNoTrailing}/${child.link}${child.link.endsWith('/') ? '' : '/'}`;
return (
<li class="">
<a
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/RightSidebar/TableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const TableOfContents: FC<{ headings: MarkdownHeading[]; currentPage: string }>
.map((heading, i) => (
<li className={`w-full`} key={i}>
<a
href={`${currentPage}#${heading.slug}`}
href={`${currentPage}/#${heading.slug}`}
onClick={onLinkClick}
className={`py-0.5 block cursor-pointer w-full border-l-4 border-lineHighlight hover:bg-lineHighlight ${
['pl-4', 'pl-9', 'pl-12'][heading.depth - minDepth]
Expand Down
7 changes: 2 additions & 5 deletions website/src/pages/img/example-[name].png.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { evaluate } from '@strudel.cycles/transpiler';
import '../../../../test/runtime.mjs';
import * as tunes from '../../repl/tunes.mjs';

export async function get({ params, request }) {
export async function GET({ params, request }) {
const { name } = params;
const tune = tunes[name];
const { pattern } = await evaluate(tune);
Expand All @@ -13,10 +13,7 @@ export async function get({ params, request }) {
const ctx = canvas.getContext('2d');
pianoroll({ time: 4, haps, ctx, playhead: 1, fold: 1, background: 'transparent', playheadColor: 'transparent' });
const buffer = canvas.toBuffer('image/png');
return {
body: buffer,
encoding: 'binary',
};
return new Response(buffer);
}
export function getStaticPaths() {
return Object.keys(tunes).map((name) => ({
Expand Down
7 changes: 5 additions & 2 deletions website/src/pages/swatch/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Content } from '../../../../my-patterns/README.md';
import HeadCommon from '../../components/HeadCommon.astro';

const myPatterns = await getMyPatterns();

const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
---

<head>
Expand All @@ -23,12 +26,12 @@ const myPatterns = await getMyPatterns();
Object.entries(myPatterns).map(([name, tune]) => (
<a
class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative"
href={`./#${btoa(tune as string)}`}
href={`${baseNoTrailing}/#${btoa(tune as string)}`}
>
<div class="absolute w-full h-full flex justify-center items-center">
<span class="bg-slate-800 p-2 rounded-md text-white">{name}</span>
</div>
<img src={`./swatch/${name}.png`} />
<img src={`${baseNoTrailing}/swatch/${name}.png/`} />
</a>
))
}
Expand Down
2 changes: 1 addition & 1 deletion website/src/pages/workshop/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { MiniRepl } from '../../docs/MiniRepl';

# Welcome

<img src="/icons/strudel_icon.png" className="w-32 animate-pulse md:float-right ml-8" />
<div className="w-32 animate-pulse md:float-right ml-8">![Strudel Icon](/icons/strudel_icon.png)</div>

Welcome to the Strudel documentation pages!
You've come to the right place if you want to learn how to make music with code.
Expand Down
5 changes: 4 additions & 1 deletion website/src/repl/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { FilesTab } from './FilesTab';

const TAURI = window.__TAURI__;

const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;

export function Footer({ context }) {
const footerContent = useRef();
const [log, setLog] = useState([]);
Expand Down Expand Up @@ -154,7 +157,7 @@ function WelcomeTab() {
</p>
<p>
To learn more about what this all means, check out the{' '}
<a href="./workshop/getting-started" target="_blank">
<a href={`${baseNoTrailing}/workshop/getting-started/`} target="_blank">
interactive tutorial
</a>
. Also feel free to join the{' '}
Expand Down
4 changes: 3 additions & 1 deletion website/src/repl/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import React, { useContext } from 'react';
import { useSettings, setIsZen } from '../settings.mjs';
// import { ReplContext } from './Repl';
import './Repl.css';
const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;

export function Header({ context }) {
const {
Expand Down Expand Up @@ -123,7 +125,7 @@ export function Header({ context }) {
{!isEmbedded && (
<a
title="learn"
href="./workshop/getting-started"
href={`${baseNoTrailing}/workshop/getting-started/`}
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
>
<AcademicCapIcon className="w-6 h-6" />
Expand Down
11 changes: 7 additions & 4 deletions website/src/repl/prebake.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycle
import './piano.mjs';
import './files.mjs';

const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;

export async function prebake() {
// https://archive.org/details/SalamanderGrandPianoV3
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
Expand All @@ -14,16 +17,16 @@ export async function prebake() {
// => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically
// seems to be a problem with soundfont2
import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
samples(`./piano.json`, `./piano/`, { prebake: true }),
samples(`${baseNoTrailing}/piano.json`, `./piano/`, { prebake: true }),
// https://github.com/sgossner/VCSL/
// https://api.github.com/repositories/126427031/contents/
// LICENSE: CC0 general-purpose
samples(`./vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }),
samples(`./tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', {
samples(`${baseNoTrailing}/vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }),
samples(`${baseNoTrailing}/tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', {
prebake: true,
tag: 'drum-machines',
}),
samples(`./EmuSP12.json`, `./EmuSP12/`, { prebake: true, tag: 'drum-machines' }),
samples(`${baseNoTrailing}/EmuSP12.json`, `${baseNoTrailing}/EmuSP12/`, { prebake: true, tag: 'drum-machines' }),
samples(
{
casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'],
Expand Down