Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions frontend/src/components/DocShell.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> -->
<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -->

<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from "vue";
import { RouterLink } from "vue-router";
import { sidebarFor } from "../content/routes";
import { locale } from "../preferences";
import { site } from "../site";

/**
* The frame of every documentation page: the topics of the site on the left, the page in a readable column, the
* headings of the page on the right, and the previous and next topics under it. The headings are read from the
* rendered page, so a page only has to write its h2 elements.
*/
const props = defineProps<{ path: string; editPath: string }>();

const labels = {
"en-US": {
contents: "Contents",
onPage: "On this page",
previous: "Previous",
next: "Next",
edit: "Edit this page",
docs: "Documentation",
},
"de-DE": {
contents: "Inhalt",
onPage: "Auf dieser Seite",
previous: "Zurück",
next: "Weiter",
edit: "Diese Seite bearbeiten",
docs: "Dokumentation",
},
"ru-RU": {
contents: "Содержание",
onPage: "На этой странице",
previous: "Назад",
next: "Далее",
edit: "Изменить страницу",
docs: "Документация",
},
"he-IL": {
contents: "תוכן",
onPage: "בעמוד הזה",
previous: "הקודם",
next: "הבא",
edit: "עריכת העמוד",
docs: "תיעוד",
},
} as const;
const copy = computed(() => labels[locale.value]);
const brand = { language: "Tala", talac: "Talac", tazel: "Tazel" }[site];

const sidebar = computed(() => sidebarFor(site, locale.value));
const position = computed(() => sidebar.value.findIndex((entry) => entry.path === props.path));
const current = computed(() => sidebar.value[position.value]);
const previous = computed(() =>
position.value > 0 ? sidebar.value[position.value - 1] : undefined,
);
const next = computed(() =>
position.value >= 0 && position.value < sidebar.value.length - 1
? sidebar.value[position.value + 1]
: undefined,
);
const editUrl = computed(
() => `https://github.com/Progmasoft/website/blob/main/frontend/${props.editPath}`,
);

const article = ref<HTMLElement>();
const contents = ref<HTMLDetailsElement>();
const headings = ref<{ id: string; text: string }[]>([]);

/** A fragment for a heading: its words, in any script, joined by hyphens. */
function slug(text: string): string {
return text
.toLowerCase()
.replace(/[^\p{L}\p{N}]+/gu, "-")
.replace(/^-+|-+$/g, "");
}

async function collectHeadings() {
await nextTick();
const found: { id: string; text: string }[] = [];
for (const heading of article.value?.querySelectorAll("h2") ?? []) {
const text = heading.textContent?.trim() ?? "";
if (!text) continue;
let id = slug(text) || `section-${found.length + 1}`;
while (found.some((entry) => entry.id === id)) id = `${id}-${found.length + 1}`;
heading.id = id;
found.push({ id, text });
}
headings.value = found;
}

onMounted(() => {
// On a narrow screen the topics fold away above the page; on a wide one they stay open beside it.
if (contents.value && window.matchMedia("(max-width: 760px)").matches)
contents.value.open = false;
collectHeadings();
});
watch([locale, () => props.path], collectHeadings);
</script>

<template>
<div class="docs">
<aside class="docs-nav">
<details ref="contents" class="docs-nav-toggle" open>
<summary>{{ copy.contents }}</summary>
<nav :aria-label="copy.docs">
<p class="docs-nav-site">{{ brand }}</p>
<RouterLink
v-for="entry in sidebar"
:key="entry.path"
:to="entry.path"
:aria-current="entry.path === path ? 'page' : undefined"
>{{ entry.label }}</RouterLink
>
<a v-if="site === 'language'" href="https://github.com/Progmasoft/tala/tree/main/Spec"
>Specification ↗</a
>
</nav>
</details>
</aside>

<div class="docs-main">
<nav class="docs-crumbs" aria-label="Breadcrumb">
<RouterLink to="/">{{ brand }}</RouterLink>
<span aria-hidden="true">/</span>
<span>{{ copy.docs }}</span>
<template v-if="current">
<span aria-hidden="true">/</span>
<span aria-current="page">{{ current.label }}</span>
</template>
</nav>
<article ref="article" class="doc-content">
<slot />
</article>
<nav class="docs-pager" :aria-label="`${copy.previous} / ${copy.next}`">
<RouterLink v-if="previous" class="docs-pager-previous" :to="previous.path">
<span>{{ copy.previous }}</span>
<strong>{{ previous.label }}</strong>
</RouterLink>
<RouterLink v-if="next" class="docs-pager-next" :to="next.path">
<span>{{ copy.next }}</span>
<strong>{{ next.label }}</strong>
</RouterLink>
</nav>
<p class="docs-edit">
<a :href="editUrl" target="_blank" rel="noopener noreferrer">{{ copy.edit }} ↗</a>
</p>
</div>

<aside v-if="headings.length > 1" class="docs-toc">
<p>{{ copy.onPage }}</p>
<a v-for="heading in headings" :key="heading.id" :href="`#${heading.id}`">{{
heading.text
}}</a>
</aside>
</div>
</template>
76 changes: 32 additions & 44 deletions frontend/src/pages/DocPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,46 @@

<script setup lang="ts">
import { computed } from "vue";
import { RouterLink } from "vue-router";
import { sidebarFor } from "../content/routes";
import DocShell from "../components/DocShell.vue";
import type { DocTopic } from "../content/types";
import { locale } from "../preferences";
import { site } from "../site";

const props = defineProps<{ topic: DocTopic; path: string }>();
const page = computed(() => props.topic[locale.value]);
const sidebar = computed(() => sidebarFor(site, locale.value));
const kicker = { language: "TALA", talac: "TALAC", tazel: "TAZEL" }[site];
// The file a page of each site is written in, for its "Edit this page" link.
const source = {
language: "src/content/syntax.ts",
talac: "src/content/talac.ts",
tazel: "src/content/tazel.ts",
}[site];
</script>

<template>
<div class="doc-layout section-wrap">
<aside class="doc-sidebar">
<span class="kicker">{{ kicker }}</span
><RouterLink
v-for="entry in sidebar"
:key="entry.path"
:to="entry.path"
:aria-current="entry.path === path ? 'page' : undefined"
>{{ entry.label }}</RouterLink
><a v-if="site === 'language'" href="https://github.com/Progmasoft/tala/tree/main/Spec"
>Specification ↗</a
>
</aside>
<article class="doc-content">
<span class="kicker">{{ page.section }}</span>
<h1>{{ page.heading }}</h1>
<p class="doc-lead">{{ page.lead }}</p>
<template v-for="(block, index) in page.blocks" :key="index">
<h2 v-if="block.kind === 'heading'">{{ block.text }}</h2>
<h3 v-else-if="block.kind === 'subheading'">{{ block.text }}</h3>
<p v-else-if="block.kind === 'text'">{{ block.text }}</p>
<pre
v-else-if="block.kind === 'code'"
class="doc-code"
dir="ltr"
><code>{{ block.code }}</code></pre>
<dl v-else-if="block.kind === 'terms'" class="doc-boundaries">
<div v-for="item in block.items" :key="item.term">
<dt dir="ltr">
<code>{{ item.term }}</code>
</dt>
<dd>{{ item.detail }}</dd>
</div>
</dl>
<div v-else-if="block.kind === 'note'" class="doc-note">
<h3>{{ block.title }}</h3>
<p>{{ block.text }}</p>
<DocShell :path="path" :edit-path="source">
<h1>{{ page.heading }}</h1>
<p class="doc-lead">{{ page.lead }}</p>
<template v-for="(block, index) in page.blocks" :key="index">
<h2 v-if="block.kind === 'heading'">{{ block.text }}</h2>
<h3 v-else-if="block.kind === 'subheading'">{{ block.text }}</h3>
<p v-else-if="block.kind === 'text'">{{ block.text }}</p>
<pre
v-else-if="block.kind === 'code'"
class="doc-code"
dir="ltr"
><code>{{ block.code }}</code></pre>
<dl v-else-if="block.kind === 'terms'" class="doc-boundaries">
<div v-for="item in block.items" :key="item.term">
<dt dir="ltr">
<code>{{ item.term }}</code>
</dt>
<dd>{{ item.detail }}</dd>
</div>
</template>
</article>
</div>
</dl>
<div v-else-if="block.kind === 'note'" class="doc-note">
<h3>{{ block.title }}</h3>
<p>{{ block.text }}</p>
</div>
</template>
</DocShell>
</template>
116 changes: 54 additions & 62 deletions frontend/src/pages/GettingStartedPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -->

<script setup lang="ts">
import DocShell from "../components/DocShell.vue";
import { computed } from "vue";
import { RouterLink } from "vue-router";
import { arrow, locale } from "../preferences";
Expand Down Expand Up @@ -256,69 +257,60 @@ public class HelloWorld {
</script>

<template>
<div class="doc-layout section-wrap">
<aside class="doc-sidebar">
<span class="kicker">TALA</span
><RouterLink to="/docs/overview">{{ c.navOverview }}</RouterLink
><RouterLink to="/docs/getting-started" aria-current="page">{{ c.navStart }}</RouterLink
><a href="https://github.com/Progmasoft/tala/tree/main/Spec">Specification ↗</a>
</aside>
<article class="doc-content">
<span class="kicker">{{ c.section }}</span>
<h1>{{ c.title }}</h1>
<p class="doc-lead">{{ c.lead }}</p>
<h2>{{ c.step1 }}</h2>
<p>{{ c.step1Text }}</p>
<pre
class="doc-code"
><code>git clone --recurse-submodules https://github.com/Progmasoft/tala.git</code></pre>
<h2>{{ c.step2 }}</h2>
<p>{{ c.step2Text }}</p>
<p>{{ c.platform }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/develop doctor</code></pre>
<h3>{{ c.bootstrap }}</h3>
<p>{{ c.toolchainHelp }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/prebuild check
<DocShell path="/docs/getting-started" edit-path="src/pages/GettingStartedPage.vue">
<h1>{{ c.title }}</h1>
<p class="doc-lead">{{ c.lead }}</p>
<h2>{{ c.step1 }}</h2>
<p>{{ c.step1Text }}</p>
<pre
class="doc-code"
><code>git clone --recurse-submodules https://github.com/Progmasoft/tala.git</code></pre>
<h2>{{ c.step2 }}</h2>
<p>{{ c.step2Text }}</p>
<p>{{ c.platform }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/develop doctor</code></pre>
<h3>{{ c.bootstrap }}</h3>
<p>{{ c.toolchainHelp }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/prebuild check
go run ./helpers/cmd/prebuild install</code></pre>
<h2>{{ c.step3 }}</h2>
<p>{{ c.step3Text }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/develop build
<h2>{{ c.step3 }}</h2>
<p>{{ c.step3Text }}</p>
<pre class="doc-code"><code>go run ./helpers/cmd/develop build
go run ./helpers/cmd/develop test</code></pre>
<h2>{{ c.step4 }}</h2>
<p>{{ c.step4Text }}</p>
<pre class="doc-code"><code>{{ hello }}</code></pre>
<h3>{{ c.fileCheck }}</h3>
<pre
class="doc-code"
><code>talac Examples/HelloWorld/HelloWorld.tala --emit=metadata</code></pre>
<h3>{{ c.fileBuild }}</h3>
<pre
class="doc-code"
><code>talac Examples/HelloWorld/HelloWorld.tala --entry=Examples.HelloWorld.HelloWorld</code></pre>
<p>{{ c.subset }}</p>
<h2>{{ c.step5 }}</h2>
<p>{{ c.step5Text }}</p>
<pre class="doc-code"><code>talac --help
<h2>{{ c.step4 }}</h2>
<p>{{ c.step4Text }}</p>
<pre class="doc-code"><code>{{ hello }}</code></pre>
<h3>{{ c.fileCheck }}</h3>
<pre
class="doc-code"
><code>talac Examples/HelloWorld/HelloWorld.tala --emit=metadata</code></pre>
<h3>{{ c.fileBuild }}</h3>
<pre
class="doc-code"
><code>talac Examples/HelloWorld/HelloWorld.tala --entry=Examples.HelloWorld.HelloWorld</code></pre>
<p>{{ c.subset }}</p>
<h2>{{ c.step5 }}</h2>
<p>{{ c.step5Text }}</p>
<pre class="doc-code"><code>talac --help
tazel help</code></pre>
<div class="doc-resource-list">
<a v-for="link in c.links" :key="link.href" :href="link.href">
<strong>{{ link.title }} <span aria-hidden="true">↗</span></strong>
<span>{{ link.detail }}</span>
</a>
</div>
<div class="doc-note">
<h3>{{ c.note }}</h3>
<p>{{ c.noteText }}</p>
</div>
<div class="doc-actions">
<RouterLink class="button button-primary" to="/docs/overview"
>{{ c.more }} {{ arrow }}</RouterLink
><a class="inline-link" href="https://github.com/Progmasoft/tala/tree/main/Spec"
>{{ c.spec }} ↗</a
><a class="inline-link" href="https://github.com/Progmasoft/tala/tree/main/Examples"
>{{ c.examples }} ↗</a
>
</div>
</article>
</div>
<div class="doc-resource-list">
<a v-for="link in c.links" :key="link.href" :href="link.href">
<strong>{{ link.title }} <span aria-hidden="true">↗</span></strong>
<span>{{ link.detail }}</span>
</a>
</div>
<div class="doc-note">
<h3>{{ c.note }}</h3>
<p>{{ c.noteText }}</p>
</div>
<div class="doc-actions">
<RouterLink class="button button-primary" to="/docs/overview"
>{{ c.more }} {{ arrow }}</RouterLink
><a class="inline-link" href="https://github.com/Progmasoft/tala/tree/main/Spec"
>{{ c.spec }} ↗</a
><a class="inline-link" href="https://github.com/Progmasoft/tala/tree/main/Examples"
>{{ c.examples }} ↗</a
>
</div>
</DocShell>
</template>
Loading
Loading