website: fix broken links across docs and pages

Render glossary tooltips through the shared replaceLink transform so
their relative links become absolute site URLs instead of 404ing when
injected across pages; add the English-only routes (downloads, faq,
jobs, reproduce, security, transparency) to supportedRoutes so the
language switcher stops emitting href="undefined"; passthrough-copy
docs/guide/images, docs/guide/diagrams and docs/themes.

Fix the SIMPLEX.md p2p anchor typo and correct image paths in CLI,
the v0.4 blog post and the fr/pl translation docs to point at the
existing shared assets.
This commit is contained in:
shum
2026-07-14 12:38:09 +00:00
parent a85b958e1d
commit 949c6ce65e
8 changed files with 61 additions and 50 deletions
+2 -2
View File
@@ -30,13 +30,13 @@ To create a group use the `/g <group>` command. You can then invite contacts to
**Please note:** Groups are not stored on any server; they are maintained as a list of members in the app database. Sending a message to the group sends a message to each member of the group.
![simplex-chat](../images/groups.gif)
![simplex-chat](/images/groups.gif)
### File transfer
Sharing files is simple! To send a file to a contact, use the `/f @<contact> <file_path>` command. The recipient will have to accept before the file is sent.
![simplex-chat](../images/files.gif)
![simplex-chat](/images/files.gif)
## We're always looking for help!
+3 -3
View File
@@ -220,7 +220,7 @@ Use `/help` in chat to see the list of available commands.
To create a group use `/g <group>`, then add contacts to it with `/a <group> <name>`. You can then send messages to the group by entering `#<group> <message>`. Use `/help groups` for other commands.
![simplex-chat](../images/groups.gif)
![simplex-chat](/images/groups.gif)
> **Please note**: the groups are not stored on any server, they are maintained as a list of members in the app database to whom the messages will be sent.
@@ -228,7 +228,7 @@ To create a group use `/g <group>`, then add contacts to it with `/a <group> <na
You can send a file to your contact with `/f @<contact> <file_path>` - the recipient will have to accept it before it is sent. Use `/help files` for other commands.
![simplex-chat](../images/files.gif)
![simplex-chat](/images/files.gif)
You can send files to a group with `/f #<group> <file_path>`.
@@ -242,4 +242,4 @@ User address is "long-term" in a sense that it is a multiple-use connection link
Use `/help address` for other commands.
![simplex-chat](../images/user-addresses.gif)
![simplex-chat](/images/user-addresses.gif)
+1 -1
View File
@@ -163,7 +163,7 @@ In the context of SimpleX network, these are the identifiers generated by SMP re
Peer-to-peer (P2P) is the network architecture when participants have equal rights and communicate directly via a general purpose transport or overlay network. Unlike client-server architecture, all peers in a P2P network both provide and consume the resources. In the context of messaging, P2P architecture usually means that the messages are sent between peers, without user accounts or messages being stored on any servers. Examples are Tox, Briar, Cwtch and many others.
The advantage is that the participants do not depend on any servers. There are [multiple downsides](./SIMPLEX.md#comparison-with-p2p9-messaging-protocols) to that architecture, such as no asynchronous message delivery, the need for network-wide peer addresses, possibility of network-wide attacks, that are usually mitigated only by using a centralized authority. These disadvantages are avoided with [proxied P2P](#proxied-peer-to-peer) architecture.
The advantage is that the participants do not depend on any servers. There are [multiple downsides](./SIMPLEX.md#comparison-with-p2p-messaging-protocols) to that architecture, such as no asynchronous message delivery, the need for network-wide peer addresses, possibility of network-wide attacks, that are usually mitigated only by using a centralized authority. These disadvantages are avoided with [proxied P2P](#proxied-peer-to-peer) architecture.
[Wikipedia](https://en.wikipedia.org/wiki/Peer-to-peer).
+2 -2
View File
@@ -26,7 +26,7 @@ Ce document est créé pour accélérer ce processus, et partager quelques astuc
2. Certaines des chaînes n'ont pas besoin d'être traduites, mais elles doivent quand même être copiées - il y a un bouton dans l'interface weblate pour cela :
<img src="/images/weblate_1.png" alt="weblate: copy source to translation" width="100%">
<img src="/docs/images/weblate_1.png" alt="weblate: copy source to translation" width="100%">
3. Weblate propose également des suggestions automatiques qui peuvent accélérer le processus. Parfois, elles peuvent être utilisées telles quelles, parfois elles nécessitent quelques retouches - cliquez pour les utiliser dans les traductions.
@@ -34,7 +34,7 @@ Ce document est créé pour accélérer ce processus, et partager quelques astuc
5. Quand vous traduisez [l'app iOS](https://hosted.weblate.org/projects/simplex-chat/ios/), la plupart des chaînes de caractères sont identiques, elles peuvent être copiées en un clic dans la section Glossaire. L'indice visuel que cela est possible est que la chaîne source entière est surlignée en jaune. De nombreuses autres chaînes sont très similaires, elles ne diffèrent que par la syntaxe d'interpolation ou la façon dont la police en gras est utilisée - elles ne nécessitent qu'une édition minimale. Certaines chaînes sont propres à la plate-forme iOS. Elles doivent être traduites séparément.
<img src="/images/weblate_2.png" alt="weblate: automatic suggestions" width="100%">
<img src="/docs/images/weblate_2.png" alt="weblate: automatic suggestions" width="100%">
## Une fois la traduction terminée
+1 -1
View File
@@ -482,4 +482,4 @@ Możliwe jest również udostępnienie adresu swojego serwera znajomym, pozwalaj
_Uwaga_: Do obsługi haseł wymagany jest serwer SMP w wersji 4.0. Jeśli już posiadasz serwer, możesz dodać hasło do niego poprzez wpisanie hasła do pliku INI serwera.
<img src="./server_config_1.png" width="288"> &nbsp;&nbsp; <img src="./server_config_2.png" width="288"> &nbsp;&nbsp; <img src="./server_config_3.png" width="288">
<img src="/docs/server_config_1.png" width="288"> &nbsp;&nbsp; <img src="/docs/server_config_2.png" width="288"> &nbsp;&nbsp; <img src="/docs/server_config_3.png" width="288">
+2 -2
View File
@@ -39,7 +39,7 @@ Kroki są następujące:
2. Niektóre ciągi nie wymagają tłumaczenia, ale nadal trzeba je skopiować - w interfejsie użytkownika weblate znajduje się odpowiedni przycisk:
<img src="./images/weblate_1.png" alt="weblate: copy source to translation" width="100%">
<img src="/docs/images/weblate_1.png" alt="weblate: copy source to translation" width="100%">
3. Weblate posiada również automatyczne sugestie, które mogą przyspieszyć ten proces. Czasami mogą być używane w niezmienionej formie, a czasami wymagają edycji - kliknij, aby użyć ich w tłumaczeniach.
@@ -68,7 +68,7 @@ My wtedy:
Serdecznie dziękujemy! To ogromny wysiłek i wielka pomoc dla rozwoju sieci SimpleX.
<img src="./images/weblate_2.png" alt="weblate: automatic suggestions" width="100%">
<img src="/docs/images/weblate_2.png" alt="weblate: automatic suggestions" width="100%">
## Częste błędy w tłumaczeniu
+3 -3
View File
@@ -139,7 +139,7 @@ To tyle - teraz możesz wykonywać połączenia audio i wideo za pośrednictwem
2. W sekcji **Build up ICE Server List** dodaj:
<img src="./stun_1.png">
<img src="/docs/stun_1.png">
- `STUN: stun:<twoje_ip_lub_domena>:<port>` kliknij `Add STUN`
- `TURN: turn:<twoje_ip_lub_domena>:<port>`, `Username: <twój_login>`, `Credential: <twoje_hasło>` kliknij `Add TURN`
@@ -148,11 +148,11 @@ To tyle - teraz możesz wykonywać połączenia audio i wideo za pośrednictwem
3. Powinieneś zobaczyć swoje serwery w sekcji **ICE server list**. Jeśli wszystko jest skonfigurowane poprawnie, naciśnij `Start test`:
<img src="./stun_2.png">
<img src="/docs/stun_2.png">
4. W sekcji **Results** powinieneś zobaczyć coś takiego:
<img src="./stun_3.png">
<img src="/docs/stun_3.png">
Jeśli wyniki pokazują `srflx` i `relay`, wszystko jest skonfigurowane poprawnie!
+47 -36
View File
@@ -21,10 +21,50 @@ linksData.forEach(entry => {
entry.imageExists = entry.image && fs.existsSync(path.join(linkImagesDir, entry.image))
})
// Rewrites relative markdown links to website permalinks or GitHub URLs.
// Shared by the main markdown renderer (markdownLib) and the glossary renderer
// below, so glossary tooltips injected into pages get absolute, working links.
function replaceLink(link, _env) {
let parsed = uri.parse(link)
if (parsed.scheme || parsed.host) return link
let hostFile = path.resolve(_env.page.inputPath)
let linkFile = path.resolve(hostFile, '..', parsed.path)
if (parsed.path.startsWith('/')) {
let srcIndex = hostFile.indexOf("/src")
if (srcIndex !== -1) {
linkFile = path.join(hostFile.slice(0, srcIndex + 4), parsed.path)
}
}
if (fs.existsSync(linkFile) && fs.statSync(linkFile).isFile()) {
// this condition works if the link is a valid website file
const fileContent = fs.readFileSync(linkFile, 'utf8')
parsed.path = (matter(fileContent).data?.permalink || parsed.path).replace(/\.md$/, ".html").toLowerCase()
} else if (!fs.existsSync(linkFile)) {
linkFile = linkFile.replace('/website/src', '')
if (fs.existsSync(linkFile)) {
// this condition works if the link is a valid project file
const githubUrl = "https://github.com/simplex-chat/simplex-chat/blob/stable"
const repoRoot = hostFile.slice(0, hostFile.indexOf('/website/src'))
const repoPath = linkFile.slice(repoRoot.length)
return `${githubUrl}${repoPath}`
} else {
// if the link is not a valid website file or project file
throw new Error(`Broken link: ${parsed.path} in ${hostFile}`)
}
}
return uri.serialize(parsed)
}
// The implementation of Glossary feature
const md = new markdownIt()
const md = new markdownIt({ replaceLink }).use(markdownItReplaceLink)
// Resolve the glossary's relative links (e.g. ./SIMPLEX.md) as if rendered from
// its built location, so tooltips embedded on other pages get absolute URLs.
const glossaryInputPath = path.resolve(__dirname, 'src/docs/GLOSSARY.md')
const glossaryMarkdownContent = fs.readFileSync(path.resolve(__dirname, '../docs/GLOSSARY.md'), 'utf8')
const glossaryHtmlContent = md.render(glossaryMarkdownContent)
const glossaryHtmlContent = md.render(glossaryMarkdownContent, { page: { inputPath: glossaryInputPath } })
const glossaryDOM = new JSDOM(glossaryHtmlContent)
const glossaryDocument = glossaryDOM.window.document
const glossary = require('./src/_data/glossary.json')
@@ -63,7 +103,7 @@ const globalConfig = {
}
const translationsDirectoryPath = './langs'
const supportedRoutes = ["blog", "contact", "invitation", "messaging", "docs", "fdroid", "why", "file", ""]
const supportedRoutes = ["blog", "contact", "invitation", "messaging", "docs", "fdroid", "why", "file", "downloads", "faq", "reproduce", "transparency", "security", "jobs", ""]
let supportedLangs = []
fs.readdir(translationsDirectoryPath, (err, files) => {
if (err) {
@@ -320,6 +360,9 @@ module.exports = function (ty) {
ty.addPassthroughCopy("src/blog/images")
ty.addPassthroughCopy("src/docs/*.png")
ty.addPassthroughCopy("src/docs/images")
ty.addPassthroughCopy("src/docs/guide/images")
ty.addPassthroughCopy("src/docs/guide/diagrams")
ty.addPassthroughCopy("src/docs/themes")
ty.addPassthroughCopy("src/docs/protocol/diagrams")
ty.addPassthroughCopy("src/docs/protocol/*.json")
ty.addPassthroughCopy("src/images")
@@ -418,39 +461,7 @@ module.exports = function (ty) {
html: true,
breaks: true,
linkify: true,
replaceLink: function (link, _env) {
let parsed = uri.parse(link)
if (parsed.scheme || parsed.host) return link
let hostFile = path.resolve(_env.page.inputPath)
let linkFile = path.resolve(hostFile, '..', parsed.path)
if (parsed.path.startsWith('/')) {
let srcIndex = hostFile.indexOf("/src")
if (srcIndex !== -1) {
linkFile = path.join(hostFile.slice(0, srcIndex + 4), parsed.path)
}
}
if (fs.existsSync(linkFile) && fs.statSync(linkFile).isFile()) {
// this condition works if the link is a valid website file
const fileContent = fs.readFileSync(linkFile, 'utf8')
parsed.path = (matter(fileContent).data?.permalink || parsed.path).replace(/\.md$/, ".html").toLowerCase()
} else if (!fs.existsSync(linkFile)) {
linkFile = linkFile.replace('/website/src', '')
if (fs.existsSync(linkFile)) {
// this condition works if the link is a valid project file
const githubUrl = "https://github.com/simplex-chat/simplex-chat/blob/stable"
const repoRoot = hostFile.slice(0, hostFile.indexOf('/website/src'))
const repoPath = linkFile.slice(repoRoot.length)
return `${githubUrl}${repoPath}`
} else {
// if the link is not a valid website file or project file
throw new Error(`Broken link: ${parsed.path} in ${hostFile}`)
}
}
return uri.serialize(parsed)
}
replaceLink: replaceLink
}).use(markdownItAnchor, {
slugify: (str) =>
slugify(str, {