1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2024-11-25 23:01:08 +00:00

Add markdown syntax for article links

This commit is contained in:
Felix Ableitner 2024-02-15 12:30:51 +01:00
parent 5ab62aecd5
commit 6e09d4881f
5 changed files with 98 additions and 19 deletions

View file

@ -13,7 +13,7 @@ You can start by reading the main page which is rendered from Markdown. In the "
To continue, register a new account and you are logged in immediately without further confirmation. If you are admin of a newly created instance, login to the automatically created admin account instead. Login details are specified in the config file (by default user `ibis` and password also `ibis`). To continue, register a new account and you are logged in immediately without further confirmation. If you are admin of a newly created instance, login to the automatically created admin account instead. Login details are specified in the config file (by default user `ibis` and password also `ibis`).
On a new instance only the default Main Page will be shown. Use "Create Article" to create a new one. You have to enter the title, text and a summary of the edit. Afterwards press the submit button, and you are redirected to the new article. You can also make changes to existing articles with the "Edit" button at the top. If multiple users attempt to edit an article at the same time, Ibis will attempt to merge the changes automatically. If this is unsuccessful, the user has to perform a manual merge (again like in git). For remote articles, there is additionally a "Fork" option under the "Actions" tab. This allows copying a remote article including the full change history to the local instance. It can be useful if the original instance is dead, or if there are disagreements how the article should be written. On a new instance only the default Main Page will be shown. Use "Create Article" to create a new one. You have to enter the title, text and a summary of the edit. Afterwards press the submit button, and you are redirected to the new article. You can also make changes to existing articles with the "Edit" button at the top. The article text uses standard markdown. Additionally you can link to other articles with `[[Title@example.com]]`. If multiple users attempt to edit an article at the same time, Ibis will attempt to merge the changes automatically. If this is unsuccessful, the user has to perform a manual merge (again like in git). For remote articles, there is additionally a "Fork" option under the "Actions" tab. This allows copying a remote article including the full change history to the local instance. It can be useful if the original instance is dead, or if there are disagreements how the article should be written.
To kickstart federation, paste the domain of a remote instance into the search field, eg `https://example.com`. This will fetch the instance data over Activitypub, and also fetch all articles to make them available locally. The search page will show a link to the instance details page. Here you can follow the instance, so that new articles and edits are automatically federated to your local instance. You can also fetch individual articles from remote instances by pasting the URL into the search field. To kickstart federation, paste the domain of a remote instance into the search field, eg `https://example.com`. This will fetch the instance data over Activitypub, and also fetch all articles to make them available locally. The search page will show a link to the instance details page. Here you can follow the instance, so that new articles and edits are automatically federated to your local instance. You can also fetch individual articles from remote instances by pasting the URL into the search field.

View file

@ -1,5 +1,6 @@
use crate::common::LocalUserView; use crate::common::LocalUserView;
use crate::frontend::api::ApiClient; use crate::frontend::api::ApiClient;
use crate::frontend::backend_hostname;
use crate::frontend::components::nav::Nav; use crate::frontend::components::nav::Nav;
use crate::frontend::pages::article::actions::ArticleActions; use crate::frontend::pages::article::actions::ArticleActions;
use crate::frontend::pages::article::create::CreateArticle; use crate::frontend::pages::article::create::CreateArticle;
@ -65,15 +66,7 @@ impl GlobalState {
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
let backend_hostname; let backend_hostname = backend_hostname();
#[cfg(not(feature = "ssr"))]
{
backend_hostname = web_sys::window().unwrap().location().host().unwrap();
}
#[cfg(feature = "ssr")]
{
backend_hostname = crate::backend::config::IbisConfig::read().bind.to_string();
}
provide_meta_context(); provide_meta_context();
let backend_hostname = GlobalState { let backend_hostname = GlobalState {

80
src/frontend/markdown.rs Normal file
View file

@ -0,0 +1,80 @@
use crate::frontend::backend_hostname;
use markdown_it::parser::inline::{InlineRule, InlineState};
use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
pub fn markdown_parser() -> MarkdownIt {
let mut parser = MarkdownIt::new();
markdown_it::plugins::cmark::add(&mut parser);
markdown_it::plugins::extra::add(&mut parser);
parser.inline.add_rule::<ArticleLinkScanner>();
parser
}
#[derive(Debug)]
pub struct ArticleLink {
title: String,
domain: String,
}
// This defines how your custom node should be rendered.
impl NodeValue for ArticleLink {
fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
let mut attrs = node.attrs.clone();
let local = backend_hostname() == self.domain;
let link = if local {
format!("/article/{}", self.title)
} else {
format!("/article/{}@{}", self.title, self.domain)
};
attrs.push(("href", link));
fmt.open("a", &attrs);
fmt.text(&self.title);
fmt.close("a");
}
}
struct ArticleLinkScanner;
impl InlineRule for ArticleLinkScanner {
const MARKER: char = '[';
/// Find `[[Title@example.com]], return the position and split title/domain.
fn run(state: &mut InlineState) -> Option<(Node, usize)> {
let input = &state.src[state.pos..state.pos_max];
if !input.starts_with("[[") {
return None;
}
const SEPARATOR_LENGTH: usize = 2;
input.find("]]").and_then(|i| {
let start = state.pos + SEPARATOR_LENGTH;
let content = &state.src[start..i];
content.split_once('@').map(|(title, domain)| {
let node = Node::new(ArticleLink {
title: title.to_string(),
domain: domain.to_string(),
});
(node, i + SEPARATOR_LENGTH)
})
})
}
}
#[test]
fn test_markdown_local_article_link() {
let parser = markdown_parser();
let rendered = parser.parse("[[Title@127.0.0.1:8081]]").render();
assert_eq!("<p><a href=\"/article/Title\">Title</a></p>\n", rendered);
}
#[test]
fn test_markdown_remote_article_link() {
let parser = markdown_parser();
let rendered = parser.parse("[[Title@example.com]]").render();
assert_eq!(
"<p><a href=\"/article/Title@example.com\">Title</a></p>\n",
rendered
);
}

View file

@ -6,6 +6,7 @@ pub mod api;
pub mod app; pub mod app;
mod components; mod components;
pub mod error; pub mod error;
pub mod markdown;
pub mod pages; pub mod pages;
#[cfg(feature = "hydrate")] #[cfg(feature = "hydrate")]
@ -47,3 +48,16 @@ fn user_link(person: &DbPerson) -> impl IntoView {
<a href={creator_path}>{user_title(person)}</a> <a href={creator_path}>{user_title(person)}</a>
} }
} }
fn backend_hostname() -> String {
let backend_hostname;
#[cfg(not(feature = "ssr"))]
{
backend_hostname = web_sys::window().unwrap().location().host().unwrap();
}
#[cfg(feature = "ssr")]
{
backend_hostname = crate::backend::config::IbisConfig::read().bind.to_string();
}
backend_hostname
}

View file

@ -1,10 +1,9 @@
use crate::frontend::article_title; use crate::frontend::article_title;
use crate::frontend::components::article_nav::ArticleNav; use crate::frontend::components::article_nav::ArticleNav;
use crate::frontend::markdown::markdown_parser;
use crate::frontend::pages::article_resource; use crate::frontend::pages::article_resource;
use leptos::*; use leptos::*;
use markdown_it::MarkdownIt;
#[component] #[component]
pub fn ReadArticle() -> impl IntoView { pub fn ReadArticle() -> impl IntoView {
let article = article_resource(); let article = article_resource();
@ -24,10 +23,3 @@ pub fn ReadArticle() -> impl IntoView {
</Suspense> </Suspense>
} }
} }
fn markdown_parser() -> MarkdownIt {
let mut parser = MarkdownIt::new();
markdown_it::plugins::cmark::add(&mut parser);
markdown_it::plugins::extra::add(&mut parser);
parser
}