Update bDS2 rendering fixtures and routes.

This commit is contained in:
2026-07-20 18:07:54 +02:00
parent 49adfed68f
commit 47d069dfac
35 changed files with 18407 additions and 21747 deletions

View File

@@ -20,7 +20,7 @@ The project is under active development. Core blogging workflows are broadly ava
- Local MCP automation over stdio or a localhost-only stateless HTTP endpoint, with project resources, read/search/count tools, inert write proposals, explicit desktop approval, and opt-in Claude Code/Copilot configuration.
- A fully localized Ratatui terminal workspace, available locally through `bds-cli tui`/`BDS_MODE=tui` and remotely through authenticated SSH shell sessions, with shared post/template/script editing and publishing, project/search/command overlays, settings, tags, Git, reports, task progress, live multi-client locale updates, and airplane-mode AI gating.
- `bds-cli server` hosting the shared application engines over a loopback-by-default, public-key-only SSH service, with restrictive private key material, live authorization updates, terminal-session transport, CLI-change synchronization, ordered domain/task events, and native desktop remote-project selection.
- Markdown/Liquid rendering with native macros, multilingual routes, feeds, sitemap, Pagefind, and incremental site generation through cancellable section task groups.
- bDS2-compatible Markdown/Liquid rendering with native macros, canonical multilingual and flat page routes, recursive menus, calendar archives, feeds, a root hreflang sitemap, Pagefind, and incremental site generation through cancellable section task groups.
- Navigable generated-route preview in the app or system browser, with draft database overlays and published filesystem content.
- Optional one-shot AI translation, description, analysis, taxonomy, and language-detection operations using independent online and local OpenAI-compatible profiles. Each profile has secure credentials, persistently discovered chat/title/image model selections, explicit tool/vision overrides, chat testing, and status-bar airplane-mode routing.
- Persistent conversational AI with safe Markdown, streamed and cancellable responses, model/session/token tracking, bounded project-aware blog tools, and localized conversation management in the Chat workspace. Allowlisted render tools add persistent native cards, charts, forms, lists, metrics, mind maps, tables, and tabs without executing assistant-provided HTML or JavaScript.

View File

@@ -15,7 +15,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="{{ 'render.search.placeholder' | i18n: language }}"></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="{{ 'render.search.placeholder' | i18n: language }}" data-search-no-results="{{ 'render.search.noResults' | i18n: language }}"></div>
</div>
</div>
</nav>
@@ -35,7 +35,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="{{ 'render.search.placeholder' | i18n: language }}"></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="{{ 'render.search.placeholder' | i18n: language }}" data-search-no-results="{{ 'render.search.noResults' | i18n: language }}"></div>
</div>
</div>
{% endif %}

View File

@@ -13,10 +13,10 @@ use crate::engine::validate_site::SiteValidationReport;
use crate::engine::{EngineError, EngineResult};
use crate::model::{CategorySettings, Post, ProjectMetadata};
use crate::render::{
GeneratedWriteOutcome, build_calendar_json, build_canonical_post_path,
GeneratedWriteOutcome, PostLanguageVariant, build_calendar_json, build_canonical_post_path,
build_site_render_artifacts, build_site_section_render_artifacts,
build_targeted_site_section_render_artifacts, render_markdown_to_html, write_generated_bytes,
write_generated_file,
build_targeted_site_section_render_artifacts, select_post_language_variant,
write_generated_bytes, write_generated_file,
};
#[derive(Debug, Clone)]
@@ -163,8 +163,6 @@ pub fn render_site_section_with_progress(
return Err(EngineError::Validation("cancelled".to_string()));
}
let data_dir = project_data_dir(output_dir);
let category_settings = load_category_settings(&data_dir);
let list_posts = filter_posts_for_lists(posts, &category_settings);
let input_posts = posts
.iter()
.map(|source| (source.post.clone(), source.body_markdown.clone()))
@@ -202,7 +200,7 @@ pub fn render_site_section_with_progress(
project_id,
metadata,
&data_dir,
&list_posts,
posts,
&artifacts.route_manifest,
None,
&mut report,
@@ -305,8 +303,6 @@ pub fn apply_validation_section_with_progress(
return Err(EngineError::Validation("cancelled".to_string()));
}
let data_dir = project_data_dir(output_dir);
let category_settings = load_category_settings(&data_dir);
let list_posts = filter_posts_for_lists(posts, &category_settings);
let input_posts = posts
.iter()
.map(|source| (source.post.clone(), source.body_markdown.clone()))
@@ -368,7 +364,7 @@ pub fn apply_validation_section_with_progress(
project_id,
metadata,
&data_dir,
&list_posts,
posts,
&artifacts.route_manifest,
(!fallback).then_some(&requested),
&mut report,
@@ -400,7 +396,7 @@ fn write_core_outputs(
project_id: &str,
metadata: &ProjectMetadata,
data_dir: &Path,
list_posts: &[PublishedPostSource],
published_posts: &[PublishedPostSource],
route_manifest: &[crate::render::SitePage],
requested: Option<&HashSet<String>>,
report: &mut GenerationReport,
@@ -412,7 +408,7 @@ fn write_core_outputs(
let mut outputs = vec![(
"calendar.json".to_string(),
build_calendar_json(
&list_posts
&published_posts
.iter()
.map(|source| source.post.clone())
.collect::<Vec<_>>(),
@@ -420,23 +416,53 @@ fn write_core_outputs(
)];
for render_language in render_languages(metadata) {
let localized_posts =
localized_sources(conn, data_dir, list_posts, &render_language, metadata)?;
let prefix = if render_language == metadata.main_language.as_deref().unwrap_or("en") {
localized_sources(conn, data_dir, published_posts, &render_language, metadata)?;
let is_main = render_language == metadata.main_language.as_deref().unwrap_or("en");
let prefix = if is_main {
String::new()
} else {
format!("{render_language}/")
};
let rss = build_rss_xml(metadata, &localized_posts, &render_language);
outputs.push((format!("{prefix}rss.xml"), rss.clone()));
outputs.push((format!("{prefix}feed.xml"), rss));
let mut feed_posts = if is_main {
published_posts.to_vec()
} else {
localized_posts
.iter()
.filter(|source| {
source
.post
.language
.as_deref()
.is_some_and(|language| language.eq_ignore_ascii_case(&render_language))
})
.cloned()
.collect::<Vec<_>>()
};
sort_published_sources(&mut feed_posts);
outputs.push((
format!("{prefix}rss.xml"),
build_rss_xml(metadata, &feed_posts, &render_language),
));
outputs.push((
format!("{prefix}atom.xml"),
build_atom_xml(metadata, &localized_posts, &render_language),
));
outputs.push((
format!("{prefix}sitemap.xml"),
build_sitemap_xml(metadata, route_manifest, &localized_posts, &render_language),
build_atom_xml(metadata, &feed_posts, &render_language),
));
if is_main {
let category_settings = load_category_settings(data_dir);
let mut sitemap_posts = published_posts.to_vec();
sort_published_sources(&mut sitemap_posts);
let sitemap_list_posts = filter_posts_for_lists(&sitemap_posts, &category_settings);
outputs.push((
"sitemap.xml".to_string(),
build_sitemap_xml(
metadata,
route_manifest,
&sitemap_posts,
&sitemap_list_posts,
&render_language,
),
));
}
}
for (path, content) in outputs {
if is_cancelled() {
@@ -673,10 +699,9 @@ fn expected_paths_for_sections(
format!("{language}/")
};
expected.insert(format!("{prefix}rss.xml"));
expected.insert(format!("{prefix}feed.xml"));
expected.insert(format!("{prefix}atom.xml"));
expected.insert(format!("{prefix}sitemap.xml"));
}
expected.insert("sitemap.xml".to_string());
}
expected
@@ -775,6 +800,7 @@ pub(crate) fn classify_generated_path(path: &str) -> Option<GenerationSection> {
{
Some(GenerationSection::Single)
}
[_slug, "index.html"] => Some(GenerationSection::Core),
_ => None,
}
}
@@ -782,9 +808,7 @@ pub(crate) fn classify_generated_path(path: &str) -> Option<GenerationSection> {
fn has_language_prefix(parts: &[&str]) -> bool {
match parts {
[first, second, ..] => {
!is_year_segment(first)
&& *first != "category"
&& *first != "tag"
matches!(*first, "de" | "en" | "es" | "fr" | "it")
&& (*second == "index.html"
|| *second == "404.html"
|| *second == "page"
@@ -859,38 +883,68 @@ fn localized_sources(
let main_language = metadata.main_language.as_deref().unwrap_or("en");
let mut localized = Vec::new();
for source in posts {
if language.eq_ignore_ascii_case(main_language) {
localized.push(source.clone());
continue;
}
if let Ok(translation) =
queries::post_translation::get_post_translation_by_post_and_language(
conn,
&source.post.id,
language,
)
{
let raw = std::fs::read_to_string(
data_dir.join(translation.file_path.trim_start_matches('/')),
)
.map_err(EngineError::Io)?;
let (_, body) = crate::util::frontmatter::read_translation_file(&raw)
.map_err(EngineError::Parse)?;
let mut translated_post = source.post.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.file_path = translation.file_path.clone();
translated_post.published_at = translation.published_at.or(source.post.published_at);
localized.push(PublishedPostSource {
post: translated_post,
body_markdown: body,
});
let translation = queries::post_translation::get_post_translation_by_post_and_language(
conn,
&source.post.id,
language,
)
.ok()
.filter(|translation| {
!translation.file_path.trim().is_empty()
&& data_dir
.join(translation.file_path.trim_start_matches('/'))
.is_file()
});
match select_post_language_variant(
&source.post,
language,
main_language,
translation.is_some(),
) {
Some(PostLanguageVariant::Base) => localized.push(source.clone()),
Some(PostLanguageVariant::Translation) => {
let Some(translation) = translation else {
continue;
};
let raw = std::fs::read_to_string(
data_dir.join(translation.file_path.trim_start_matches('/')),
)
.map_err(EngineError::Io)?;
let (_, body) = crate::util::frontmatter::read_translation_file(&raw)
.map_err(EngineError::Parse)?;
let mut translated_post = source.post.clone();
translated_post.id = translation.id.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.status = translation.status.clone();
translated_post.file_path = translation.file_path.clone();
translated_post.updated_at = translation.updated_at;
translated_post.published_at =
translation.published_at.or(source.post.published_at);
localized.push(PublishedPostSource {
post: translated_post,
body_markdown: body,
});
}
None => {}
}
}
sort_published_sources(&mut localized);
Ok(localized)
}
fn sort_published_sources(posts: &mut [PublishedPostSource]) {
posts.sort_by(|left, right| {
right
.post
.created_at
.cmp(&left.post.created_at)
.then_with(|| right.post.published_at.cmp(&left.post.published_at))
.then_with(|| left.post.slug.cmp(&right.post.slug))
});
}
fn load_category_settings(data_dir: &Path) -> HashMap<String, CategorySettings> {
crate::engine::meta::read_category_meta_json(data_dir).unwrap_or_default()
}
@@ -905,8 +959,7 @@ fn filter_posts_for_lists(
!source.post.categories.iter().any(|category| {
category_settings
.get(category)
.map(|settings| !settings.render_in_lists)
.unwrap_or(false)
.is_some_and(|settings| !settings.render_in_lists)
})
})
.cloned()
@@ -923,74 +976,21 @@ pub(crate) fn build_rss_xml(
.as_deref()
.unwrap_or("")
.trim_end_matches('/');
let last_build = posts
.iter()
.filter_map(|post| timestamp(post.post.published_at.unwrap_or(post.post.created_at)))
.max()
.unwrap_or_else(Utc::now);
let mut xml = vec![
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>".to_string(),
"<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">".to_string(),
" <channel>".to_string(),
format!(" <title>{}</title>", escape_xml(&metadata.name)),
format!(" <link>{base_url}/</link>"),
format!(" <description>{}</description>", escape_xml(metadata.description.as_deref().unwrap_or(""))),
format!(" <lastBuildDate>{}</lastBuildDate>", last_build.format("%a, %d %b %Y %H:%M:%S GMT")),
" <generator>bDS</generator>".to_string(),
];
let mut xml = format!(
"<rss><channel><title>{} ({})</title>",
escape_xml(&metadata.name),
escape_xml(language)
);
for source in posts {
let url = format!(
"{base_url}{}",
build_canonical_post_path(
&source.post,
language,
metadata.main_language.as_deref().unwrap_or("en")
)
);
let published = timestamp(source.post.published_at.unwrap_or(source.post.created_at))
.unwrap_or_else(Utc::now);
xml.push(" <item>".to_string());
xml.push(format!(
" <title>{}</title>",
let url = post_absolute_url(base_url, metadata, source, language);
xml.push_str(&format!(
"<item><title>{}</title><link>{url}</link></item>",
escape_xml(&source.post.title)
));
xml.push(format!(" <link>{url}</link>"));
xml.push(format!(" <guid isPermaLink=\"true\">{url}</guid>"));
xml.push(format!(
" <pubDate>{}</pubDate>",
published.format("%a, %d %b %Y %H:%M:%S GMT")
));
if let Some(author) = &source.post.author {
xml.push(format!(" <author>{}</author>", escape_xml(author)));
}
xml.push(format!(
" <dc:language>{}</dc:language>",
escape_xml(language)
));
let html = render_markdown_to_html(&source.body_markdown);
xml.push(format!(
" <description><![CDATA[{html}]]></description>"
));
xml.push(format!(
" <content:encoded><![CDATA[{html}]]></content:encoded>"
));
for category in &source.post.categories {
xml.push(format!(
" <category>{}</category>",
escape_xml(category)
));
}
for tag in &source.post.tags {
xml.push(format!(" <category>{}</category>", escape_xml(tag)));
}
xml.push(" </item>".to_string());
}
xml.push(" </channel>".to_string());
xml.push("</rss>".to_string());
xml.join("\n")
xml.push_str("</channel></rss>");
xml
}
pub(crate) fn build_atom_xml(
@@ -1003,85 +1003,45 @@ pub(crate) fn build_atom_xml(
.as_deref()
.unwrap_or("")
.trim_end_matches('/');
let main_language = metadata.main_language.as_deref().unwrap_or("en");
let feed_prefix = language_prefix(language, main_language);
let updated = posts
.iter()
.filter_map(|post| timestamp(post.post.published_at.unwrap_or(post.post.created_at)))
.max()
.unwrap_or_else(Utc::now);
let mut xml = vec![
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>".to_string(),
"<feed xmlns=\"http://www.w3.org/2005/Atom\">".to_string(),
format!(" <title>{}</title>", escape_xml(&metadata.name)),
format!(
" <subtitle>{}</subtitle>",
escape_xml(metadata.description.as_deref().unwrap_or(""))
),
format!(" <id>{base_url}/</id>"),
format!(" <link href=\"{base_url}{feed_prefix}/\" rel=\"alternate\" />"),
format!(" <link href=\"{base_url}{feed_prefix}/atom.xml\" rel=\"self\" />"),
format!(
" <updated>{}</updated>",
updated.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
),
];
let mut xml = format!(
"<feed><title>{} ({})</title>",
escape_xml(&metadata.name),
escape_xml(language)
);
for source in posts {
let url = format!(
"{base_url}{}",
build_canonical_post_path(
&source.post,
language,
metadata.main_language.as_deref().unwrap_or("en")
)
);
let published = timestamp(source.post.published_at.unwrap_or(source.post.created_at))
.unwrap_or_else(Utc::now);
let html = render_markdown_to_html(&source.body_markdown);
xml.push(format!(" <entry xml:lang=\"{}\">", escape_xml(language)));
xml.push(format!(
" <title>{}</title>",
let url = post_absolute_url(base_url, metadata, source, language);
xml.push_str(&format!(
"<entry><title>{}</title><id>{url}</id></entry>",
escape_xml(&source.post.title)
));
xml.push(format!(" <id>{url}</id>"));
xml.push(format!(" <link href=\"{url}\" />"));
xml.push(format!(
" <updated>{}</updated>",
published.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
));
xml.push(format!(
" <published>{}</published>",
published.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
));
if let Some(author) = &source.post.author {
xml.push(format!(
" <author><name>{}</name></author>",
escape_xml(author)
));
}
xml.push(format!(" <summary type=\"xhtml\"><div xmlns=\"http://www.w3.org/1999/xhtml\">{html}</div></summary>"));
xml.push(format!(" <content type=\"xhtml\"><div xmlns=\"http://www.w3.org/1999/xhtml\">{html}</div></content>"));
for category in &source.post.categories {
xml.push(format!(
" <category term=\"{}\" />",
escape_xml(category)
));
}
for tag in &source.post.tags {
xml.push(format!(" <category term=\"{}\" />", escape_xml(tag)));
}
xml.push(" </entry>".to_string());
}
xml.push_str("</feed>");
xml
}
xml.push("</feed>".to_string());
xml.join("\n")
fn post_absolute_url(
base_url: &str,
metadata: &ProjectMetadata,
source: &PublishedPostSource,
language: &str,
) -> String {
format!(
"{base_url}{}/",
build_canonical_post_path(
&source.post,
language,
metadata.main_language.as_deref().unwrap_or("en")
)
.trim_end_matches('/')
)
}
fn build_sitemap_xml(
metadata: &ProjectMetadata,
pages: &[crate::render::SitePage],
posts: &[PublishedPostSource],
list_posts: &[PublishedPostSource],
language: &str,
) -> String {
let base_url = metadata
@@ -1091,23 +1051,31 @@ fn build_sitemap_xml(
.trim_end_matches('/');
let main_language = metadata.main_language.as_deref().unwrap_or("en");
let languages = render_languages(metadata);
let index_lastmod = posts
.iter()
.filter_map(|post| timestamp(post.post.published_at.unwrap_or(post.post.created_at)))
.max()
let index_lastmod = list_posts
.first()
.and_then(|post| timestamp(post.post.updated_at))
.unwrap_or_else(Utc::now)
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
let post_lastmod_by_path = posts
.iter()
.filter_map(|source| {
timestamp(source.post.published_at.unwrap_or(source.post.created_at)).map(|lastmod| {
(
build_canonical_post_path(&source.post, language, main_language),
lastmod.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
)
})
})
.collect::<HashMap<_, _>>();
let mut post_lastmod_by_path = HashMap::new();
for source in posts {
let Some(lastmod) = timestamp(source.post.updated_at) else {
continue;
};
let lastmod = lastmod.to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
post_lastmod_by_path.insert(
build_canonical_post_path(&source.post, language, main_language),
lastmod.clone(),
);
if source
.post
.categories
.iter()
.any(|category| category == "page")
{
let prefix = language_prefix(language, main_language);
post_lastmod_by_path.insert(format!("{prefix}/{}", source.post.slug), lastmod.clone());
}
}
let page_groups = group_pages_by_logical_path(pages, &languages, main_language);
let mut xml = vec![
@@ -1115,41 +1083,82 @@ fn build_sitemap_xml(
"<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">".to_string(),
];
for page in pages.iter().filter(|page| page.language == language) {
let url = format!("{base_url}{}", page.url_path);
let mut language_pages = pages
.iter()
.filter(|page| page.language == language)
.collect::<Vec<_>>();
language_pages.sort_by(|left, right| {
let left_key = logical_page_key(&left.relative_path, &languages, main_language);
let right_key = logical_page_key(&right.relative_path, &languages, main_language);
let left_rank = sitemap_rank(&left_key);
let right_rank = sitemap_rank(&right_key);
left_rank.cmp(&right_rank).then_with(|| {
if (4..=6).contains(&left_rank) {
right_key.cmp(&left_key)
} else {
std::cmp::Ordering::Equal
}
})
});
for page in language_pages {
let logical_key = logical_page_key(&page.relative_path, &languages, main_language);
if logical_key.contains("/page/") && !logical_key.starts_with("page/") {
continue;
}
let url_path = sitemap_url_path(&page.url_path);
let url = format!("{base_url}{url_path}");
let alternates = page_groups.get(&logical_key);
let lastmod = post_lastmod_by_path
.get(&page.url_path)
.cloned()
.unwrap_or_else(|| index_lastmod.clone());
let is_home = page.url_path == language_root_url_path(language, main_language);
let (changefreq, priority) = sitemap_metadata(&logical_key, is_home);
let rank = sitemap_rank(&logical_key);
xml.push(" <url>".to_string());
xml.push(format!(" <loc>{url}</loc>"));
xml.push(format!(" <loc>{}</loc>", escape_xml(&url)));
xml.push(format!(" <lastmod>{lastmod}</lastmod>"));
xml.push(format!(
" <changefreq>{}</changefreq>",
if is_home { "daily" } else { "weekly" }
));
xml.push(format!(
" <priority>{}</priority>",
if is_home { "1.0" } else { "0.8" }
));
if let Some(alternates) = alternates {
for alternate in alternates {
xml.push(format!(" <changefreq>{}</changefreq>", changefreq));
xml.push(format!(" <priority>{}</priority>", priority));
if !matches!(rank, 2 | 3) {
for alternate_language in &languages {
let alternate_path = if alternate_language == main_language {
page.url_path.clone()
} else if page.url_path == "/" {
format!("/{alternate_language}")
} else {
format!("/{alternate_language}{}", page.url_path)
};
let href = format!("{base_url}{}", sitemap_url_path(&alternate_path));
xml.push(format!(
" <xhtml:link rel=\"alternate\" hreflang=\"{}\" href=\"{base_url}{}\" />",
" <xhtml:link rel=\"alternate\" hreflang=\"{}\" href=\"{}\" />",
escape_xml(alternate_language),
escape_xml(&href),
));
}
let href = format!("{base_url}{}", sitemap_url_path(&page.url_path));
xml.push(format!(
" <xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"{}\" />",
escape_xml(&href),
));
} else if let Some(alternates) = alternates {
for alternate in alternates {
let href = format!("{base_url}{}", sitemap_url_path(&alternate.url_path));
xml.push(format!(
" <xhtml:link rel=\"alternate\" hreflang=\"{}\" href=\"{}\" />",
escape_xml(&alternate.language),
alternate.url_path,
escape_xml(&href),
));
}
if let Some(default_page) = alternates
.iter()
.find(|alternate| alternate.language == main_language)
{
let href = format!("{base_url}{}", sitemap_url_path(&default_page.url_path));
xml.push(format!(
" <xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"{base_url}{}\" />",
default_page.url_path,
" <xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"{}\" />",
escape_xml(&href),
));
}
}
@@ -1157,7 +1166,65 @@ fn build_sitemap_xml(
}
xml.push("</urlset>".to_string());
xml.join("\n")
format!("{}\n", xml.join("\n"))
}
fn sitemap_url_path(path: &str) -> String {
if path == "/" {
path.to_string()
} else {
format!("{}/", path.trim_end_matches('/'))
}
}
fn sitemap_metadata(logical_path: &str, is_home: bool) -> (&'static str, &'static str) {
if is_home {
return ("daily", "1.0");
}
if logical_path.starts_with("page/") {
return ("daily", "0.9");
}
let parts = logical_path.split('/').collect::<Vec<_>>();
match parts.as_slice() {
[year, month, day, _slug, "index.html"]
if is_year_segment(year) && is_month_segment(month) && is_day_segment(day) =>
{
("monthly", "0.8")
}
["category" | "tag", ..] => ("weekly", "0.6"),
[year, month, day, "index.html"]
if is_year_segment(year) && is_month_segment(month) && is_day_segment(day) =>
{
("monthly", "0.4")
}
[year, ..] if is_year_segment(year) => ("monthly", "0.5"),
[_slug, "index.html"] => ("weekly", "0.7"),
_ => ("weekly", "0.6"),
}
}
fn sitemap_rank(logical_path: &str) -> u8 {
let parts = logical_path.split('/').collect::<Vec<_>>();
match parts.as_slice() {
["index.html"] => 0,
["page", _, "index.html"] => 1,
[year, month, day, _slug, "index.html"]
if is_year_segment(year) && is_month_segment(month) && is_day_segment(day) =>
{
2
}
[year, "index.html"] if is_year_segment(year) => 4,
[year, month, "index.html"] if is_year_segment(year) && is_month_segment(month) => 5,
[year, month, day, "index.html"]
if is_year_segment(year) && is_month_segment(month) && is_day_segment(day) =>
{
6
}
[_slug, "index.html"] => 3,
["category", ..] => 7,
["tag", ..] => 8,
_ => 9,
}
}
fn language_prefix(language: &str, main_language: &str) -> String {

View File

@@ -14,7 +14,7 @@ use crate::db::{Database, queries};
use crate::engine::generation::PublishedPostSource;
use crate::engine::{EngineError, EngineResult};
use crate::model::{Post, PostStatus, pico_stylesheet_href};
use crate::render::build_preview_response;
use crate::render::{PostLanguageVariant, build_preview_response, select_post_language_variant};
use crate::util::frontmatter::{read_post_file, read_translation_file};
pub const PREVIEW_HOST: &str = "127.0.0.1";
@@ -204,9 +204,8 @@ fn render_preview_response(
let metadata = crate::engine::meta::read_project_json(&state.data_dir)?;
let db = Database::open(&state.db_path)?;
let preview_posts = collect_preview_posts(state)?;
let list_posts = filter_preview_list_posts(&state.data_dir, &preview_posts);
if path == "/calendar.json" {
let posts = list_posts
let posts = preview_posts
.iter()
.map(|source| source.post.clone())
.collect::<Vec<_>>();
@@ -220,13 +219,27 @@ fn render_preview_response(
.into_response());
}
if let Some((language, kind)) = preview_feed_request(path, &metadata) {
let localized_posts = localized_preview_posts(
db.conn(),
&state.data_dir,
&list_posts,
&language,
metadata.main_language.as_deref().unwrap_or("en"),
)?;
let main_language = metadata.main_language.as_deref().unwrap_or("en");
let localized_posts = if language.eq_ignore_ascii_case(main_language) {
preview_posts.clone()
} else {
localized_preview_posts(
db.conn(),
&state.data_dir,
&preview_posts,
&language,
main_language,
)?
.into_iter()
.filter(|source| {
source
.post
.language
.as_deref()
.is_some_and(|post_language| post_language.eq_ignore_ascii_case(&language))
})
.collect()
};
let (content_type, xml) = match kind {
PreviewFeedKind::Rss => (
"application/rss+xml; charset=utf-8",
@@ -414,28 +427,17 @@ fn collect_preview_posts(state: &PreviewServerState) -> EngineResult<Vec<Publish
post,
});
}
preview_posts.sort_by_key(|source| source.post.published_at.unwrap_or(source.post.created_at));
preview_posts.sort_by(|left, right| {
right
.post
.created_at
.cmp(&left.post.created_at)
.then_with(|| right.post.published_at.cmp(&left.post.published_at))
.then_with(|| left.post.slug.cmp(&right.post.slug))
});
Ok(preview_posts)
}
fn filter_preview_list_posts(
data_dir: &Path,
posts: &[PublishedPostSource],
) -> Vec<PublishedPostSource> {
let settings = crate::engine::meta::read_category_meta_json(data_dir).unwrap_or_default();
posts
.iter()
.filter(|source| {
!source.post.categories.iter().any(|category| {
settings
.get(category)
.is_some_and(|setting| !setting.render_in_lists)
})
})
.cloned()
.collect()
}
fn preview_feed_request(
path: &str,
metadata: &crate::model::ProjectMetadata,
@@ -460,7 +462,7 @@ fn preview_feed_request(
_ => return None,
};
let kind = match filename {
"rss.xml" | "feed.xml" => PreviewFeedKind::Rss,
"rss.xml" => PreviewFeedKind::Rss,
"atom.xml" => PreviewFeedKind::Atom,
_ => return None,
};
@@ -474,30 +476,49 @@ fn localized_preview_posts(
language: &str,
main_language: &str,
) -> EngineResult<Vec<PublishedPostSource>> {
if language.eq_ignore_ascii_case(main_language) {
return Ok(posts.to_vec());
}
let mut localized = Vec::new();
for source in posts {
let Ok(translation) = queries::post_translation::get_post_translation_by_post_and_language(
let translation = queries::post_translation::get_post_translation_by_post_and_language(
conn,
&source.post.id,
language,
) else {
continue;
};
let mut translated_post = source.post.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.status = translation.status.clone();
translated_post.file_path = translation.file_path.clone();
translated_post.published_at = translation.published_at.or(source.post.published_at);
localized.push(PublishedPostSource {
post: translated_post,
body_markdown: load_translation_body(data_dir, &translation)?,
)
.ok()
.filter(|translation| {
(translation.status == PostStatus::Draft && translation.content.is_some())
|| (!translation.file_path.trim().is_empty()
&& data_dir
.join(translation.file_path.trim_start_matches('/'))
.is_file())
});
match select_post_language_variant(
&source.post,
language,
main_language,
translation.is_some(),
) {
Some(PostLanguageVariant::Base) => localized.push(source.clone()),
Some(PostLanguageVariant::Translation) => {
let Some(translation) = translation else {
continue;
};
let mut translated_post = source.post.clone();
translated_post.id = translation.id.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.status = translation.status.clone();
translated_post.file_path = translation.file_path.clone();
translated_post.updated_at = translation.updated_at;
translated_post.published_at =
translation.published_at.or(source.post.published_at);
localized.push(PublishedPostSource {
post: translated_post,
body_markdown: load_translation_body(data_dir, &translation)?,
});
}
None => {}
}
}
Ok(localized)
}
@@ -789,6 +810,25 @@ mod tests {
assert!(html.contains("<strong>world</strong>"));
}
#[test]
fn preview_renders_page_category_post_at_flat_path() {
let db = Database::open_in_memory().unwrap();
let mut source = make_post();
source.post.categories = vec!["page".into()];
let response = build_preview_response(
db.conn(),
Path::new("."),
"project-1",
&make_metadata(),
&[(source.post, source.body_markdown)],
"/hello",
)
.unwrap();
assert_eq!(response.status_code, 200);
assert!(response.html.contains("<h1>Hello</h1>"));
}
#[test]
fn preview_renders_language_prefixed_single_post() {
let dir = tempfile::tempdir().unwrap();
@@ -923,11 +963,10 @@ mod tests {
assert!(translated_draft_html.contains("Deutscher <strong>Entwurf</strong>"));
assert!(calendar_json.contains("2024"));
assert!(rss_xml.contains("<rss"));
assert!(rss_xml.contains("Draft <strong>body</strong>"));
assert!(rss_xml.contains("Filesystem <strong>body</strong>"));
assert!(!rss_xml.contains("Stale database body"));
assert!(rss_xml.contains("<title>Hello</title>"));
assert!(rss_xml.contains("<title>Published from file</title>"));
assert!(translated_atom_xml.contains("<feed"));
assert!(translated_atom_xml.contains("Deutscher <strong>Entwurf</strong>"));
assert!(translated_atom_xml.contains("<title>Hallo</title>"));
}
#[test]

View File

@@ -47,10 +47,9 @@ pub fn validate_site(
format!("{language}/")
};
expected.insert(format!("{prefix}rss.xml"));
expected.insert(format!("{prefix}feed.xml"));
expected.insert(format!("{prefix}atom.xml"));
expected.insert(format!("{prefix}sitemap.xml"));
}
expected.insert("sitemap.xml".to_string());
let mut actual = HashSet::new();
if output_dir.exists() {

View File

@@ -3,7 +3,7 @@ use std::fs;
use std::path::Path;
use crate::db::DbConnection as Connection;
use chrono::{Datelike, TimeZone, Utc};
use chrono::{Datelike, Local, TimeZone};
use serde::Serialize;
use crate::db::queries::generated_file_hash as qhash;
@@ -99,7 +99,6 @@ pub fn build_core_generation_paths(main_language: &str, blog_languages: &[String
"404.html".to_string(),
"sitemap.xml".to_string(),
"rss.xml".to_string(),
"feed.xml".to_string(),
"atom.xml".to_string(),
"calendar.json".to_string(),
];
@@ -108,9 +107,7 @@ pub fn build_core_generation_paths(main_language: &str, blog_languages: &[String
if language != main_language {
paths.push(format!("{language}/index.html"));
paths.push(format!("{language}/404.html"));
paths.push(format!("{language}/sitemap.xml"));
paths.push(format!("{language}/rss.xml"));
paths.push(format!("{language}/feed.xml"));
paths.push(format!("{language}/atom.xml"));
}
}
@@ -124,8 +121,7 @@ pub fn build_calendar_archive_data(posts: &[Post]) -> CalendarArchiveData {
let mut days = BTreeMap::new();
for post in posts {
let timestamp_ms = post.published_at.unwrap_or(post.created_at);
let Some(created_at) = Utc.timestamp_millis_opt(timestamp_ms).single() else {
let Some(created_at) = Local.timestamp_millis_opt(post.created_at).single() else {
continue;
};
@@ -146,5 +142,5 @@ pub fn build_calendar_archive_data(posts: &[Post]) -> CalendarArchiveData {
}
pub fn build_calendar_json(posts: &[Post]) -> serde_json::Result<String> {
serde_json::to_string_pretty(&build_calendar_archive_data(posts))
serde_json::to_string(&build_calendar_archive_data(posts))
}

View File

@@ -13,6 +13,7 @@ pub use generation::{
pub use markdown::render_markdown_to_html;
pub(crate) use page_renderer::render_liquid_template_with_host;
pub use page_renderer::{RenderError, render_liquid_template};
pub(crate) use routes::{PostLanguageVariant, select_post_language_variant};
pub use routes::{
RenderedPage, build_canonical_post_path, render_starter_list_page,
render_starter_list_page_with_media_map, render_starter_single_post_page,

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use chrono::{Datelike, TimeZone, Utc};
use chrono::{Datelike, Local, TimeZone};
use serde::Serialize;
use crate::i18n::normalize_language;
@@ -17,6 +17,40 @@ const STARTER_MENU_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/menu.liquid");
const STARTER_LANGUAGE_SWITCHER_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/language-switcher.liquid");
const STARTER_MENU_ITEMS_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/menu-items.liquid");
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PostLanguageVariant {
Base,
Translation,
}
pub(crate) fn select_post_language_variant(
post: &Post,
target_language: &str,
main_language: &str,
has_translation: bool,
) -> Option<PostLanguageVariant> {
let source_language = post.language.as_deref().unwrap_or(main_language);
if target_language.eq_ignore_ascii_case(main_language) {
return Some(if has_translation {
PostLanguageVariant::Translation
} else {
PostLanguageVariant::Base
});
}
if post.do_not_translate {
return None;
}
if source_language.eq_ignore_ascii_case(target_language) {
Some(PostLanguageVariant::Base)
} else if has_translation {
Some(PostLanguageVariant::Translation)
} else {
Some(PostLanguageVariant::Base)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RenderedPage {
@@ -99,8 +133,7 @@ struct PostTemplateContext<'a> {
}
pub fn build_canonical_post_path(post: &Post, language: &str, main_language: &str) -> String {
let timestamp_ms = post.published_at.unwrap_or(post.created_at);
let Some(timestamp) = Utc.timestamp_millis_opt(timestamp_ms).single() else {
let Some(timestamp) = Local.timestamp_millis_opt(post.created_at).single() else {
return fallback_language_path(post, language, main_language);
};
@@ -276,8 +309,7 @@ fn starter_partials() -> HashMap<String, String> {
),
(
"partials/menu-items".to_string(),
"{% for item in items %}<a href=\"{{ item.href }}\">{{ item.title }}</a>{% endfor %}"
.to_string(),
STARTER_MENU_ITEMS_PARTIAL.to_string(),
),
])
}
@@ -310,8 +342,8 @@ fn fallback_language_path(post: &Post, language: &str, main_language: &str) -> S
}
fn calendar_initial_parts(post: &Post) -> (i32, u32) {
let timestamp_ms = post.published_at.unwrap_or(post.created_at);
Utc.timestamp_millis_opt(timestamp_ms)
Local
.timestamp_millis_opt(post.created_at)
.single()
.map(|timestamp| (timestamp.year(), timestamp.month()))
.unwrap_or((1970, 1))
@@ -406,8 +438,7 @@ fn build_day_blocks(posts: &[(Post, String)]) -> Vec<DayBlockContext> {
let mut current_key: Option<String> = None;
for (post, body) in posts {
let timestamp_ms = post.published_at.unwrap_or(post.created_at);
let Some(timestamp) = Utc.timestamp_millis_opt(timestamp_ms).single() else {
let Some(timestamp) = Local.timestamp_millis_opt(post.created_at).single() else {
continue;
};
@@ -425,10 +456,10 @@ fn build_day_blocks(posts: &[(Post, String)]) -> Vec<DayBlockContext> {
blocks.push(DayBlockContext {
show_date_marker: true,
date_label: format!(
"{:02}.{:02}.{:04}",
timestamp.day(),
"{:04}-{:02}-{:02}",
timestamp.year(),
timestamp.month(),
timestamp.year()
timestamp.day()
),
posts: Vec::new(),
show_separator: false,
@@ -500,7 +531,8 @@ mod tests {
#[test]
fn canonical_post_paths_follow_language_prefix_rule() {
let post = make_post();
let mut post = make_post();
post.published_at = Some(1_712_678_400_000);
assert_eq!(
build_canonical_post_path(&post, "en", "en"),
"/2024/03/09/hello"
@@ -511,6 +543,52 @@ mod tests {
);
}
#[test]
fn canonical_post_paths_use_the_bds2_local_created_date() {
let mut post = make_post();
post.created_at = 1_711_927_800_000;
let local = Local
.timestamp_millis_opt(post.created_at)
.single()
.unwrap();
assert_eq!(
build_canonical_post_path(&post, "en", "en"),
format!(
"/{:04}/{:02}/{:02}/hello",
local.year(),
local.month(),
local.day()
)
);
}
#[test]
fn language_variants_match_bds2_canonical_and_fallback_rules() {
let mut post = make_post();
post.language = Some("en".into());
assert_eq!(
select_post_language_variant(&post, "de", "de", true),
Some(PostLanguageVariant::Translation)
);
assert_eq!(
select_post_language_variant(&post, "en", "de", false),
Some(PostLanguageVariant::Base)
);
assert_eq!(
select_post_language_variant(&post, "fr", "de", true),
Some(PostLanguageVariant::Translation)
);
assert_eq!(
select_post_language_variant(&post, "fr", "de", false),
Some(PostLanguageVariant::Base)
);
post.do_not_translate = true;
assert_eq!(select_post_language_variant(&post, "fr", "de", true), None);
}
#[test]
fn starter_single_post_renderer_uses_canonical_route_and_language_links() {
let post = make_post();
@@ -591,8 +669,8 @@ mod tests {
assert_eq!(rendered.relative_path, "de/index.html");
assert!(rendered.html.contains("archive-day-group"));
assert!(rendered.html.contains("09.03.2024"));
assert!(rendered.html.contains("10.03.2024"));
assert!(rendered.html.contains("2024-03-09"));
assert!(rendered.html.contains("2024-03-10"));
assert!(rendered.html.contains("href=\"/de/2024/03/10/next\""));
}
}

View File

@@ -5,7 +5,7 @@ use std::path::Path;
use std::sync::Arc;
use crate::db::DbConnection as Connection;
use chrono::{Datelike, TimeZone, Utc};
use chrono::{Datelike, Local, TimeZone, Utc};
use rayon::prelude::*;
use serde_json::{Value, json};
@@ -17,8 +17,8 @@ use crate::model::{
TemplateKind, TemplateStatus,
};
use crate::render::{
RenderCategorySettings, RenderTemplateLookup, build_canonical_post_path,
render_liquid_template_with_host, resolve_post_template,
PostLanguageVariant, RenderCategorySettings, RenderTemplateLookup, build_canonical_post_path,
render_liquid_template_with_host, resolve_post_template, select_post_language_variant,
};
use crate::scripting::{CoreHost, HostApi, UnavailableHost};
use crate::util::frontmatter::{read_script_file, read_template_file, read_translation_file};
@@ -36,6 +36,8 @@ const STARTER_MENU_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/menu.liquid");
const STARTER_LANGUAGE_SWITCHER_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/language-switcher.liquid");
const STARTER_MENU_ITEMS_PARTIAL: &str =
include_str!("../../../../assets/starter-templates/partials/menu-items.liquid");
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SitePage {
@@ -81,6 +83,7 @@ struct TemplateBundle {
#[derive(Debug, Clone)]
struct RenderPostRecord {
post: Post,
source_post_id: String,
body_markdown: String,
}
@@ -278,47 +281,64 @@ fn build_site_render_artifacts_with_mode(
canonical_post_path_by_slug(&localized_posts, &language, &main_language);
for record in &localized_posts {
let canonical_path = build_canonical_post_path(&record.post, &language, &main_language);
let relative_path = format!("{}/index.html", canonical_path.trim_start_matches('/'));
artifacts.route_manifest.push(SitePage {
language: language.clone(),
relative_path: relative_path.clone(),
url_path: canonical_path.clone(),
html: String::new(),
});
if section.is_some_and(|section| section != GenerationSection::Single) {
continue;
let mut post_paths = vec![(canonical_path, GenerationSection::Single)];
if record
.post
.categories
.iter()
.any(|category| category == "page")
{
post_paths.push((
if language == main_language {
format!("/{}", record.post.slug)
} else {
format!("/{language}/{}", record.post.slug)
},
GenerationSection::Core,
));
}
if requested_paths.is_some_and(|requested| !requested.contains(&relative_path)) {
continue;
for (url_path, route_section) in post_paths {
let relative_path = format!("{}/index.html", url_path.trim_start_matches('/'));
artifacts.route_manifest.push(SitePage {
language: language.clone(),
relative_path: relative_path.clone(),
url_path: url_path.clone(),
html: String::new(),
});
if section.is_some_and(|section| section != route_section)
|| requested_paths.is_some_and(|requested| !requested.contains(&relative_path))
{
continue;
}
let html = render_post_route(
conn,
metadata,
&language,
&main_language,
record,
&localized_posts,
&tags,
&category_settings,
&media_by_id,
&canonical_map,
&menu_items,
&post_data_json_by_id,
&bundle,
is_preview,
)?;
artifacts.pagefind_documents.push(PagefindDocument {
language: language.clone(),
relative_path: relative_path.clone(),
url_path: url_path.clone(),
html: html.clone(),
});
artifacts.pages.push(SitePage {
language: language.clone(),
relative_path,
url_path,
html,
});
}
let html = render_post_route(
conn,
metadata,
&language,
&main_language,
record,
&localized_posts,
&tags,
&category_settings,
&media_by_id,
&canonical_map,
&menu_items,
&post_data_json_by_id,
&bundle,
is_preview,
)?;
artifacts.pagefind_documents.push(PagefindDocument {
language: language.clone(),
relative_path: relative_path.clone(),
url_path: canonical_path.clone(),
html: html.clone(),
});
artifacts.pages.push(SitePage {
language: language.clone(),
relative_path,
url_path: canonical_path,
html,
});
}
}
@@ -513,47 +533,61 @@ fn load_language_posts(
) -> Result<Vec<RenderPostRecord>, Box<dyn Error + Send + Sync>> {
let mut posts = Vec::new();
for (post, body) in published_posts {
if language.eq_ignore_ascii_case(main_language) {
posts.push(RenderPostRecord {
let translation = queries::post_translation::get_post_translation_by_post_and_language(
conn, &post.id, language,
)
.ok()
.filter(|translation| {
(is_preview && translation.status == PostStatus::Draft && translation.content.is_some())
|| (!translation.file_path.trim().is_empty()
&& data_dir
.join(translation.file_path.trim_start_matches('/'))
.is_file())
});
match select_post_language_variant(post, language, main_language, translation.is_some()) {
Some(PostLanguageVariant::Base) => posts.push(RenderPostRecord {
post: post.clone(),
source_post_id: post.id.clone(),
body_markdown: body.clone(),
});
continue;
}
if let Ok(translation) =
queries::post_translation::get_post_translation_by_post_and_language(
conn, &post.id, language,
)
{
let translated_body = if is_preview && translation.status == PostStatus::Draft {
match &translation.content {
Some(content) => content.clone(),
None => read_translation_body(data_dir, &translation.file_path)?,
}
} else {
read_translation_body(data_dir, &translation.file_path)?
};
let mut translated_post = post.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.status = translation.status.clone();
translated_post.file_path = translation.file_path.clone();
translated_post.published_at = translation.published_at.or(post.published_at);
posts.push(RenderPostRecord {
post: translated_post,
body_markdown: translated_body,
});
}),
Some(PostLanguageVariant::Translation) => {
let Some(translation) = translation else {
continue;
};
let translated_body = if is_preview && translation.status == PostStatus::Draft {
match &translation.content {
Some(content) => content.clone(),
None => read_translation_body(data_dir, &translation.file_path)?,
}
} else {
read_translation_body(data_dir, &translation.file_path)?
};
let mut translated_post = post.clone();
translated_post.id = translation.id.clone();
translated_post.title = translation.title.clone();
translated_post.excerpt = translation.excerpt.clone();
translated_post.language = Some(translation.language.clone());
translated_post.status = translation.status.clone();
translated_post.file_path = translation.file_path.clone();
translated_post.updated_at = translation.updated_at;
translated_post.published_at = translation.published_at.or(post.published_at);
posts.push(RenderPostRecord {
post: translated_post,
source_post_id: post.id.clone(),
body_markdown: translated_body,
});
}
None => {}
}
}
posts.sort_by(|left, right| {
right
.post
.published_at
.unwrap_or(right.post.created_at)
.cmp(&left.post.published_at.unwrap_or(left.post.created_at))
.created_at
.cmp(&left.post.created_at)
.then_with(|| right.post.published_at.cmp(&left.post.published_at))
.then_with(|| left.post.slug.cmp(&right.post.slug))
});
Ok(posts)
}
@@ -607,10 +641,7 @@ fn build_language_routes(
.or_default()
.push(record.clone());
}
if let Some(timestamp) = Utc
.timestamp_millis_opt(record.post.published_at.unwrap_or(record.post.created_at))
.single()
{
if let Some(timestamp) = Local.timestamp_millis_opt(record.post.created_at).single() {
year_posts
.entry(timestamp.year())
.or_default()
@@ -791,8 +822,8 @@ fn render_list_route(
"calendar_initial_month": route.posts.first().map(|post| calendar_initial_parts(&post.post).1).unwrap_or(1),
"archive_context": route.archive_context,
"show_archive_range_heading": false,
"min_date": route.posts.last().map(|record| timestamp_parts(record.post.published_at.unwrap_or(record.post.created_at))),
"max_date": route.posts.first().map(|record| timestamp_parts(record.post.published_at.unwrap_or(record.post.created_at))),
"min_date": route.posts.last().map(|record| timestamp_parts(record.post.created_at)),
"max_date": route.posts.first().map(|record| timestamp_parts(record.post.created_at)),
"day_blocks": build_day_blocks(&route.posts, category_settings),
"is_list_page": route.current_page > 1,
"is_first_page": route.current_page == 1,
@@ -866,19 +897,19 @@ fn render_post_route(
.or_else(|| resolved.content.clone())
.unwrap_or_else(|| STARTER_SINGLE_POST_TEMPLATE.to_string());
let linked_media = queries::post_media::list_post_media_by_post(conn, &record.post.id)
let linked_media = queries::post_media::list_post_media_by_post(conn, &record.source_post_id)
.unwrap_or_default()
.into_iter()
.filter_map(|link| media_by_id.get(&link.media_id))
.map(media_context)
.collect::<Vec<_>>();
let outgoing_links =
queries::post_link::list_links_by_source(conn, &record.post.id).unwrap_or_default();
queries::post_link::list_links_by_source(conn, &record.source_post_id).unwrap_or_default();
let incoming_links =
queries::post_link::list_links_by_target(conn, &record.post.id).unwrap_or_default();
queries::post_link::list_links_by_target(conn, &record.source_post_id).unwrap_or_default();
let post_by_id = all_posts
.iter()
.map(|item| (item.post.id.clone(), item))
.map(|item| (item.source_post_id.clone(), item))
.collect::<HashMap<_, _>>();
let outgoing_link_context = outgoing_links
.iter()
@@ -1072,8 +1103,7 @@ fn build_day_blocks(
let mut current_label = String::new();
for record in posts {
let timestamp_ms = record.post.published_at.unwrap_or(record.post.created_at);
let Some(timestamp) = Utc.timestamp_millis_opt(timestamp_ms).single() else {
let Some(timestamp) = Local.timestamp_millis_opt(record.post.created_at).single() else {
continue;
};
let key = format!(
@@ -1091,13 +1121,8 @@ fn build_day_blocks(
}));
current_posts = Vec::new();
}
current_label = key.clone();
current_key = key;
current_label = format!(
"{:02}.{:02}.{:04}",
timestamp.day(),
timestamp.month(),
timestamp.year()
);
let show_title = should_show_list_title(&record.post, category_settings);
current_posts.push(json!({
"id": record.post.id,
@@ -1631,8 +1656,7 @@ fn starter_partials() -> HashMap<String, String> {
),
(
"partials/menu-items".to_string(),
"{% for item in items %}<a href=\"{{ item.href }}\">{{ item.title }}</a>{% endfor %}"
.to_string(),
STARTER_MENU_ITEMS_PARTIAL.to_string(),
),
])
}
@@ -1644,8 +1668,8 @@ fn pico_stylesheet_href(metadata: &ProjectMetadata) -> Option<String> {
}
fn calendar_initial_parts(post: &Post) -> (i32, u32) {
let timestamp_ms = post.published_at.unwrap_or(post.created_at);
Utc.timestamp_millis_opt(timestamp_ms)
Local
.timestamp_millis_opt(post.created_at)
.single()
.map(|timestamp| (timestamp.year(), timestamp.month()))
.unwrap_or((1970, 1))
@@ -1670,6 +1694,22 @@ mod menu_tests {
use super::*;
use crate::engine::menu::{MenuItem, MenuItemKind};
#[test]
fn starter_menu_partial_keeps_nested_submenus_and_calendar_navigation() {
let partials = starter_partials();
let menu_items = partials.get("partials/menu-items").unwrap();
assert!(menu_items.contains("blog-menu-submenu"));
assert!(menu_items.contains("data-blog-calendar-toggle"));
assert!(menu_items.contains("data-blog-calendar-root"));
assert!(
partials
.get("partials/language-switcher")
.unwrap()
.contains("data-search-no-results")
);
}
#[test]
fn preview_request_paths_select_only_the_matching_generated_page() {
assert_eq!(preview_relative_path("/"), "index.html");

View File

@@ -247,3 +247,26 @@ fn relationships_reference_existing_entities() {
);
}
}
#[test]
fn generated_site_golden_tracks_current_bds2_rendering() {
let directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../fixtures/golden-generated-sites/rfc1437-sample");
let index = fs::read_to_string(directory.join("index.html")).unwrap();
let rss = fs::read_to_string(directory.join("rss.xml")).unwrap();
let atom = fs::read_to_string(directory.join("atom.xml")).unwrap();
let sitemap = fs::read_to_string(directory.join("sitemap.xml")).unwrap();
let calendar: serde_json::Value =
serde_json::from_str(&fs::read_to_string(directory.join("calendar.json")).unwrap())
.unwrap();
assert!(index.contains("class=\"blog-menu-submenu\""));
assert!(index.contains("data-search-no-results=\"Keine Ergebnisse gefunden\""));
assert!(index.contains("<span>2026-07-17</span>"));
assert!(rss.starts_with("<rss><channel><title>rfc1437 (de)</title>"));
assert!(atom.starts_with("<feed><title>rfc1437 (de)</title>"));
assert!(sitemap.contains("<loc>https://www.rfc1437.de/</loc>"));
assert!(sitemap.contains("hreflang=\"en\" href=\"https://www.rfc1437.de/en/\""));
assert!(sitemap.contains("<loc>https://www.rfc1437.de/bilderarchiv-2004/</loc>"));
assert_eq!(calendar["days"]["2026-07-17"], 3);
}

View File

@@ -11,8 +11,8 @@ use bds_core::engine::generation::{
use bds_core::engine::meta::write_category_meta_json;
use bds_core::engine::validate_site::validate_site;
use bds_core::model::{
CategorySettings, Post, PostStatus, Project, ProjectMetadata, Template, TemplateKind,
TemplateStatus,
CategorySettings, Post, PostStatus, PostTranslation, Project, ProjectMetadata, Template,
TemplateKind, TemplateStatus,
};
use tempfile::TempDir;
@@ -177,7 +177,7 @@ fn generation_engine_writes_core_and_single_post_artifacts() {
assert!(report.written_paths.contains(&"index.html".to_string()));
assert!(report.written_paths.contains(&"calendar.json".to_string()));
assert!(report.written_paths.contains(&"rss.xml".to_string()));
assert!(report.written_paths.contains(&"feed.xml".to_string()));
assert!(!report.written_paths.contains(&"feed.xml".to_string()));
assert!(report.written_paths.contains(&"atom.xml".to_string()));
assert!(report.written_paths.contains(&"sitemap.xml".to_string()));
assert!(report.written_paths.contains(&"404.html".to_string()));
@@ -209,7 +209,7 @@ fn generation_engine_writes_core_and_single_post_artifacts() {
assert!(dir.path().join("index.html").exists());
assert!(dir.path().join("rss.xml").exists());
assert!(dir.path().join("feed.xml").exists());
assert!(!dir.path().join("feed.xml").exists());
assert!(dir.path().join("atom.xml").exists());
assert!(dir.path().join("sitemap.xml").exists());
assert!(dir.path().join("assets/pico.min.css").exists());
@@ -217,14 +217,188 @@ fn generation_engine_writes_core_and_single_post_artifacts() {
assert!(dir.path().join("2024/03/09/hello/index.html").exists());
let rss = std::fs::read_to_string(dir.path().join("rss.xml")).unwrap();
assert!(rss.contains("<rss version=\"2.0\""));
assert!(rss.contains("https://example.com/2024/03/09/hello"));
assert_eq!(
rss,
"<rss><channel><title>Blog (en)</title><item><title>next</title><link>https://example.com/2024/03/10/next/</link></item><item><title>hello</title><link>https://example.com/2024/03/09/hello/</link></item></channel></rss>"
);
let atom = std::fs::read_to_string(dir.path().join("atom.xml")).unwrap();
assert_eq!(
atom,
"<feed><title>Blog (en)</title><entry><title>next</title><id>https://example.com/2024/03/10/next/</id></entry><entry><title>hello</title><id>https://example.com/2024/03/09/hello/</id></entry></feed>"
);
let sitemap = std::fs::read_to_string(dir.path().join("sitemap.xml")).unwrap();
assert!(sitemap.contains("https://example.com/2024/03/09/hello"));
assert!(sitemap.contains("https://example.com/category/article"));
}
#[test]
fn mixed_source_languages_use_the_main_language_at_canonical_urls() {
let (db, dir) = setup();
let mut metadata = make_metadata();
metadata.main_language = Some("de".into());
metadata.blog_languages = vec!["de".into(), "en".into()];
bds_core::engine::meta::write_project_json(dir.path(), &metadata).unwrap();
insert_template(
db.conn(),
&Template {
id: "template-post".into(),
project_id: "p1".into(),
slug: "post".into(),
title: "Post".into(),
kind: TemplateKind::Post,
enabled: true,
version: 1,
file_path: String::new(),
status: TemplateStatus::Published,
content: Some("ID={{ post.id }} {{ post.content }}".into()),
created_at: 1,
updated_at: 1,
},
)
.unwrap();
let mut english_source = make_post("english-source", 1_710_000_000_000);
english_source.language = Some("en".into());
write_published_snapshot(&dir, &mut english_source, "English source body");
bds_core::db::queries::post::insert_post(db.conn(), &english_source).unwrap();
let german_translation = PostTranslation {
id: "translation-english-de".into(),
project_id: "p1".into(),
translation_for: english_source.id.clone(),
language: "de".into(),
title: "Deutsche Übersetzung".into(),
excerpt: None,
content: None,
status: PostStatus::Published,
file_path: "posts/english-source.de.md".into(),
checksum: None,
created_at: english_source.created_at,
updated_at: english_source.updated_at,
published_at: english_source.published_at,
};
bds_core::db::queries::post_translation::insert_post_translation(
db.conn(),
&german_translation,
)
.unwrap();
std::fs::write(
dir.path().join(&german_translation.file_path),
bds_core::util::frontmatter::write_translation_file(
&german_translation,
"Deutscher Übersetzungstext",
),
)
.unwrap();
let mut german_without_translation = make_post("german-fallback", 1_710_172_800_000);
german_without_translation.language = Some("de".into());
german_without_translation.title = "Deutscher Fallback".into();
let mut german_source = make_post("german-source", 1_710_086_400_000);
german_source.language = Some("de".into());
german_source.title = "Deutsche Quelle".into();
write_published_snapshot(&dir, &mut german_source, "Deutscher Quelltext");
bds_core::db::queries::post::insert_post(db.conn(), &german_source).unwrap();
let english_translation = PostTranslation {
id: "translation-german-en".into(),
project_id: "p1".into(),
translation_for: german_source.id.clone(),
language: "en".into(),
title: "English translation".into(),
excerpt: None,
content: None,
status: PostStatus::Published,
file_path: "posts/german-source.en.md".into(),
checksum: None,
created_at: german_source.created_at,
updated_at: german_source.updated_at,
published_at: german_source.published_at,
};
bds_core::db::queries::post_translation::insert_post_translation(
db.conn(),
&english_translation,
)
.unwrap();
std::fs::write(
dir.path().join(&english_translation.file_path),
bds_core::util::frontmatter::write_translation_file(
&english_translation,
"English translation body",
),
)
.unwrap();
let mut german_private = make_post("german-private", 1_710_259_200_000);
german_private.language = Some("de".into());
german_private.do_not_translate = true;
let output = dir.path().join("html");
let sources = vec![
PublishedPostSource {
post: english_source,
body_markdown: "English source body".into(),
},
PublishedPostSource {
post: german_source,
body_markdown: "Deutscher Quelltext".into(),
},
PublishedPostSource {
post: german_without_translation,
body_markdown: "Unübersetzter Quelltext".into(),
},
PublishedPostSource {
post: german_private,
body_markdown: "Nur auf Deutsch".into(),
},
];
generate_starter_site(db.conn(), &output, "p1", &metadata, &sources, "de").unwrap();
let canonical_english_source =
std::fs::read_to_string(output.join("2024/03/09/english-source/index.html")).unwrap();
let localized_english_source =
std::fs::read_to_string(output.join("en/2024/03/09/english-source/index.html")).unwrap();
let canonical_german_source =
std::fs::read_to_string(output.join("2024/03/10/german-source/index.html")).unwrap();
let localized_german_source =
std::fs::read_to_string(output.join("en/2024/03/10/german-source/index.html")).unwrap();
let localized_fallback =
std::fs::read_to_string(output.join("en/2024/03/11/german-fallback/index.html")).unwrap();
assert!(canonical_english_source.contains("Deutscher Übersetzungstext"));
assert!(canonical_english_source.contains("ID=translation-english-de"));
assert!(localized_english_source.contains("English source body"));
assert!(localized_english_source.contains("ID=post-english-source"));
assert!(canonical_german_source.contains("Deutscher Quelltext"));
assert!(localized_german_source.contains("English translation body"));
assert!(localized_fallback.contains("Unübersetzter Quelltext"));
assert!(
output
.join("2024/03/12/german-private/index.html")
.is_file()
);
assert!(
!output
.join("en/2024/03/12/german-private/index.html")
.exists()
);
let sitemap = std::fs::read_to_string(output.join("sitemap.xml")).unwrap();
assert!(sitemap.contains("https://example.com/2024/03/09/english-source"));
assert!(sitemap.contains("https://example.com/en/2024/03/09/english-source"));
assert!(sitemap.contains("https://example.com/2024/03/10/german-source"));
assert!(sitemap.contains("https://example.com/en/2024/03/10/german-source"));
assert!(sitemap.contains("https://example.com/2024/03/12/german-private"));
assert!(!sitemap.contains("https://example.com/en/2024/03/12/german-private"));
let main_rss = std::fs::read_to_string(output.join("rss.xml")).unwrap();
let english_rss = std::fs::read_to_string(output.join("en/rss.xml")).unwrap();
assert!(main_rss.contains("<title>english-source</title>"));
assert!(!main_rss.contains("<title>Deutsche Übersetzung</title>"));
assert!(english_rss.contains("<title>english-source</title>"));
assert!(english_rss.contains("<title>English translation</title>"));
assert!(!english_rss.contains("<title>Deutscher Fallback</title>"));
}
#[test]
fn section_generation_reports_its_urls_and_defers_pagefind() {
let (db, dir) = setup();
@@ -345,7 +519,8 @@ fn multilingual_generation_writes_language_aware_atom_and_sitemap_routes() {
assert!(report.written_paths.contains(&"en/atom.xml".to_string()));
assert!(report.written_paths.contains(&"en/rss.xml".to_string()));
assert!(report.written_paths.contains(&"en/sitemap.xml".to_string()));
assert!(!report.written_paths.contains(&"en/sitemap.xml".to_string()));
assert!(!report.written_paths.contains(&"feed.xml".to_string()));
assert!(report.written_paths.contains(&"en/404.html".to_string()));
assert!(
report
@@ -354,19 +529,79 @@ fn multilingual_generation_writes_language_aware_atom_and_sitemap_routes() {
);
let atom = std::fs::read_to_string(dir.path().join("en/atom.xml")).unwrap();
assert!(
atom.contains("<link href=\"https://example.com/en/\" rel=\"alternate\" />")
|| atom.contains("<link href=\"https://example.com/en\" rel=\"alternate\" />")
);
assert!(atom.contains("<link href=\"https://example.com/en/atom.xml\" rel=\"self\" />"));
assert!(atom.starts_with("<feed><title>Blog (en)</title>"));
let sitemap = std::fs::read_to_string(dir.path().join("sitemap.xml")).unwrap();
assert!(sitemap.contains("hreflang=\"de\" href=\"https://example.com/\""));
assert!(sitemap.contains("hreflang=\"en\" href=\"https://example.com/en\""));
assert!(sitemap.contains("hreflang=\"en\" href=\"https://example.com/en/\""));
assert!(sitemap.contains("hreflang=\"x-default\" href=\"https://example.com/\""));
assert!(sitemap.contains("https://example.com/category/article"));
}
#[test]
fn page_category_posts_also_generate_flat_routes() {
let (db, dir) = setup();
let metadata = make_metadata();
let mut page = make_post("about", 1_710_000_000_000);
page.categories = vec!["page".into()];
let posts = vec![PublishedPostSource {
post: page,
body_markdown: "About this site".into(),
}];
let report =
generate_starter_site(db.conn(), dir.path(), "p1", &metadata, &posts, "en").unwrap();
assert!(
report
.written_paths
.contains(&"about/index.html".to_string())
);
assert!(dir.path().join("about/index.html").is_file());
let sitemap = std::fs::read_to_string(dir.path().join("sitemap.xml")).unwrap();
assert!(sitemap.contains("<loc>https://example.com/about/</loc>"));
}
#[test]
fn page_aliases_belong_to_core_while_dated_posts_belong_to_single() {
let (db, dir) = setup();
let metadata = make_metadata();
let mut page = make_post("about", 1_710_000_000_000);
page.categories = vec!["page".into()];
let posts = vec![PublishedPostSource {
post: page,
body_markdown: "About this site".into(),
}];
let single = render_site_section_with_progress(
db.conn(),
dir.path(),
"p1",
&metadata,
&posts,
GenerationSection::Single,
|_current, _total, _url| {},
|| false,
)
.unwrap();
assert_eq!(single.written_paths, vec!["2024/03/09/about/index.html"]);
assert!(!dir.path().join("about/index.html").exists());
let core = render_site_section_with_progress(
db.conn(),
dir.path(),
"p1",
&metadata,
&posts,
GenerationSection::Core,
|_current, _total, _url| {},
|| false,
)
.unwrap();
assert!(core.written_paths.contains(&"about/index.html".to_string()));
assert!(dir.path().join("about/index.html").is_file());
}
#[test]
fn generation_respects_category_list_settings_and_writes_bundled_images() {
let (db, dir) = setup();
@@ -457,12 +692,12 @@ fn generation_respects_category_list_settings_and_writes_bundled_images() {
std::fs::read_to_string(dir.path().join("category/featured/index.html")).unwrap();
assert!(featured_html.contains("FEATURED:[Featured Post|false]"));
let feed = std::fs::read_to_string(dir.path().join("feed.xml")).unwrap();
assert!(!feed.contains("hidden-post"));
assert!(feed.contains("featured-post"));
let rss = std::fs::read_to_string(dir.path().join("rss.xml")).unwrap();
assert!(rss.contains("hidden-post"));
assert!(rss.contains("featured-post"));
let calendar = std::fs::read_to_string(dir.path().join("calendar.json")).unwrap();
assert!(!calendar.contains("2024-03-09"));
assert!(calendar.contains("2024-03-09"));
assert!(calendar.contains("2024-03-10"));
}
@@ -484,7 +719,7 @@ fn generation_engine_skips_unchanged_outputs_on_second_run() {
assert!(second.skipped_paths.contains(&"index.html".to_string()));
assert!(second.skipped_paths.contains(&"calendar.json".to_string()));
assert!(second.skipped_paths.contains(&"rss.xml".to_string()));
assert!(second.skipped_paths.contains(&"feed.xml".to_string()));
assert!(!second.skipped_paths.contains(&"feed.xml".to_string()));
assert!(
second
.skipped_paths
@@ -506,12 +741,12 @@ fn site_validation_detects_stale_and_missing_outputs() {
generate_starter_site(db.conn(), dir.path(), "p1", &metadata, &posts, "en").unwrap();
std::fs::write(dir.path().join("index.html"), "tampered").unwrap();
std::fs::remove_file(dir.path().join("feed.xml")).unwrap();
std::fs::remove_file(dir.path().join("atom.xml")).unwrap();
let report = validate_site(db.conn(), dir.path(), "p1").unwrap();
assert!(report.stale_pages.contains(&"index.html".to_string()));
assert!(report.missing_pages.contains(&"feed.xml".to_string()));
assert!(report.missing_pages.contains(&"atom.xml".to_string()));
assert!(report.extra_pages.is_empty());
}
@@ -529,7 +764,7 @@ fn apply_validation_repairs_core_section_outputs() {
generate_starter_site(db.conn(), dir.path(), "p1", &metadata, &posts, "en").unwrap();
std::fs::write(dir.path().join("index.html"), "tampered").unwrap();
std::fs::remove_file(dir.path().join("feed.xml")).unwrap();
std::fs::remove_file(dir.path().join("atom.xml")).unwrap();
let report = validate_site(db.conn(), dir.path(), "p1").unwrap();
let sections = sections_from_validation_report(&report);

View File

@@ -111,20 +111,20 @@ fn generated_write_rewrites_stale_file_even_when_db_hash_matches() {
}
#[test]
fn core_generation_paths_include_language_prefixed_variants() {
fn core_generation_paths_match_bds2_outputs() {
let paths = build_core_generation_paths("en", &["en".into(), "de".into(), "fr".into()]);
assert!(paths.contains(&"index.html".to_string()));
assert!(paths.contains(&"404.html".to_string()));
assert!(paths.contains(&"sitemap.xml".to_string()));
assert!(paths.contains(&"rss.xml".to_string()));
assert!(paths.contains(&"feed.xml".to_string()));
assert!(!paths.contains(&"feed.xml".to_string()));
assert!(paths.contains(&"atom.xml".to_string()));
assert!(paths.contains(&"calendar.json".to_string()));
assert!(paths.contains(&"de/index.html".to_string()));
assert!(paths.contains(&"de/404.html".to_string()));
assert!(paths.contains(&"de/sitemap.xml".to_string()));
assert!(!paths.contains(&"de/sitemap.xml".to_string()));
assert!(paths.contains(&"de/rss.xml".to_string()));
assert!(paths.contains(&"de/feed.xml".to_string()));
assert!(!paths.contains(&"de/feed.xml".to_string()));
assert!(paths.contains(&"de/atom.xml".to_string()));
assert!(paths.contains(&"fr/index.html".to_string()));
}
@@ -138,6 +138,7 @@ fn calendar_json_groups_posts_by_year_month_day() {
];
let json = build_calendar_json(&posts).unwrap();
assert!(!json.contains('\n'));
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["years"]["2024"], 3);
@@ -146,3 +147,15 @@ fn calendar_json_groups_posts_by_year_month_day() {
assert_eq!(parsed["days"]["2024-03-09"], 2);
assert_eq!(parsed["days"]["2024-04-09"], 1);
}
#[test]
fn calendar_json_uses_created_at_instead_of_published_at() {
let mut post = make_post("a", 1_710_000_000_000);
post.published_at = Some(1_712_678_400_000);
let json = build_calendar_json(&[post]).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["days"]["2024-03-09"], 1);
assert!(parsed["days"].get("2024-04-09").is_none());
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>Esmeralda</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,7 +14,11 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml" />
<link rel="alternate" hreflang="de" href="/2005/11/13/esmeralda" />
<link rel="alternate" hreflang="es" href="/2005/11/13/esmeralda/" />
<link rel="alternate" hreflang="de" href="/2005/11/13/esmeralda/" />
<link rel="alternate" hreflang="en" href="/en/2005/11/13/esmeralda/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
@@ -27,7 +31,6 @@
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script defer src="/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
@@ -49,7 +52,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..." data-search-no-results="Keine Ergebnisse gefunden"></div>
</div>
</div>
</nav>
@@ -252,7 +255,7 @@
hidden
>
<header class="blog-calendar-header">
<strong>Archivkalender</strong>
<strong>Archiv</strong>
<button
type="button"
class="blog-calendar-close"
@@ -276,32 +279,9 @@
</nav>
<div class="single-post-taxonomy" aria-label="Taxonomie">
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/category/picture/">picture</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/fotografie/">fotografie</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/makro/">makro</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/natur/">natur</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/spinne/">spinne</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/tiere/">tiere</a>
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><h3><a href="/media/esmeralda/">Esmeralda</a></h3>
<p><img src="/media/2005/11/eb0cf9d7-6fbd-4b74-9be3-759d6e16f240.jpg" alt="Esmeralda"></p>
</div>
<p><img src="/media/2005/11/eb0cf9d7-6fbd-4b74-9be3-759d6e16f240.jpg" alt="Esmeralda" /></p></div>
</article>
</main>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>cmux — Das Terminal für Multitasking</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,6 +14,10 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml" />
<link rel="alternate" hreflang="de" href="/2026/03/13/cmux-das-terminal-fur-multitasking/" />
<link rel="alternate" hreflang="en" href="/en/2026/03/13/cmux-das-terminal-fur-multitasking/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
<script defer src="/assets/d3.layout.cloud.js"></script>
@@ -25,7 +29,6 @@
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script defer src="/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
@@ -47,7 +50,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..." data-search-no-results="Keine Ergebnisse gefunden"></div>
</div>
</div>
</nav>
@@ -250,7 +253,7 @@
hidden
>
<header class="blog-calendar-header">
<strong>Archivkalender</strong>
<strong>Archiv</strong>
<button
type="button"
class="blog-calendar-close"
@@ -290,15 +293,14 @@
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://www.cmux.dev/de">cmux — Das Terminal für Multitasking</a> ist ein ziemlich geniales Terminal-Programm für die gerade durch Agentic Coding wieder verstärkt kommenden CLI Workflows. Und was mich begeistert: es hat eine saubere Persistierung von offenen Workspaces in denen man dann mehrere Tabs haben kann, so dass ich meine Arbeitsumgebung nicht ständig offen lassen muss. Ich habe immer gerne diverse Verzeichnisse direkt offen, weil ich zwischen mehreren hin und her wechsel wärend der Programmierung, das läuft mit CMUX deutlich besser als mit allen anderen.</p>
</div>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://www.cmux.dev/de">cmux — Das Terminal für Multitasking</a> ist ein ziemlich geniales Terminal-Programm für die gerade durch Agentic Coding wieder verstärkt kommenden CLI Workflows. Und was mich begeistert: es hat eine saubere Persistierung von offenen Workspaces in denen man dann mehrere Tabs haben kann, so dass ich meine Arbeitsumgebung nicht ständig offen lassen muss. Ich habe immer gerne diverse Verzeichnisse direkt offen, weil ich zwischen mehreren hin und her wechsel wärend der Programmierung, das läuft mit CMUX deutlich besser als mit allen anderen.</p></div>
</article>
<div class="single-post-backlinks" aria-label="Rückverweise">
<span class="single-post-backlinks-label">Verlinkt von</span>
<a class="single-post-taxonomy-bubble single-post-backlink-bubble" href="/2026/03/13/ghostty">ghostty</a>
<a class="single-post-taxonomy-bubble single-post-backlink-bubble" href="/2026/06/30/supacode/">supacode</a>
</div>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>Ghostty</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,6 +14,10 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml" />
<link rel="alternate" hreflang="de" href="/2026/03/13/ghostty/" />
<link rel="alternate" hreflang="en" href="/en/2026/03/13/ghostty/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
<script defer src="/assets/d3.layout.cloud.js"></script>
@@ -25,7 +29,6 @@
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script defer src="/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
@@ -47,7 +50,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..." data-search-no-results="Keine Ergebnisse gefunden"></div>
</div>
</div>
</nav>
@@ -250,7 +253,7 @@
hidden
>
<header class="blog-calendar-header">
<strong>Archivkalender</strong>
<strong>Archiv</strong>
<button
type="button"
class="blog-calendar-close"
@@ -290,9 +293,8 @@
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://ghostty.org/">Ghostty</a> ist die Basis, auf der <a href="/2026/03/13/cmux-das-terminal-fur-multitasking">cmux — Das Terminal für Multitasking</a> aufgebaut wurde. Generell ebenfalls ein sehr nettes Terminal, das auch deutlich schneller reagiert als das Standardterminal und auch schon sehr gut funktioniert. Was mir nicht gefiel, war dass Tabs nicht automatisch in den passenden Pfaden wieder neu geöffnet wurden, wenn das Programm beendet wurde. Ich habe einfach zu lange persistente Sessions, die ich immer wieder aufgreife. Das macht cmux einfach besser.</p>
</div>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://ghostty.org/">Ghostty</a> ist die Basis, auf der <a href="/en/2026/03/13/cmux-das-terminal-fur-multitasking/">cmux — Das Terminal für Multitasking</a> aufgebaut wurde. Generell ebenfalls ein sehr nettes Terminal, das auch deutlich schneller reagiert als das Standardterminal und auch schon sehr gut funktioniert. Was mir nicht gefiel, war dass Tabs nicht automatisch in den passenden Pfaden wieder neu geöffnet wurden, wenn das Programm beendet wurde. Ich habe einfach zu lange persistente Sessions, die ich immer wieder aufgreife. Das macht cmux einfach besser.</p></div>
</article>
</main>

View File

@@ -15,11 +15,12 @@
}
initialized = true;
var placeholder = root.getAttribute('data-search-placeholder') || 'Search...';
var zeroResults = root.getAttribute('data-search-no-results') || 'No results found';
new PagefindUI({
element: root,
showSubResults: true,
showImages: false,
translations: { placeholder: placeholder }
translations: { placeholder: placeholder, zero_results: zeroResults }
});
var input = root.querySelector('input');
if (input) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -25,7 +25,6 @@
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script defer src="/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
@@ -47,7 +46,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..." data-search-no-results="Keine Ergebnisse gefunden"></div>
</div>
</div>
</nav>
@@ -257,7 +256,7 @@
hidden
>
<header class="blog-calendar-header">
<strong>Archivkalender</strong>
<strong>Archiv</strong>
<button
type="button"
class="blog-calendar-close"
@@ -285,42 +284,50 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>22.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-02-22</span></aside>
<div class="archive-day-posts">
<div class="post">
<h2 class="post-title"><a href="/2026/02/22/steve-yegge-on-vibe-coding">Steve Yegge on Vibe Coding</a></h2>
<p>Steve Yegge spricht im Interview über die Zukunft der Programmierung und die Herausforderungen des Vibe Codings. Der Autor teilt seine Erfahrungen und sieht einen Umbruch vergleichbar mit der Einführung von Hochsprachen.</p>
<h2 class="post-title"><a href="/en/2026/02/22/steve-yegge-on-vibe-coding/">Steve Yegge on Vibe Coding</a></h2>
<div class="macro-youtube">
<iframe
src="https://www.youtube.com/embed/zuJyJP517Uw?rel=0"
title="YouTube-Video"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
</div>
<p>Interessantes Interview mit Steve Yegge (ja, genau, Flamer-Steve der früher oft durch sehr pointierte bis polemische Kommentare aufgefallen ist). Mein Lieblingszitat: &quot;we are entering the John Deere era of programming&quot; - yep, nicht mehr der kleine Landwird mit Familienbetrieb oder die kleine Kooperative, sondern die große Welle mit großen Codeengen die entstehen werden in Massen. Ebenfalls interessant der Punkt, dass Code-Merge in der nahen Zukunft das Bottleneck wird, weil 10x produktive Programmierer halt auch 10x an Code produzieren und mehrere von denen die gleichzeitig reorganisieren weils gerade mal dazwischen passt - autsch.</p>
<p>Meine eigenen Erfahrungen mit Vibe Coding: die Software, mit der ich jetzt mein Blog manage, ist komplett so entstanden. Ich baue auch an Tools für SecondLife Content per Vibe Coding. Ich nutze es auf der Arbeit für Prototypen aber auch für komplexe Desktop-Anwendungen für speziellere Zwecke. Und das ganze jetzt seit Dezember. Ich muss sagen, dass ich definitiv das Gefühl habe, das die Programmierung vor dem Umbruch steht, der vergleichbar mit der Einführung von Hochsprachen (etwas vor meiner Zeit) oder die Einführung von Runtimes mit Speichermanagement (Java, DotNet, ...) ist. Dementsprechend laut ist es gerade mit all den Pro- und Contra-Stimmen. Aber spannend ist es definitiv. Und die Welt wird sich für Programmierer definitiv ändern.</p>
</div>
<div class="post">
<h2 class="post-title"><a href="/2026/02/22/neue-software-langsam-benutzbar">bDS langsam benutzbar</a></h2>
<h2 class="post-title"><a href="/en/2026/02/22/neue-software-langsam-benutzbar/">bDS langsam benutzbar</a></h2>
<p>So langsam macht die neue Software Spaß, alles was man für die Benutzung und Erstellung braucht ist weitestgehend vorhanden. Ein paar kleine Schönheitsfehler hat sie moch und es gibt noch das eine oder andere Feature, das ich will, aber ich kann schon alles machen, was ich wirklich als Minimum brauche. Veröffentlichen geht jetzt einfach mit einem shell script:</p>
<pre><code class="language-bash" data-code-language="bash">#!/bin/sh
<p>So langsam macht die neue Software Spaß, alles was man für die Benutzung und Erstellung braucht ist weitestgehend vorhanden. Ein paar kleine Schönheitsfehler hat sie moch und es gibt noch das eine oder andere Feature, das ich will, aber ich kann schon alles machen, was ich wirklich als Minimum brauche. Veröffentlichen geht jetzt einfach mit einem shell script:</p>
<pre><code class="language-bash">#!/bin/sh
cd ~/Blogs/rfc1437.de/html
rsync -rav --delete \
--exclude=&#39;thumbnails/&#39; \
--exclude=&#39;media/&#39; \
--exclude='thumbnails/' \
--exclude='media/' \
* git.rfc1437.de:git-server/html/
cd ..
rsync -rav --delete thumbnails/ git.rfc1437.de:git-server/html/thumbnails/
rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/html/media/
rsync -rav --exclude='*.meta' --delete media/ git.rfc1437.de:git-server/html/media/
</code></pre>
<p>Ja, das war jetzt nur um meine Source-Formatierung zu testen. Und?</p>
</div>
</div>
@@ -333,19 +340,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>27.08.2023</span></aside>
<aside class="archive-day-marker"><span>2023-08-27</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/300930/schotten-totten-2">Schotten Totten 2</a> entwickelt sich zu einem unserer Lieblings-Spiele für zwei Spieler. Ein Remake des alten Schotten Totten (das es leider nicht mehr zu kaufen gibt unter dem Namen, nur als Battle Line, aber das eben nur auf Englisch). Im Gegensatz zum ersten Spiel jetzt asymmetrisch mit gerade genug Unterschied in der Spielweise, dass sich die beiden Seiten definitiv unterschiedlich anfühlen, aber trotzdem noch größtenteils die gleichen Sachen machen. Schnell aufgebaut, schnell gespielt und schnell weggeräumt mit genug Taktik um einen für ein paar Spiele zu fesseln. Und der Grafik-Stil ist einfach nett.</p>
<p><a href="https://boardgamegeek.com/boardgame/300930/schotten-totten-2">Schotten Totten 2</a> entwickelt sich zu einem unserer Lieblings-Spiele für zwei Spieler. Ein Remake des alten Schotten Totten (das es leider nicht mehr zu kaufen gibt unter dem Namen, nur als Battle Line, aber das eben nur auf Englisch). Im Gegensatz zum ersten Spiel jetzt asymmetrisch mit gerade genug Unterschied in der Spielweise, dass sich die beiden Seiten definitiv unterschiedlich anfühlen, aber trotzdem noch größtenteils die gleichen Sachen machen. Schnell aufgebaut, schnell gespielt und schnell weggeräumt mit genug Taktik um einen für ein paar Spiele zu fesseln. Und der Grafik-Stil ist einfach nett.</p>
</div>
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/197405/tak">Tak</a> ist ein ziemlich interessantes Spiel: <a href="https://en.wikipedia.org/wiki/Tak_(game)">inspiriert aus einem Fantasy-Roman von Patrick Rothfuss</a>, zum Leben erweckt als eine fiktives &quot;klassisches&quot; Strategiespiel. Das schöne daran: man kann es sich problemlos selber machen, wenn man will und geschickt ist. Die Regeln sind super simpel und leicht gelernt, das Spiel aber trickreich mit vielen Möglichkeiten dem Gegner Fallen zu stellen. Wird vermutlich für unseren nächsten Urlaub mitgenommen, weil praktisch zum Draussen spielen.</p>
<p><a href="https://boardgamegeek.com/boardgame/197405/tak">Tak</a> ist ein ziemlich interessantes Spiel: <a href="https://en.wikipedia.org/wiki/Tak_(game)">inspiriert aus einem Fantasy-Roman von Patrick Rothfuss</a>, zum Leben erweckt als eine fiktives &quot;klassisches&quot; Strategiespiel. Das schöne daran: man kann es sich problemlos selber machen, wenn man will und geschickt ist. Die Regeln sind super simpel und leicht gelernt, das Spiel aber trickreich mit vielen Möglichkeiten dem Gegner Fallen zu stellen. Wird vermutlich für unseren nächsten Urlaub mitgenommen, weil praktisch zum Draussen spielen.</p>
</div>
</div>
@@ -358,18 +363,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>19.08.2023</span></aside>
<aside class="archive-day-marker"><span>2023-08-19</span></aside>
<div class="archive-day-posts">
<div class="post">
<h2 class="post-title"><a href="/2023/08/19/mal-wieder-auf-linux">Mal wieder auf Linux ...</a></h2>
<h2 class="post-title"><a href="/en/2023/08/19/mal-wieder-auf-linux/">Mal wieder auf Linux ...</a></h2>
<p>... und natürlich tuts Surround-Sound meines Notebooks nicht mehr, aus Gründen. Und Fingerprint-Login macht weitaus weniger Sinn, wenn man nach Login dann doch das Passwort für den Keyring eingeben muss. Wenn der wenigstens erst beim ersten Bedarf nach einem Passwort käme, dann wärs ja ok, aber der geht direkt nach Login auf. Bah.</p>
<p>... und natürlich tuts Surround-Sound meines Notebooks nicht mehr, aus Gründen. Und Fingerprint-Login macht weitaus weniger Sinn, wenn man nach Login dann doch das Passwort für den Keyring eingeben muss. Wenn der wenigstens erst beim ersten Bedarf nach einem Passwort käme, dann wärs ja ok, aber der geht direkt nach Login auf. Bah.</p>
<p>Ansonsten bin ich allerdings angenehm überrascht davon, wie gut die Linux-Unterstützung für das Lenovo T480 ist. Alles andere funktioniert bisher tadellos.</p>
</div>
</div>
@@ -382,13 +386,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>06.11.2022</span></aside>
<aside class="archive-day-marker"><span>2022-11-06</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://www.moxfield.com/users/rfc1437">Prime Time for Prime Slime</a> ist die zweite meiner &quot;reinventing the past&quot;-Decklisten. Es ist tatsächlich mein erstes Commander-Precon-Deck - Mimeoplasm - einfach auf 11 aufgepeppt. Es ist irgendwie lustig, wie ich am Thema des Decks festgehalten habe, es vom Precon zu Ooze-Tribal und später zu Reanimator übergegangen bin, es dann in Muldrotha-Combo verwandelt und wieder zu Mimeoplasm zurückgebracht habe, als das Prime Slime Secret Lair herauskam (ich liebe den Kunststil), dieses Mal voll auf Necrotic Ooze-Combo. Es macht einen Haufen Spaß zu spielen, deshalb hat sich einige der alten Sachen aus meiner Sammlung in dieses Deck geschlichen. #EDH #MtG #MagicTheGathering</p>
<p><a href="https://www.moxfield.com/users/rfc1437">Prime Time for Prime Slime</a> is the second of my &quot;reinventing the past&quot; deck lists. It is actually my first commander precon deck - Mimeoplasm - just spiced up to 11. It kinda is funny how I stuck with the theme of the deck, transitioning it from the precon to Ooze tribal and later reanimator, then turned it into Muldrotha combo and again turned it back to Mimeoplasm, when the Prime Slime secret lair came out (I love the art style), going full on Necrotic Ooze combo this time. It is a ton of fun to play, which is why some of the old stuff from my collection made it's way into that deck. #EDH #MtG #MagicTheGathering</p>
</div>
</div>
@@ -401,19 +404,18 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>27.03.2020</span></aside>
<aside class="archive-day-marker"><span>2020-03-27</span></aside>
<div class="archive-day-posts">
<div class="post">
<h2 class="post-title"><a href="/2020/03/27/brettspielrunde-wie-macht-man-das-in-zeiten-von-corona">Brettspielrunde - wie macht man das in Zeiten von Corona?</a></h2>
<h2 class="post-title"><a href="/en/2020/03/27/brettspielrunde-wie-macht-man-das-in-zeiten-von-corona/">Brettspielrunde - wie macht man das in Zeiten von Corona?</a></h2>
<p>Also wir haben da eine Lösung gefunden. Und es lief richtig gut - 6 Spieler waren dabei, jeder zu Hause. Bei mir war das Spiel und eine Kamerakonstruktion, um das Bild ins Internet zu stellen - und mit Microsoft Teams dann allen nach Hause zu liefern. Dazu dann Headsets und los gings, Spiel erklären, spielen, gemeinsam drüber reden - all das geht auch zu Zeiten des Social Distancing.</p>
<p><img src="/media/2020/03/f1cedfa3-bf94-44a5-92e2-16bdd4cb6e29.jpg" alt="IMG 20200326 181231"></p>
<p>Also wir haben da eine Lösung gefunden. Und es lief richtig gut - 6 Spieler waren dabei, jeder zu Hause. Bei mir war das Spiel und eine Kamerakonstruktion, um das Bild ins Internet zu stellen - und mit Microsoft Teams dann allen nach Hause zu liefern. Dazu dann Headsets und los gings, Spiel erklären, spielen, gemeinsam drüber reden - all das geht auch zu Zeiten des Social Distancing.</p>
<p><img src="/media/2020/03/f1cedfa3-bf94-44a5-92e2-16bdd4cb6e29.jpg" alt="IMG 20200326 181231" /></p>
<p>Das Kamera-Stativ hatte ich eh schon, der kleine Schwenkarm mit Handy-Halterung kostet 35€ und das Handy - sowas hat man oft ja schon da. Dann auf dem Handy eine kleine App - &quot;IP Webcam&quot; - mit der die Kamera des Handy über eine Webseite erreichbar wird. Und auf dem Notebook Teams, mit dem ich das Fenster des Browsers mit allen Teilnehmern geteilt habe. Und los kanns gehen!</p>
</div>
</div>
@@ -426,13 +428,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>20.02.2020</span></aside>
<aside class="archive-day-marker"><span>2020-02-20</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="http://tappedout.net/mtg-decks/bears-on-a-plane/">Bears on a Plane</a> ist mein Versuch ein effektives Prison-Deck in cEDH zu bauen. Erste Versuche mit dem Deck waren dementsprechend &quot;disgusting&quot;. Kann nur sporadisch getestet werden wegen Anger-Management-Issues der Playgroup ...</p>
<p><a href="http://tappedout.net/mtg-decks/bears-on-a-plane/">Bears on a Plane</a> ist mein Versuch ein effektives Prison-Deck in cEDH zu bauen. Erste Versuche mit dem Deck waren dementsprechend &quot;disgusting&quot;. Kann nur sporadisch getestet werden wegen Anger-Management-Issues der Playgroup ...</p>
</div>
</div>
@@ -445,13 +446,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>02.08.2019</span></aside>
<aside class="archive-day-marker"><span>2019-08-02</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://www.fantasyflightgames.com/en/news/2019/7/31/marvel-champions/">Marvel Champions - Fantasy Flight Games</a> Wow, das kommt unerwartet. Es sieht stark nach einem weiteren Ableger des LOTR Systems aus, allerdings auf den ersten Blick etwas streamlined. Aber wenn das ganze trotzdem die Spieltiefe von LOTR erreicht, wärs klasse. Ich habe zwar mitlerweile eine gut laufende Magic Spielrunde, aber so für zwischendurch fand ich LOTR immer eigentlich recht spannend, da viele Spielentscheidungen (speziell der Szenario-spezifische Bau eines Decks) sehr nah an Magic ran kommen.</p>
<p><a href="https://www.fantasyflightgames.com/en/news/2019/7/31/marvel-champions/">Marvel Champions - Fantasy Flight Games</a> Wow, das kommt unerwartet. Es sieht stark nach einem weiteren Ableger des LOTR Systems aus, allerdings auf den ersten Blick etwas streamlined. Aber wenn das ganze trotzdem die Spieltiefe von LOTR erreicht, wärs klasse. Ich habe zwar mitlerweile eine gut laufende Magic Spielrunde, aber so für zwischendurch fand ich LOTR immer eigentlich recht spannend, da viele Spielentscheidungen (speziell der Szenario-spezifische Bau eines Decks) sehr nah an Magic ran kommen.</p>
</div>
</div>
@@ -464,13 +464,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>03.06.2019</span></aside>
<aside class="archive-day-marker"><span>2019-06-03</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Der <a href="https://magic.wizards.com/en/articles/archive/news/london-mulligan-2019-06-03">London Mulligan</a> wird der offizielle Mulligan in Magic ab Core Set 2020. Klasse. Wir haben den in Commander ausprobiert und er war völlig ok. Und in Pauper wars auch gut. Klar, es gibt sicherlich ein paar Decks, die übermäßig profitieren, aber das kann man anderweitig regeln. Der neue Mulligan hilft einfach &quot;Nicht-Spiele&quot; aufgrund von mehrfachen schlechten Starthänden zu vermeiden. Es gibt nix nervigeres als ein Spiel im Mulligan zu verlieren, weil einfach jede Hand ohne Länder oder nur Länder war.</p>
<p>Der <a href="https://magic.wizards.com/en/articles/archive/news/london-mulligan-2019-06-03">London Mulligan</a> wird der offizielle Mulligan in Magic ab Core Set 2020. Klasse. Wir haben den in Commander ausprobiert und er war völlig ok. Und in Pauper wars auch gut. Klar, es gibt sicherlich ein paar Decks, die übermäßig profitieren, aber das kann man anderweitig regeln. Der neue Mulligan hilft einfach &quot;Nicht-Spiele&quot; aufgrund von mehrfachen schlechten Starthänden zu vermeiden. Es gibt nix nervigeres als ein Spiel im Mulligan zu verlieren, weil einfach jede Hand ohne Länder oder nur Länder war.</p>
</div>
</div>
@@ -483,19 +482,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>20.05.2019</span></aside>
<aside class="archive-day-marker"><span>2019-05-20</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://www.coolstuffinc.com/a/jimdavis-05202019-a-force-to-be-reckoned-with/">A Force to be Reckoned With</a> - die neue Karte &quot;Force of Vigor&quot; hat mal eben zwei meiner Decks deutlich getroffen. Jhoira als Artefakt-Storm-Deck und Paradox Arcum als Artefakt-Kombo sind beide ziemlich abhängig von ihren Artefakten und Enchantments, so eine Karte kann die mal eben abschalten. Instant-speed. Für lau. Autsch. Wenn andere Liste stärker auf Mana-Dorks statt Mana-Rocks gehen, haben sie genug grüne Karten um den FoV aktiv zu bekommen. Und dann bin ich raus. Jhoira kann sich vielleicht noch knapp halten, Storm hat in der Regel mehrere Vektoren, aber da Artefakte nur mit Shimmer Myr instant-speed werden (und der als Artefakt angreifbar bleibt), ist Arcum wirklich hart betroffen.</p>
<p><a href="https://www.coolstuffinc.com/a/jimdavis-05202019-a-force-to-be-reckoned-with/">A Force to be Reckoned With</a> - die neue Karte &quot;Force of Vigor&quot; hat mal eben zwei meiner Decks deutlich getroffen. Jhoira als Artefakt-Storm-Deck und Paradox Arcum als Artefakt-Kombo sind beide ziemlich abhängig von ihren Artefakten und Enchantments, so eine Karte kann die mal eben abschalten. Instant-speed. Für lau. Autsch. Wenn andere Liste stärker auf Mana-Dorks statt Mana-Rocks gehen, haben sie genug grüne Karten um den FoV aktiv zu bekommen. Und dann bin ich raus. Jhoira kann sich vielleicht noch knapp halten, Storm hat in der Regel mehrere Vektoren, aber da Artefakte nur mit Shimmer Myr instant-speed werden (und der als Artefakt angreifbar bleibt), ist Arcum wirklich hart betroffen.</p>
</div>
<div class="post">
<p><a href="https://magic.wizards.com/en/articles/archive/news/may-20-2019-banned-and-restricted-announcement">May 20, 2019 Banned and Restricted Announcement</a> - da hat mir Wizards of the Coast mal eben zwei meiner Decks komplett neutralisiert - Izzet Blitz braucht die kostenlosen Spells um seine Kreaturen zu pumpen oder Direktschaden zu aktivieren und Skred Delver brauchte sie um die Hand zu füllen im Mittelspiel. Autsch. Ok, in letzter Zeit hab ich meistens Tron gespielt (oder mal GB Tortex, wenn mir nach wilden Boardstates war), aber trotzdem, Skred Delver war immer noch mein Lieblingsdeck wegen seiner explosiven Comebacks.</p>
<p><a href="https://magic.wizards.com/en/articles/archive/news/may-20-2019-banned-and-restricted-announcement">May 20, 2019 Banned and Restricted Announcement</a> - da hat mir Wizards of the Coast mal eben zwei meiner Decks komplett neutralisiert - Izzet Blitz braucht die kostenlosen Spells um seine Kreaturen zu pumpen oder Direktschaden zu aktivieren und Skred Delver brauchte sie um die Hand zu füllen im Mittelspiel. Autsch. Ok, in letzter Zeit hab ich meistens Tron gespielt (oder mal GB Tortex, wenn mir nach wilden Boardstates war), aber trotzdem, Skred Delver war immer noch mein Lieblingsdeck wegen seiner explosiven Comebacks.</p>
</div>
</div>
@@ -508,17 +505,16 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>25.04.2019</span></aside>
<aside class="archive-day-marker"><span>2019-04-25</span></aside>
<div class="archive-day-posts">
<div class="post">
<h2 class="post-title"><a href="/2019/04/25/metaowl-is-gone">Metaowl is gone</a></h2>
<p>Ok Leute, Metaowl ist heute den Weg alles digitalen gegangen: das war eine urmel-alte WP Installation, die dahinter lief, mit diversen Anpassungen. Das war einfach nicht mehr zu retten, das mit neueren PHP Versionen zum Fliegen zu kriegen frisst einfach zu viel Zeit. Ich glaub auch nicht, dass da irgendwer noch groß bemerkt, dass die Eule nicht mehr da ist. War einfach nur ein Stück Netzgeschichte.</p>
<h2 class="post-title"><a href="/en/2019/04/25/metaowl-is-gone/">Metaowl is gone</a></h2>
<p>Ok Leute, Metaowl ist heute den Weg alles digitalen gegangen: das war eine urmel-alte WP Installation, die dahinter lief, mit diversen Anpassungen. Das war einfach nicht mehr zu retten, das mit neueren PHP Versionen zum Fliegen zu kriegen frisst einfach zu viel Zeit. Ich glaub auch nicht, dass da irgendwer noch groß bemerkt, dass die Eule nicht mehr da ist. War einfach nur ein Stück Netzgeschichte.</p>
</div>
</div>
@@ -531,13 +527,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>22.02.2019</span></aside>
<aside class="archive-day-marker"><span>2019-02-22</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Ich mag die neue Variante des MTG Mulligan, die in London getestet werden soll. Definitiv muss geschaut werden, ob dadurch Combo nicht zu sehr bevorzugt wird, aber das derzeitige Mana-System und die Struktur des derzeitigen Mulligan führt einfach viel zu häufig zu non-Games, besonders in meinem Lieblingsformat EDH. Und es fühlt sich einfach dämlich an, wenn du auf 4 runter gehen musst und immer noch nur eine mistige Kartenhand hast, oder dann einfach scoopst - und die anderen ihre guten Starthände wegschieben können, weil sie sonst ein Spiel mit N-1 Spielern hätten, bei dem einer zuguckt. Klar, gerade Multiplayer kann man dem anderen direkt einfach einen weiteren 7er Mulligan anbieten, aber die neue Variante würde das nicht nötig machen - ich kann mir halt immer die besten N Karten aus 7 aussuchen, da ist auch ein 4er Mulligan schon verschmerzbar.</p>
<p>Ich mag die neue Variante des MTG Mulligan, die in London getestet werden soll. Definitiv muss geschaut werden, ob dadurch Combo nicht zu sehr bevorzugt wird, aber das derzeitige Mana-System und die Struktur des derzeitigen Mulligan führt einfach viel zu häufig zu non-Games, besonders in meinem Lieblingsformat EDH. Und es fühlt sich einfach dämlich an, wenn du auf 4 runter gehen musst und immer noch nur eine mistige Kartenhand hast, oder dann einfach scoopst - und die anderen ihre guten Starthände wegschieben können, weil sie sonst ein Spiel mit N-1 Spielern hätten, bei dem einer zuguckt. Klar, gerade Multiplayer kann man dem anderen direkt einfach einen weiteren 7er Mulligan anbieten, aber die neue Variante würde das nicht nötig machen - ich kann mir halt immer die besten N Karten aus 7 aussuchen, da ist auch ein 4er Mulligan schon verschmerzbar.</p>
</div>
</div>
@@ -550,13 +545,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>11.09.2017</span></aside>
<aside class="archive-day-marker"><span>2017-09-11</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/187617/nemos-war-second-edition">Nemo&#39;s War (second edition)</a> habe ich jetzt schon zweimal gespielt und es mausert sich zu einem meiner Lieblings-Solo-Spielen. Es packt die Athmosphäre von Jules Vernes Büchern gut in ein unterhaltsames Spiel. Auch wenn natürlich das Thema nur durch die Karten und die Grafik des Spiels getragen wird. Das Spielsystem basieet natürlich auf viel Zufall, der aber erstaunlich gut gemanaged werden kann. Das Ergebnis ist recht viel Strategie in einem doch relativ klassischen Roll-for-Success System. Die Spieldauer verhindert allerdings, das ich es öfter auf den Tisch bekomme. Trotzdem steht es in meiner Liste weit oben.</p>
<p><a href="https://boardgamegeek.com/boardgame/187617/nemos-war-second-edition">Nemo's War (second edition)</a> habe ich jetzt schon zweimal gespielt und es mausert sich zu einem meiner Lieblings-Solo-Spielen. Es packt die Athmosphäre von Jules Vernes Büchern gut in ein unterhaltsames Spiel. Auch wenn natürlich das Thema nur durch die Karten und die Grafik des Spiels getragen wird. Das Spielsystem basieet natürlich auf viel Zufall, der aber erstaunlich gut gemanaged werden kann. Das Ergebnis ist recht viel Strategie in einem doch relativ klassischen Roll-for-Success System. Die Spieldauer verhindert allerdings, das ich es öfter auf den Tisch bekomme. Trotzdem steht es in meiner Liste weit oben.</p>
</div>
</div>
@@ -569,13 +563,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>14.08.2017</span></aside>
<aside class="archive-day-marker"><span>2017-08-13</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Vor wenigen Tagen kam <a href="https://boardgamegeek.com/boardgame/180263/7th-continent">The 7th Continent</a> endlich nach fast 2 Jahren bei mir an. Wow, hat sich das Warten gelohnt! Das Spiel ist fantastisch geworden und hat sich bei mir in die Top-Liste gespielt. Wenn der Spaß anhält, ist es ein klarer Favorit für die 10. Einzig die fummelig winzigen Miniaturen hätten auch wegbleiben können, mir sind die Pappaufsteller da zehnmal lieber. Aber das Spiel selber - einfach grandios. Sehr schön gezeichnete Karten, klare Symbolik und simples Spielsystem - alles zusammen bildet ein grafisches choose-your-own-adventure Spiel. Sieht einfach nur toll aus auf dem Tisch. Dazu ein cleverer Save/Restore Mechanismus, der das Wegpacken zwecks Tisch freimachen (ab und zu muss man ja auch mal essen) trivial macht. Beim Weiterspielen hat man zwar ein wenig Sortierarbeit, aber das ist auch übersichtlich. Und die Story, die man durchlebt, ist wirklich die Zeit wert die man investiert. Dazu eine Reihe von Flüchen, denen man sich aussetzen kann und viele Abenteurer, die man auf die Reise schicken kann, das bietet schon einiges an Wiederspielwert. Auch wenn man natürlich auf die Dauer den Kontinent kennen lernt, wird es glaube ich nicht langweilig. Das wird noch viel Spielfreude bringen und für den Herbst ist schon der nächste Kickstarter mit weiteren Flüchen angekündigt. Ich glaub da bin ich dann wieder dabei.</p>
<p>Vor wenigen Tagen kam <a href="https://boardgamegeek.com/boardgame/180263/7th-continent">The 7th Continent</a> endlich nach fast 2 Jahren bei mir an. Wow, hat sich das Warten gelohnt! Das Spiel ist fantastisch geworden und hat sich bei mir in die Top-Liste gespielt. Wenn der Spaß anhält, ist es ein klarer Favorit für die 10. Einzig die fummelig winzigen Miniaturen hätten auch wegbleiben können, mir sind die Pappaufsteller da zehnmal lieber. Aber das Spiel selber - einfach grandios. Sehr schön gezeichnete Karten, klare Symbolik und simples Spielsystem - alles zusammen bildet ein grafisches choose-your-own-adventure Spiel. Sieht einfach nur toll aus auf dem Tisch. Dazu ein cleverer Save/Restore Mechanismus, der das Wegpacken zwecks Tisch freimachen (ab und zu muss man ja auch mal essen) trivial macht. Beim Weiterspielen hat man zwar ein wenig Sortierarbeit, aber das ist auch übersichtlich. Und die Story, die man durchlebt, ist wirklich die Zeit wert die man investiert. Dazu eine Reihe von Flüchen, denen man sich aussetzen kann und viele Abenteurer, die man auf die Reise schicken kann, das bietet schon einiges an Wiederspielwert. Auch wenn man natürlich auf die Dauer den Kontinent kennen lernt, wird es glaube ich nicht langweilig. Das wird noch viel Spielfreude bringen und für den Herbst ist schon der nächste Kickstarter mit weiteren Flüchen angekündigt. Ich glaub da bin ich dann wieder dabei.</p>
</div>
</div>
@@ -588,13 +581,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>06.11.2016</span></aside>
<aside class="archive-day-marker"><span>2016-11-06</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/181521/warhammer-quest-adventure-card-game">Warhammer Quest: The Adventure Card Game</a> habe ich schon eine Weile rumliegen, aber erst heute die Chance genabt, es auch mal zu spielen. Das Spiel ist sehr ähnlich zu Space Hulk: Death Angel, aber deutlich ausgebaut. Schon im Tutorial kommt deutlich mehr Entscheidungsraum zum Tragen und man hat deutlich mehr das Gefühl von Kontrolle. Es macht Spaß und die verschiedenen Szenarien versprechen noch mehr davon. Speziell das aufwerten der Aktionskarten sieht interessant aus, ist aber leider nicht Teil des Tutorials. Macht aber definitiv Lust auf mehr. Setup ist allerdings deutlich mehr als bei SH:DA. Von daher mal sehen, wie oft es wirklich auf dem Tisch landet, zumal ja auch noch das Arkahm Horror LCG angekündigt ist, das in eine ähnliche Richtung geht.</p>
<p><a href="https://boardgamegeek.com/boardgame/181521/warhammer-quest-adventure-card-game">Warhammer Quest: The Adventure Card Game</a> habe ich schon eine Weile rumliegen, aber erst heute die Chance genabt, es auch mal zu spielen. Das Spiel ist sehr ähnlich zu Space Hulk: Death Angel, aber deutlich ausgebaut. Schon im Tutorial kommt deutlich mehr Entscheidungsraum zum Tragen und man hat deutlich mehr das Gefühl von Kontrolle. Es macht Spaß und die verschiedenen Szenarien versprechen noch mehr davon. Speziell das aufwerten der Aktionskarten sieht interessant aus, ist aber leider nicht Teil des Tutorials. Macht aber definitiv Lust auf mehr. Setup ist allerdings deutlich mehr als bei SH:DA. Von daher mal sehen, wie oft es wirklich auf dem Tisch landet, zumal ja auch noch das Arkahm Horror LCG angekündigt ist, das in eine ähnliche Richtung geht.</p>
</div>
</div>
@@ -607,13 +599,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>30.10.2016</span></aside>
<aside class="archive-day-marker"><span>2016-10-30</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/192458/51st-state-master-set">51st State: Master Set</a> ist eines der neuesten Spiele von Portal Games und ein Remake des alte 51st State mit allen Erweiterungen und überarbeiteten Regeln. Bringt praktisch Imperial Settlers zurück in die post-apokalyptische Welt von 51st State. Mir gefällt es sehr gzt. Die Asymmetrie der Faktionen ist nicht so stark ausgeprägt wie in Imperial Settlers (dort ja durch die eigenen Kartenstapel), aber das Spiel ist viel flüssiger und fühlt sich schneller und interaktiver an. Und das Solospiel ist einfacher zu spielen. Insgesamt gefällt es mir sehr gut, vor allem für das Solospiel sogar besser als Imperial Settlers (wobei ich dort noch die völker-spezifischen AI und die Kampagne ausprobieren muss).</p>
<p><a href="https://boardgamegeek.com/boardgame/192458/51st-state-master-set">51st State: Master Set</a> ist eines der neuesten Spiele von Portal Games und ein Remake des alte 51st State mit allen Erweiterungen und überarbeiteten Regeln. Bringt praktisch Imperial Settlers zurück in die post-apokalyptische Welt von 51st State. Mir gefällt es sehr gzt. Die Asymmetrie der Faktionen ist nicht so stark ausgeprägt wie in Imperial Settlers (dort ja durch die eigenen Kartenstapel), aber das Spiel ist viel flüssiger und fühlt sich schneller und interaktiver an. Und das Solospiel ist einfacher zu spielen. Insgesamt gefällt es mir sehr gut, vor allem für das Solospiel sogar besser als Imperial Settlers (wobei ich dort noch die völker-spezifischen AI und die Kampagne ausprobieren muss).</p>
</div>
</div>
@@ -626,13 +617,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>18.10.2016</span></aside>
<aside class="archive-day-marker"><span>2016-10-17</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/198773/codenames-pictures">Codenames: Pictures</a> ist das Schwesterspiel des Spiel des Jahres - nur eben mit Bildern, statt Wörtern. Mit Juliana mal mit den Zwei-Spieler-Regeln ausprobiert, und es funktioniert erstaunlich gut. Natürlich nichts, das man ständig zu zweit spielen würde, aber das war sowieso nur eine Trainingsrunde für den nächsten Spieleabend. Da wird es aber definitiv in der Runde ausprobiert.</p>
<p><a href="https://boardgamegeek.com/boardgame/198773/codenames-pictures">Codenames: Pictures</a> ist das Schwesterspiel des Spiel des Jahres - nur eben mit Bildern, statt Wörtern. Mit Juliana mal mit den Zwei-Spieler-Regeln ausprobiert, und es funktioniert erstaunlich gut. Natürlich nichts, das man ständig zu zweit spielen würde, aber das war sowieso nur eine Trainingsrunde für den nächsten Spieleabend. Da wird es aber definitiv in der Runde ausprobiert.</p>
</div>
</div>
@@ -645,13 +635,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>16.10.2016</span></aside>
<aside class="archive-day-marker"><span>2016-10-16</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/161417/red7">Red7</a> ist ein kleines und schnelles Spiel für zwischendurch. Ideal für die Wartezeit auf das Essen oder sogar im Flugzeug, da es nur minimalen Platz braucht. Und dabei trotz einfacher Regeln und schnellem Spiel durchaus was zum Denken liefert. Außerdem ist das Spiel modular in den Regeln und kann in der Komplexität angepasst werden. Alles in allem ein wirklich nettes Spiel, das sicherlich öfter mal bei uns auf den Tisch kommen wird.</p>
<p><a href="https://boardgamegeek.com/boardgame/161417/red7">Red7</a> ist ein kleines und schnelles Spiel für zwischendurch. Ideal für die Wartezeit auf das Essen oder sogar im Flugzeug, da es nur minimalen Platz braucht. Und dabei trotz einfacher Regeln und schnellem Spiel durchaus was zum Denken liefert. Außerdem ist das Spiel modular in den Regeln und kann in der Komplexität angepasst werden. Alles in allem ein wirklich nettes Spiel, das sicherlich öfter mal bei uns auf den Tisch kommen wird.</p>
</div>
</div>
@@ -664,13 +653,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>11.09.2016</span></aside>
<aside class="archive-day-marker"><span>2016-09-11</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/174785/mare-nostrum-empires">Mare Nostrum: Empires</a> ist am Freitag endlich nach fast einem Jahr Extrazeit angekommen. War einer der komplizierteren Kickstarter. Aber für das Spiel hat sich das Warten gelohnt. Die Komponenten sind - wie für Academy Games typisch - fantastisch. Das Spiel selber ist erstaunlich übersichtlich für ein Spiel mit diesem Thema (Kulturen im Mittelmeerraum mit Handel und Krieg) und spielt sich auch sehr flott. Mein erstes Solo-Spiel (zwei Kulturen plus Barbaren) hatte eine sogar für mich selbst überraschende Wendung - am Anfang lag Karthago vorne, kam auf 4 Wunder (das fünfte hätte den Sieg gebracht), aber Rom konnte mit Schwenk auf Militär den Handel unter Druck setzen und den Sieg durch Dominanz in den Bereichen Handel, Kultur und Militär sichern. Klasse System und ich hoffe es noch öfter auf den Tisch zu bringen.</p>
<p><a href="https://boardgamegeek.com/boardgame/174785/mare-nostrum-empires">Mare Nostrum: Empires</a> ist am Freitag endlich nach fast einem Jahr Extrazeit angekommen. War einer der komplizierteren Kickstarter. Aber für das Spiel hat sich das Warten gelohnt. Die Komponenten sind - wie für Academy Games typisch - fantastisch. Das Spiel selber ist erstaunlich übersichtlich für ein Spiel mit diesem Thema (Kulturen im Mittelmeerraum mit Handel und Krieg) und spielt sich auch sehr flott. Mein erstes Solo-Spiel (zwei Kulturen plus Barbaren) hatte eine sogar für mich selbst überraschende Wendung - am Anfang lag Karthago vorne, kam auf 4 Wunder (das fünfte hätte den Sieg gebracht), aber Rom konnte mit Schwenk auf Militär den Handel unter Druck setzen und den Sieg durch Dominanz in den Bereichen Handel, Kultur und Militär sichern. Klasse System und ich hoffe es noch öfter auf den Tisch zu bringen.</p>
</div>
</div>
@@ -683,13 +671,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>28.08.2016</span></aside>
<aside class="archive-day-marker"><span>2016-08-28</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/68448/7-wonders">7 Wonders</a> spielen Juliana und ich normalerweise in der Duell-Version. Aber weil unsere Donnerstagsrunde größer wird, haben wir uns auch das große Spiel zugelegt - mit der Städte-Erweiterung kann man das mit bis zu 8 Spielern spielen. Heute war die erste Trainingseinheit mit der in der Box enthaltenen 2-Spieler Variante. Definitiv umständlicher als das kleine Duell, aber ebenso definitiv erkennbar, wie das Spiel mit größeren Runden laufen kann - und macht Spaß auf mehr. Gerade genug Interaktivität um nicht zum Mehrspieler-Solitär zu verkommen, aber genug Strategie um nicht zur reinen Zufallsnummer zu werden. Gefällt mir sehr gut.</p>
<p><a href="https://boardgamegeek.com/boardgame/68448/7-wonders">7 Wonders</a> spielen Juliana und ich normalerweise in der Duell-Version. Aber weil unsere Donnerstagsrunde größer wird, haben wir uns auch das große Spiel zugelegt - mit der Städte-Erweiterung kann man das mit bis zu 8 Spielern spielen. Heute war die erste Trainingseinheit mit der in der Box enthaltenen 2-Spieler Variante. Definitiv umständlicher als das kleine Duell, aber ebenso definitiv erkennbar, wie das Spiel mit größeren Runden laufen kann - und macht Spaß auf mehr. Gerade genug Interaktivität um nicht zum Mehrspieler-Solitär zu verkommen, aber genug Strategie um nicht zur reinen Zufallsnummer zu werden. Gefällt mir sehr gut.</p>
</div>
</div>
@@ -702,13 +689,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>31.07.2016</span></aside>
<aside class="archive-day-marker"><span>2016-07-31</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/130060/ohne-furcht-und-adel">Ohne Furcht und Adel</a> ist ja praktisch ein Oldie, aber für mich und Juliana war es neu. Ich hatte es hauptsächlich besorgt, um etwas kleines für größere Runden zu haben, da unser wöchentlicher Brettspielabend langsam wächst. Wir haben bisher nur zu zweit gespielt, aber auch da ist das Spiel sehr nett. Es erinnert ein bisschen an San Juan, mit den primär als Geldquelle (und Punktequelle) dienenden Gebäuden, den violetten Gebäuden mit Sonderfunktionen und der Rollenwahl. Sehr interaktiv und zumindestens mit zwei Spielern sogar mit strategischen Elementen, die aber in größeren Runden wohl zugunsten rein taktischem Spiel verdrängt werden. Macht aber definitiv Spaß.</p>
<p><a href="https://boardgamegeek.com/boardgame/130060/ohne-furcht-und-adel">Ohne Furcht und Adel</a> ist ja praktisch ein Oldie, aber für mich und Juliana war es neu. Ich hatte es hauptsächlich besorgt, um etwas kleines für größere Runden zu haben, da unser wöchentlicher Brettspielabend langsam wächst. Wir haben bisher nur zu zweit gespielt, aber auch da ist das Spiel sehr nett. Es erinnert ein bisschen an San Juan, mit den primär als Geldquelle (und Punktequelle) dienenden Gebäuden, den violetten Gebäuden mit Sonderfunktionen und der Rollenwahl. Sehr interaktiv und zumindestens mit zwei Spielern sogar mit strategischen Elementen, die aber in größeren Runden wohl zugunsten rein taktischem Spiel verdrängt werden. Macht aber definitiv Spaß.</p>
</div>
</div>
@@ -721,13 +707,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>03.07.2016</span></aside>
<aside class="archive-day-marker"><span>2016-07-03</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/173064/leaving-earth">Leaving Earth</a> is ein Spiel, in dem die Spieler eine Raumfahrtagentur leiten und Missionen durchführen müssen. Die Soloversion stellt dabei einige spannende Aufgaben, da man von den Missionspunkten die zu gewinnen sind, mindestens die Hälfte bekommen muss. Was schon ganz schön schwer ist - in meinem ersten Spiel musste ich, um überhaupt eine Chance auf einen Solosieg zu haben, erst Venus untersuchen um festzustellen, dass man dort nicht landen kann (wodurch ich die Venusmissionen abwerfen und damit die Punktelast reduzieren konnte) und dann eine bemannte Marsmission durchführen. Letzteres ist dann an der Zeit und mangelnden Ausstattung mit Antrieb gescheitert. Aber im Kopf kreisen schon Ideen, wie das nächste Mal das ganze besser ablaufen kann. Und das ist für ein hartes Optimierspiel schon ein gutes Zeichen - hier wird nicht geballert oder alte Männer mit Atombomben zu Asteroiden geschickt, hier plant man sein Budget, seine mehrjährigen Missionen und das Risiko, das man eingehen will. Cool. For Science!</p>
<p><a href="https://boardgamegeek.com/boardgame/173064/leaving-earth">Leaving Earth</a> is ein Spiel, in dem die Spieler eine Raumfahrtagentur leiten und Missionen durchführen müssen. Die Soloversion stellt dabei einige spannende Aufgaben, da man von den Missionspunkten die zu gewinnen sind, mindestens die Hälfte bekommen muss. Was schon ganz schön schwer ist - in meinem ersten Spiel musste ich, um überhaupt eine Chance auf einen Solosieg zu haben, erst Venus untersuchen um festzustellen, dass man dort nicht landen kann (wodurch ich die Venusmissionen abwerfen und damit die Punktelast reduzieren konnte) und dann eine bemannte Marsmission durchführen. Letzteres ist dann an der Zeit und mangelnden Ausstattung mit Antrieb gescheitert. Aber im Kopf kreisen schon Ideen, wie das nächste Mal das ganze besser ablaufen kann. Und das ist für ein hartes Optimierspiel schon ein gutes Zeichen - hier wird nicht geballert oder alte Männer mit Atombomben zu Asteroiden geschickt, hier plant man sein Budget, seine mehrjährigen Missionen und das Risiko, das man eingehen will. Cool. For Science!</p>
</div>
</div>
@@ -740,13 +725,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>26.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-26</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/128996/1775-rebellion">1775: Rebellion</a> kam heute zum zweiten Mal auf den Tisch und war wieder ein voller Erfolg: unentschieden, beide Seiten kontrollierten 5 Kolonien und Nordamerika wurde damit zu einer südlichen Provinz Französisch-Kanadas. Das beste von allen Ergebnissen wenn man sich heutige Präsidentschaftskandidaten anguckt. Mir gefällt das Spiel sehr - die Strategien müssen wirklich ausgewogen sein, wenn man die südlichen Kolonien halten will, muss man sie auch verteidigen, gleichzeitig will man die nördlicheren nicht verlieren, weil dort viele Kolonien sind. England ist angreifbar, weil ganz im Norden zwei schwer verteidigbare Kolonien liegen, in die die Amerikaner einfallen können. Ich war zwei Kolonien vorne, als ich die letzte Runde eingeläutet habe, habe aber dann zwei Kolonien verloren - und möglicherweise hätte Juliana sogar drei angreifen können. Juliana gefällt es auch, wird also sicher wieder auf den Tisch kommen.</p>
<p><a href="https://boardgamegeek.com/boardgame/128996/1775-rebellion">1775: Rebellion</a> kam heute zum zweiten Mal auf den Tisch und war wieder ein voller Erfolg: unentschieden, beide Seiten kontrollierten 5 Kolonien und Nordamerika wurde damit zu einer südlichen Provinz Französisch-Kanadas. Das beste von allen Ergebnissen wenn man sich heutige Präsidentschaftskandidaten anguckt. Mir gefällt das Spiel sehr - die Strategien müssen wirklich ausgewogen sein, wenn man die südlichen Kolonien halten will, muss man sie auch verteidigen, gleichzeitig will man die nördlicheren nicht verlieren, weil dort viele Kolonien sind. England ist angreifbar, weil ganz im Norden zwei schwer verteidigbare Kolonien liegen, in die die Amerikaner einfallen können. Ich war zwei Kolonien vorne, als ich die letzte Runde eingeläutet habe, habe aber dann zwei Kolonien verloren - und möglicherweise hätte Juliana sogar drei angreifen können. Juliana gefällt es auch, wird also sicher wieder auf den Tisch kommen.</p>
</div>
</div>
@@ -759,13 +743,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>25.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-25</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/169986/night-man">Night of Man</a> ist ein taktisches Kampfspiel, Aliens vs Humans (oder auch andere Aliens). Bisher war mein Lieblingsspiel in der Nische Hoplomachus, weil es meiner Meinung nach gut die Gladiatorenkämpfe einfängt, die sein Thema sind. Und weil es solide Soloregeln hat. Night of Man ergänzt meine Sammlung hin zu mehr ausgedehnten Kämpfen, auch hier mit Soloregeln. Regeln an sich sind aber auch das größte Manko - sie lesen sich zwar recht gut, aber im Spiel fallen dann doch Lücken und Schwachpunkte auf, Symbole ohne Erläuterung oder Situationen, die nicht geklärt sind. Da hätte sorgfältigere Entwicklung des Spiels sicherlich geholfen. Aber wenn man da gesunden Menschenverstand ran lässt, kann man die meisten Sachen auch selber lösen. Das Spiel an sich ist sehr unterhaltsam. Die Impulse gehen schnell durch, das Spiel stockt nicht lange, da man in der Regel nur wenige Einheiten (oft nur eine) aktiviert. Dadurch haben beide Seiten was zu tun und die Reaktionskarten geben nochmal mehr Interaktion. Macht definitiv Lust auf mehr, vor allem mit den Soloregeln (bisher war es linke Hand gegen rechte Hand). Und die Komponenten sind gut gemacht und besonders die Counter sehr alte-Männer-augenfreundlich.</p>
<p><a href="https://boardgamegeek.com/boardgame/169986/night-man">Night of Man</a> ist ein taktisches Kampfspiel, Aliens vs Humans (oder auch andere Aliens). Bisher war mein Lieblingsspiel in der Nische Hoplomachus, weil es meiner Meinung nach gut die Gladiatorenkämpfe einfängt, die sein Thema sind. Und weil es solide Soloregeln hat. Night of Man ergänzt meine Sammlung hin zu mehr ausgedehnten Kämpfen, auch hier mit Soloregeln. Regeln an sich sind aber auch das größte Manko - sie lesen sich zwar recht gut, aber im Spiel fallen dann doch Lücken und Schwachpunkte auf, Symbole ohne Erläuterung oder Situationen, die nicht geklärt sind. Da hätte sorgfältigere Entwicklung des Spiels sicherlich geholfen. Aber wenn man da gesunden Menschenverstand ran lässt, kann man die meisten Sachen auch selber lösen. Das Spiel an sich ist sehr unterhaltsam. Die Impulse gehen schnell durch, das Spiel stockt nicht lange, da man in der Regel nur wenige Einheiten (oft nur eine) aktiviert. Dadurch haben beide Seiten was zu tun und die Reaktionskarten geben nochmal mehr Interaktion. Macht definitiv Lust auf mehr, vor allem mit den Soloregeln (bisher war es linke Hand gegen rechte Hand). Und die Komponenten sind gut gemacht und besonders die Counter sehr alte-Männer-augenfreundlich.</p>
</div>
</div>
@@ -778,13 +761,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>15.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-15</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://wolfsgamingblog.com/2016/06/01/the-gallerist-review-a-masterpiece/">The Gallerist Review A Masterpiece Wolf&#39;s Gaming Blog</a> ist ein guter Review über eines meiner lieblings-Heavy-Games. Was mich erinnert, dass ich unbedingt die Kiste mal wieder aus dem Schrank holen sollte.</p>
<p><a href="https://wolfsgamingblog.com/2016/06/01/the-gallerist-review-a-masterpiece/">The Gallerist Review A Masterpiece Wolf's Gaming Blog</a> ist ein guter Review über eines meiner lieblings-Heavy-Games. Was mich erinnert, dass ich unbedingt die Kiste mal wieder aus dem Schrank holen sollte.</p>
</div>
</div>
@@ -797,13 +779,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>14.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-14</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/175095/dawn-zeds-third-edition">Dawn of the Zeds (Third edition)</a> ist eine der von mir mit Spannung erwarteten Kickstarterlieferungen dieses Jahres, die vor wenigen Tagen endlich ankam. Die Spannung kam daher, das es von einem meiner Lieblingsverlage ist - Victory Point Games - die damit ihren ersten Ausflug in massentaugliche Produkion gewagt haben. Nicht mehr Print-on-Demand mit Laser-Cut Komponenten, sondern professionelle Produktion in hoher Qualität sollte es sein. Dazu dann noch die Tatsache, dass Dawn of the Zeds der Höhepunkt in der States of Siege Reihe ist. Im Prinzip Tower-Defense mit Steuerung der Ereignisse und Angriffe der Feinde durch einen konfigurierbaren Kartenstapel. Das Ergebnis? Wow. Einfach nur Wow. Die Komponenten sind absolut top, das ganze Material sieht fantastisch aus. Das modulare Spielsystem erlaubt verschiedene Schwierigkeitsstufen, sowohl für Coop Spiel als auch fpr Einer-gegen-Viele. Dazu dann noch ein No-Brains Mode, der im Prinzip ein extrem simplifiziertes System für Bier-und-Chips Abende bietet. Das Spiel ist am ehesten mit einem Zombiefilm vergleichbar, die Geschichte wird spannend aufgebaut und durch die vorbereiteten Kartenstapel lässt der Spannungsbogen auch nicht nach. Absolut fantastisches Spielgefühl. Der Setup ist etwas umständlich, da ja die ganzen Elemente vorbereitet werden müssen, aber es liegt im akzeptablen Rahmen für so ein Spiel. Das wird definitiv noch öfter auf den Tisch kommen, da selbst im Solospiel das ganze äußerst viel Spaß macht.</p>
<p><a href="https://boardgamegeek.com/boardgame/175095/dawn-zeds-third-edition">Dawn of the Zeds (Third edition)</a> ist eine der von mir mit Spannung erwarteten Kickstarterlieferungen dieses Jahres, die vor wenigen Tagen endlich ankam. Die Spannung kam daher, das es von einem meiner Lieblingsverlage ist - Victory Point Games - die damit ihren ersten Ausflug in massentaugliche Produkion gewagt haben. Nicht mehr Print-on-Demand mit Laser-Cut Komponenten, sondern professionelle Produktion in hoher Qualität sollte es sein. Dazu dann noch die Tatsache, dass Dawn of the Zeds der Höhepunkt in der States of Siege Reihe ist. Im Prinzip Tower-Defense mit Steuerung der Ereignisse und Angriffe der Feinde durch einen konfigurierbaren Kartenstapel. Das Ergebnis? Wow. Einfach nur Wow. Die Komponenten sind absolut top, das ganze Material sieht fantastisch aus. Das modulare Spielsystem erlaubt verschiedene Schwierigkeitsstufen, sowohl für Coop Spiel als auch fpr Einer-gegen-Viele. Dazu dann noch ein No-Brains Mode, der im Prinzip ein extrem simplifiziertes System für Bier-und-Chips Abende bietet. Das Spiel ist am ehesten mit einem Zombiefilm vergleichbar, die Geschichte wird spannend aufgebaut und durch die vorbereiteten Kartenstapel lässt der Spannungsbogen auch nicht nach. Absolut fantastisches Spielgefühl. Der Setup ist etwas umständlich, da ja die ganzen Elemente vorbereitet werden müssen, aber es liegt im akzeptablen Rahmen für so ein Spiel. Das wird definitiv noch öfter auf den Tisch kommen, da selbst im Solospiel das ganze äußerst viel Spaß macht.</p>
</div>
</div>
@@ -816,13 +797,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>07.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-07</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://devices.ubports.com/#/hammerhead">UBports</a> könnte ja glatt bei Neuanschaffung meines nächsten Nexus meinem alten Nexus 5 dann weiteres Leben einhauchen. Das wäre immerhin mehr als mit meinem alten Galaxy Nexus machbar war.</p>
<p><a href="https://devices.ubports.com/#/hammerhead">UBports</a> könnte ja glatt bei Neuanschaffung meines nächsten Nexus meinem alten Nexus 5 dann weiteres Leben einhauchen. Das wäre immerhin mehr als mit meinem alten Galaxy Nexus machbar war.</p>
</div>
</div>
@@ -835,13 +815,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>03.06.2016</span></aside>
<aside class="archive-day-marker"><span>2016-06-03</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Mich faszinieren besonders Spiele mit wissenschaftlichem Hintergrund. Deshalb war <a href="https://boardgamegeek.com/boardgame/156501/greenland">Greenland</a> für mich auch eine offensichtliche Wahl. Der Autor Phil Eklund ist bekannt für sein sehr intensives Studium der Thematik seiner Spiele. So auch hier - der Überlebenskampf der Thule, Tunit und Norweger auf Grönland um das Jahr 1000 wird hier thematisch aifgegriffen, vor allem die Veränderungen durch die kleine Eiszeit und die sich daraus ergebenden Lebensveränderungen und Migrationen. Das Ergebnis ist ein erstaunlich dichtes Spiel mit viel Strategie und Taktik. Die Regeln sind etwas sehr kompakt aufgebaut, aber mit ein bischen Lesen in den Griff zu bekommen - oder einfach ein paar der Videos auf der Spieleseite bei BGG angucken. Im Prinzip nicht wirklich viel neues, was due Techniken und Mechaniken angeht - Workerplacement mit Würfelentscheiding über den Erfolg, Ressourcenmanagement und eine Eventengine, die den nötigen Druck aufbaut. Die Solovariante ist solide und kann gut den eigenen Anforderungen angepasst werden. Multiplayer hab ich leider noch nicnt ausprobieren können, da das Spiel nur auf Englisch verfügbar ist. Aber für Solo eine klare Empfehlung.</p>
<p>Mich faszinieren besonders Spiele mit wissenschaftlichem Hintergrund. Deshalb war <a href="https://boardgamegeek.com/boardgame/156501/greenland">Greenland</a> für mich auch eine offensichtliche Wahl. Der Autor Phil Eklund ist bekannt für sein sehr intensives Studium der Thematik seiner Spiele. So auch hier - der Überlebenskampf der Thule, Tunit und Norweger auf Grönland um das Jahr 1000 wird hier thematisch aifgegriffen, vor allem die Veränderungen durch die kleine Eiszeit und die sich daraus ergebenden Lebensveränderungen und Migrationen. Das Ergebnis ist ein erstaunlich dichtes Spiel mit viel Strategie und Taktik. Die Regeln sind etwas sehr kompakt aufgebaut, aber mit ein bischen Lesen in den Griff zu bekommen - oder einfach ein paar der Videos auf der Spieleseite bei BGG angucken. Im Prinzip nicht wirklich viel neues, was due Techniken und Mechaniken angeht - Workerplacement mit Würfelentscheiding über den Erfolg, Ressourcenmanagement und eine Eventengine, die den nötigen Druck aufbaut. Die Solovariante ist solide und kann gut den eigenen Anforderungen angepasst werden. Multiplayer hab ich leider noch nicnt ausprobieren können, da das Spiel nur auf Englisch verfügbar ist. Aber für Solo eine klare Empfehlung.</p>
</div>
</div>
@@ -854,13 +833,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>29.05.2016</span></aside>
<aside class="archive-day-marker"><span>2016-05-29</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/128996/1775-rebellion">1775: Rebellion</a> ist ein Spiel über den Amerikanischen Unabhängigkeitskrieg. Solide im War-Euro-Gebiet angesiedelt mit erstaunlich kompakten Regeln. Aber nicht einfach nur Risk-mit-Thema, sondern durchaus strategisch. Mir gefällt besonders die sehr kompakte Spielsituation mit nur 12 Karten pro Fraktion - im 4-Spieler-Modus wird 2-vs-2 gespielt, bei zwei Spielern spielt jeder zwei Fraktionen. Durch die geringe Regelanzahl und wenigen Karten spielt sich selbst das erste Spiel relativ flott und man erkennt schnell die strategischen und taktischen Möglichkeiten. Und trotz Kompaktheit kommt das Thema meiner Meinung nach gut raus. Macht definitiv Spaß auf mehr!</p>
<p><a href="https://boardgamegeek.com/boardgame/128996/1775-rebellion">1775: Rebellion</a> ist ein Spiel über den Amerikanischen Unabhängigkeitskrieg. Solide im War-Euro-Gebiet angesiedelt mit erstaunlich kompakten Regeln. Aber nicht einfach nur Risk-mit-Thema, sondern durchaus strategisch. Mir gefällt besonders die sehr kompakte Spielsituation mit nur 12 Karten pro Fraktion - im 4-Spieler-Modus wird 2-vs-2 gespielt, bei zwei Spielern spielt jeder zwei Fraktionen. Durch die geringe Regelanzahl und wenigen Karten spielt sich selbst das erste Spiel relativ flott und man erkennt schnell die strategischen und taktischen Möglichkeiten. Und trotz Kompaktheit kommt das Thema meiner Meinung nach gut raus. Macht definitiv Spaß auf mehr!</p>
</div>
</div>
@@ -873,13 +851,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>02.05.2016</span></aside>
<aside class="archive-day-marker"><span>2016-05-02</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/175360/w1815">W1815</a> hat mich beim ersten Spiel echt überrascht: ich erwartete ein Würfelfest mit wildem Zufall und wenig Kontrolle in einem historischen Setting. Ich fand aber ein durchaus kniffeliges Puzzle mit Würfeln. Klar, alles ist dem Zufall überlassen, aber die Aktionen der Corps mit Reaktionen der Gegner (auch in Ketten) machen das ganze interessant - ich muss ständig abschätzen, welche Risiken und Chancen sich ergeben. Dazu verändern sich die Corps je nach Situation und verändern so die Planungslage. Das bringt dann schnell erstaunlich viel Grübelei und Planung. Die natürlich dann durch die Würfel zunichte gemacht wird. Und das ganze funktioniert erstaunlich gut solo, einfach von den Würfeln die Geschichte der Schlacht (oder eine mögliche alternative Geschichte) erzählen lassen und durch die Auswahl der Corps darauf Einfluss nehmen. Sehr entspannend und spielt sich in 15-30 Minuten.</p>
<p><a href="https://boardgamegeek.com/boardgame/175360/w1815">W1815</a> hat mich beim ersten Spiel echt überrascht: ich erwartete ein Würfelfest mit wildem Zufall und wenig Kontrolle in einem historischen Setting. Ich fand aber ein durchaus kniffeliges Puzzle mit Würfeln. Klar, alles ist dem Zufall überlassen, aber die Aktionen der Corps mit Reaktionen der Gegner (auch in Ketten) machen das ganze interessant - ich muss ständig abschätzen, welche Risiken und Chancen sich ergeben. Dazu verändern sich die Corps je nach Situation und verändern so die Planungslage. Das bringt dann schnell erstaunlich viel Grübelei und Planung. Die natürlich dann durch die Würfel zunichte gemacht wird. Und das ganze funktioniert erstaunlich gut solo, einfach von den Würfeln die Geschichte der Schlacht (oder eine mögliche alternative Geschichte) erzählen lassen und durch die Auswahl der Corps darauf Einfluss nehmen. Sehr entspannend und spielt sich in 15-30 Minuten.</p>
</div>
</div>
@@ -892,16 +869,15 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>29.04.2016</span></aside>
<aside class="archive-day-marker"><span>2016-04-29</span></aside>
<div class="archive-day-posts">
<div class="post">
<blockquote>
<blockquote>
<p>Analoge Anmutung in digitalen Zeiten: Bei der neuen Kamera der M-Serie lässt Leica das Display weg. Durch die Reduktion solle sich der Nutzer der Leica M-D nur auf sein Motiv</p>
</blockquote>
<p>Quelle: <em><a href="http://www.golem.de/news/ohne-display-leica-m-d-ist-eine-analoge-digitalkamera-1604-120638.html">Ohne Display: Leica M-D ist eine analoge Digitalkamera - Golem.de</a></em> - der Witz ist, mir würde das sogar gefallen. Ich ignoriere an meiner M8 eh den Bildschirm immer und wenn meine Kamera es bietet (wie die GH1), dann klappe ich das Display weg. Aber dafür 6000 Euro? Nope.</p>
</div>
</div>
@@ -914,13 +890,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>13.03.2016</span></aside>
<aside class="archive-day-marker"><span>2016-03-13</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Gelegenheit zur Demo für <a href="https://boardgamegeek.com/boardgame/163068/trickerion-legends-illusion">Trickerion: Legends of Illusion</a> gehabt und genutzt. Nette Veranstaltung, auch wenn wir nur die erste Hälfte dabei waren. Das Spiel selbst gefällt mir sehr gut - ich habe allerdings nur die Basisvariante gespielt. Aber Spiele mit mehreren Spielstufen gefallen mir sowieso gut. Das Spiel ist eine sehr interessante Umsetzung von Worker-Placement. Mit einem wirklich schön umgesetzten Thema, die Aktionen und Komponenten machen alle thematisch Sinn (ok, zu den aktionsfördernden Kristallen fallen mir nur Amphetamine ein, aber der Rest war stimmig). Viele gute Entscheidungssituationen im Spiel, sehr kompakter Spielablauf mit nur 5/7 Runden, dabei aber eine Menge was man pro Runde machen kann und will. Und man muss immer die Gegner im Blick halten um von deren Aktionen (zum Beispiel im Theater oder bei Schnellbestellungen) zu profitieren. Hat viel Spaß gemacht und ich freue mich schon auf Oktober, wenn ich die deutsche Version bekomme.</p>
<p>Gelegenheit zur Demo für <a href="https://boardgamegeek.com/boardgame/163068/trickerion-legends-illusion">Trickerion: Legends of Illusion</a> gehabt und genutzt. Nette Veranstaltung, auch wenn wir nur die erste Hälfte dabei waren. Das Spiel selbst gefällt mir sehr gut - ich habe allerdings nur die Basisvariante gespielt. Aber Spiele mit mehreren Spielstufen gefallen mir sowieso gut. Das Spiel ist eine sehr interessante Umsetzung von Worker-Placement. Mit einem wirklich schön umgesetzten Thema, die Aktionen und Komponenten machen alle thematisch Sinn (ok, zu den aktionsfördernden Kristallen fallen mir nur Amphetamine ein, aber der Rest war stimmig). Viele gute Entscheidungssituationen im Spiel, sehr kompakter Spielablauf mit nur 5/7 Runden, dabei aber eine Menge was man pro Runde machen kann und will. Und man muss immer die Gegner im Blick halten um von deren Aktionen (zum Beispiel im Theater oder bei Schnellbestellungen) zu profitieren. Hat viel Spaß gemacht und ich freue mich schon auf Oktober, wenn ich die deutsche Version bekomme.</p>
</div>
</div>
@@ -933,19 +908,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>31.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-31</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/176524/hoplomachus-origins">Hoplomachus: Origins</a> hat mich seit ein paar Tagen mit seinen solo-Trials gepackt. Diese sind weniger ein volles solo-Spiel als vielmehr kleine Szenarien mit der Aufforderung, diese zu zerbrechen - eine Lösung durch Kombination von Gladiatoren, Taktiken und Spielzügen zu finden, die das Szenario verlässlich besiegen kann. Sehr schnell gespielt und die AI für die Gegner ist trotz sehr geringer Komplexität erstaunlich effektiv. Macht durchaus Spaß und kann schnell mal zwischendurch gespielt werden.</p>
<p><a href="https://boardgamegeek.com/boardgame/176524/hoplomachus-origins">Hoplomachus: Origins</a> hat mich seit ein paar Tagen mit seinen solo-Trials gepackt. Diese sind weniger ein volles solo-Spiel als vielmehr kleine Szenarien mit der Aufforderung, diese zu zerbrechen - eine Lösung durch Kombination von Gladiatoren, Taktiken und Spielzügen zu finden, die das Szenario verlässlich besiegen kann. Sehr schnell gespielt und die AI für die Gegner ist trotz sehr geringer Komplexität erstaunlich effektiv. Macht durchaus Spaß und kann schnell mal zwischendurch gespielt werden.</p>
</div>
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/165401/wir-sind-das-volk">Wir sind das Volk!</a> kam gestern das erste Mal mit Juliana auf den Tisch. Wie bei meiner ersten Solo-Runde allerdings nur für die erste Dekade, weil das zum ersten Lernen schon mal alle Aspekte des Spiels zeigt, aber nicht gleich mit dem vollen Programm überlädt. Bei diesem Spiel hoffe ich, dass bei Juliana das Interesse bleibt, weil es wirklich sehr gut gemacht ist. Es ist im Prinzip ein Mix aus Ressourcenmanagement mit etwas Area-Control reingemischt und - für den Osten - ein guter Schuss State of Siege. Beide Seiten spielen sich sehr verschieden, der Westen spielt ein Wirtschaftsaufbauspiel mit integrierten Angriffen auf den Osten mit Ideologie und Lebensstandard, der Osten kämpft ständig ums Überleben und versucht alle Feuer zu löschen, also im Prinzip klassische Mangelverwaltung. Der Westen gewinnt durch Auflösung des Osten, der Osten muss nur bis zum Ende überleben um zu gewinnen. Aus diesen sehr asymmetrischen Vorgehensweisen und Zielen ergibt sich dann ein spannendes Tauziehen zwischen den beiden deutschen Staaten. Das ganze gewürzt mit einem historischen Thema. Sehr cool.</p>
<p><a href="https://boardgamegeek.com/boardgame/165401/wir-sind-das-volk">Wir sind das Volk!</a> kam gestern das erste Mal mit Juliana auf den Tisch. Wie bei meiner ersten Solo-Runde allerdings nur für die erste Dekade, weil das zum ersten Lernen schon mal alle Aspekte des Spiels zeigt, aber nicht gleich mit dem vollen Programm überlädt. Bei diesem Spiel hoffe ich, dass bei Juliana das Interesse bleibt, weil es wirklich sehr gut gemacht ist. Es ist im Prinzip ein Mix aus Ressourcenmanagement mit etwas Area-Control reingemischt und - für den Osten - ein guter Schuss State of Siege. Beide Seiten spielen sich sehr verschieden, der Westen spielt ein Wirtschaftsaufbauspiel mit integrierten Angriffen auf den Osten mit Ideologie und Lebensstandard, der Osten kämpft ständig ums Überleben und versucht alle Feuer zu löschen, also im Prinzip klassische Mangelverwaltung. Der Westen gewinnt durch Auflösung des Osten, der Osten muss nur bis zum Ende überleben um zu gewinnen. Aus diesen sehr asymmetrischen Vorgehensweisen und Zielen ergibt sich dann ein spannendes Tauziehen zwischen den beiden deutschen Staaten. Das ganze gewürzt mit einem historischen Thema. Sehr cool.</p>
</div>
</div>
@@ -958,13 +931,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>25.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-25</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/zeromq/netmq">zeromq/netmq: A 100% native C# implementation of ZeroMQ for .NET</a> war mir vollkommen unbekannt (die C# Version, nicht ZeroMQ), ist aber für .NETies ein wirklich spannendes Projekt. Direkt in .NET ohne externe Libraries oder gar Server kann man damit diverse 1:1, 1:N, N:1, N:M Kommunikationsstrukturen mit diversen Verhaltensweisen (Pub/Sub, Broadcast, Req/Resp, Fifo - halt alles was ZeroMQ kann) aufbauen. Macht einen sehr guten Eindruck (ok, kein Wunder, ist ja schließlich eine Umsetzung des originalen ZeroMQ nach C#, und ZeroMQ war für seine Zwecke ja schon absolut genial). Und ist natürlich interoperabel mit dem originalen ZeroMQ.</p>
<p><a href="https://github.com/zeromq/netmq">zeromq/netmq: A 100% native C# implementation of ZeroMQ for .NET</a> war mir vollkommen unbekannt (die C# Version, nicht ZeroMQ), ist aber für .NETies ein wirklich spannendes Projekt. Direkt in .NET ohne externe Libraries oder gar Server kann man damit diverse 1:1, 1:N, N:1, N:M Kommunikationsstrukturen mit diversen Verhaltensweisen (Pub/Sub, Broadcast, Req/Resp, Fifo - halt alles was ZeroMQ kann) aufbauen. Macht einen sehr guten Eindruck (ok, kein Wunder, ist ja schließlich eine Umsetzung des originalen ZeroMQ nach C#, und ZeroMQ war für seine Zwecke ja schon absolut genial). Und ist natürlich interoperabel mit dem originalen ZeroMQ.</p>
</div>
</div>
@@ -977,13 +949,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>24.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-24</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Mein erstes COIN (Counter Insurgency) Spiel ist <a href="https://boardgamegeek.com/boardgame/111799/cuba-libre">Cuba Libre</a> - und das kam erstmalig gestern auf den Tisch. Was für ein großartiges System. Es ist komplex mit vielen Elementen, aber das Material (Brett, Spielerhilfen, Spielsequenzhilfe) macht es trotzdem leicht dem Spielfluss zu folgen und nicht mittendrin zu vergessen wessen Aktion es ist, wer als nächstes dran ist und was der machen darf. Thematisch ist das Spiel auch sehr gut mit den Mechaniken verbunden - das Syndikat baut Kasinos und greift sich Geld wo sie können, sind aber relativ lokal aktiv, das Directorio bildet viele kleinere Zellen quer über die Insel und versucht beide Seiten zu stören und sich selbst in vielen Landesteilen zu positionieren, Batista verballert sein Geld (das er von USA und dem Syndikat bekommt) auf massive Polizei und Militäraktionen gegen die Rebellen und Castros Seite versucht die Regionen von der Revolution zu überzeugen und möglichst viele Regionen und Städte zur Opposition zu motivieren. Mein Spiel war Castro gegen den Rest (von Bots gesteuert) und war leider nicht erfolgreich - das Syndikat hat das Land zu sehr korrumpiert. Die Bots zeigten recht gut den Charakter der anderen Fraktionen. Ok, die ganze Nummer dauerte 5 Stunden, aber es war ja auch das erste Spiel. Beim nächsten Mal wird das flüssiger laufen. Und es wird definitiv ein nächstes Mal geben, das Spiel ist einfach fantastisch. Und ich freue mich schon auf die kommende Erweiterung und die beiden kommenden COIN Spiele über die gallischen Kriege und die Amerikanische Revolution. Cuba Libre hat auch nur 56 Karten oder so, da bin ich schon am Überlegen, ob ich die nicht einfach übersetze - dann hätte ich auch eine Chance, das mit Juliana zu spielen. Komplexer als unser übliches Spielfutter, aber lohnend.</p>
<p>Mein erstes COIN (Counter Insurgency) Spiel ist <a href="https://boardgamegeek.com/boardgame/111799/cuba-libre">Cuba Libre</a> - und das kam erstmalig gestern auf den Tisch. Was für ein großartiges System. Es ist komplex mit vielen Elementen, aber das Material (Brett, Spielerhilfen, Spielsequenzhilfe) macht es trotzdem leicht dem Spielfluss zu folgen und nicht mittendrin zu vergessen wessen Aktion es ist, wer als nächstes dran ist und was der machen darf. Thematisch ist das Spiel auch sehr gut mit den Mechaniken verbunden - das Syndikat baut Kasinos und greift sich Geld wo sie können, sind aber relativ lokal aktiv, das Directorio bildet viele kleinere Zellen quer über die Insel und versucht beide Seiten zu stören und sich selbst in vielen Landesteilen zu positionieren, Batista verballert sein Geld (das er von USA und dem Syndikat bekommt) auf massive Polizei und Militäraktionen gegen die Rebellen und Castros Seite versucht die Regionen von der Revolution zu überzeugen und möglichst viele Regionen und Städte zur Opposition zu motivieren. Mein Spiel war Castro gegen den Rest (von Bots gesteuert) und war leider nicht erfolgreich - das Syndikat hat das Land zu sehr korrumpiert. Die Bots zeigten recht gut den Charakter der anderen Fraktionen. Ok, die ganze Nummer dauerte 5 Stunden, aber es war ja auch das erste Spiel. Beim nächsten Mal wird das flüssiger laufen. Und es wird definitiv ein nächstes Mal geben, das Spiel ist einfach fantastisch. Und ich freue mich schon auf die kommende Erweiterung und die beiden kommenden COIN Spiele über die gallischen Kriege und die Amerikanische Revolution. Cuba Libre hat auch nur 56 Karten oder so, da bin ich schon am Überlegen, ob ich die nicht einfach übersetze - dann hätte ich auch eine Chance, das mit Juliana zu spielen. Komplexer als unser übliches Spielfutter, aber lohnend.</p>
</div>
</div>
@@ -996,13 +967,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>20.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-20</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Nach längerer Pause mal wieder <a href="https://boardgamegeek.com/boardgame/102652/sentinels-multiverse">Sentinels of the Multiverse</a> gespielt. Das ist immer noch ein wirklich unterhaltsames Spiel. Aber ich merke immer mehr, dass es zwar viel Spaß macht, mir aber das nachhalten diverser Effekte in der Papierversion einfach die Lust verdirbt die Karten rauszuholen und ich es daher dann doch wieder lieber auf dem PC oder Tablet spiele. Was eigentlich schade ist, aber ich bemerke schon in der digitalen Version oft Momente von &quot;huch, an den Modifier hab ich jetzt ja gar nicht mehr gedacht&quot;, in der Papierversion wird das echt lästig. Da helfen auch die vielen Marker nicht wirklich. Andererseits ist es ja eh ein rein kooperatives Spiel, ohne Entscheidungen für den Gegner, daher kann man sowas auch gut auf Computer umsetzen. Für ein Solo-Spiel ist das völlig ok und viel bequemer.</p>
<p>Nach längerer Pause mal wieder <a href="https://boardgamegeek.com/boardgame/102652/sentinels-multiverse">Sentinels of the Multiverse</a> gespielt. Das ist immer noch ein wirklich unterhaltsames Spiel. Aber ich merke immer mehr, dass es zwar viel Spaß macht, mir aber das nachhalten diverser Effekte in der Papierversion einfach die Lust verdirbt die Karten rauszuholen und ich es daher dann doch wieder lieber auf dem PC oder Tablet spiele. Was eigentlich schade ist, aber ich bemerke schon in der digitalen Version oft Momente von &quot;huch, an den Modifier hab ich jetzt ja gar nicht mehr gedacht&quot;, in der Papierversion wird das echt lästig. Da helfen auch die vielen Marker nicht wirklich. Andererseits ist es ja eh ein rein kooperatives Spiel, ohne Entscheidungen für den Gegner, daher kann man sowas auch gut auf Computer umsetzen. Für ein Solo-Spiel ist das völlig ok und viel bequemer.</p>
</div>
</div>
@@ -1015,13 +985,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>15.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-15</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Ich habe <a href="https://boardgamegeek.com/boardgame/134253/hostage-negotiator">Hostage Negotiator</a> vor ein paar Tagen bekommen und es ausprobiert. Sehr interessante Umsetzung eines spannenden Themas. Es hat die klassische wargame-Struktur: entscheide einen Plan und die Würfel sagen dir, ob der Plan gelingt. Die chancen stehen dafür nicht sonderlich gut. Von daher erinnere es mich an die State of Siege Spiele wie Soviet Dawn oder Cruel Necessity, bei denen ist das genauso. Hier muss man auch immer das Risiko im Auge haben, das Glück spielt aber fast noch eine größere Rolle als bei den SoS Spielen. Von daher weiß ich nicht, ob es auf Dauer neben Soviet Dawn (das noch schneller aufgebaut ist) Bestand haben wird - aber es hat den Vorteil der höheren Variabilität. Verglichen mit Space Hulk: Todesengel jedenfalls steht es meiner Meinung nach gut da, die Schachtel ist auch klein, eine Reihe von Erweiterungen bingen Abwechselung, es wird also mindestens als Reisepack einen Platz haben. Und bestimmt noch einige Runden auf dem Tisch verbringen.</p>
<p>Ich habe <a href="https://boardgamegeek.com/boardgame/134253/hostage-negotiator">Hostage Negotiator</a> vor ein paar Tagen bekommen und es ausprobiert. Sehr interessante Umsetzung eines spannenden Themas. Es hat die klassische wargame-Struktur: entscheide einen Plan und die Würfel sagen dir, ob der Plan gelingt. Die chancen stehen dafür nicht sonderlich gut. Von daher erinnere es mich an die State of Siege Spiele wie Soviet Dawn oder Cruel Necessity, bei denen ist das genauso. Hier muss man auch immer das Risiko im Auge haben, das Glück spielt aber fast noch eine größere Rolle als bei den SoS Spielen. Von daher weiß ich nicht, ob es auf Dauer neben Soviet Dawn (das noch schneller aufgebaut ist) Bestand haben wird - aber es hat den Vorteil der höheren Variabilität. Verglichen mit Space Hulk: Todesengel jedenfalls steht es meiner Meinung nach gut da, die Schachtel ist auch klein, eine Reihe von Erweiterungen bingen Abwechselung, es wird also mindestens als Reisepack einen Platz haben. Und bestimmt noch einige Runden auf dem Tisch verbringen.</p>
</div>
</div>
@@ -1034,13 +1003,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>08.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-08</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/40107/soviet-dawn">Soviet Dawn</a> ist ein weiteres State of Siege Spiel. Dieses war als Beilage in der C3i 27 drin, ein echtes Schnäppchen. Thematisch sehr dicht, wie bei allen State of Siege Spielen bisher, die Spielzeit deutlich kürzer als bei Cruel Necessity. Gefällt mir sehr gut, trotz des Würfelfestes ist schon erkennbar, dass man durchaus Chancen hat, aber nur sehr knappe. Dank der kurzen Spielzeut gute Chancen öfter auf den Tisch zu kommen, allein schon weil ich das Thema (frühe Sovietunion nach der Revolution) spannend finde. Schönes Solo Spiel für zwischendurch.</p>
<p><a href="https://boardgamegeek.com/boardgame/40107/soviet-dawn">Soviet Dawn</a> ist ein weiteres State of Siege Spiel. Dieses war als Beilage in der C3i 27 drin, ein echtes Schnäppchen. Thematisch sehr dicht, wie bei allen State of Siege Spielen bisher, die Spielzeit deutlich kürzer als bei Cruel Necessity. Gefällt mir sehr gut, trotz des Würfelfestes ist schon erkennbar, dass man durchaus Chancen hat, aber nur sehr knappe. Dank der kurzen Spielzeut gute Chancen öfter auf den Tisch zu kommen, allein schon weil ich das Thema (frühe Sovietunion nach der Revolution) spannend finde. Schönes Solo Spiel für zwischendurch.</p>
</div>
</div>
@@ -1053,13 +1021,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>04.01.2016</span></aside>
<aside class="archive-day-marker"><span>2016-01-04</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="http://www.juniorgeneral.org/">Junior General</a> ist eine sehr interessante Webseite mit vielen (freien!) Papier-&quot;Miniaturen&quot; für klassische Kriegsspiele auf Basis von Miniaturenregeln wie z.B. Professor Sabins <a href="http://www.amazon.de/gp/product/B00S84UI7U/">Lost Battles: Reconstructing the Great Clashes of the Ancient World</a>. Simpel zu produzieren (ausdrucken, falten, kleben, fertig) und geben dem Schlachtfeld ein 3D-Feeling bei geringem Aufwand an Zeit und Geld.</p>
<p><a href="http://www.juniorgeneral.org/">Junior General</a> ist eine sehr interessante Webseite mit vielen (freien!) Papier-&quot;Miniaturen&quot; für klassische Kriegsspiele auf Basis von Miniaturenregeln wie z.B. Professor Sabins <a href="http://www.amazon.de/gp/product/B00S84UI7U/">Lost Battles: Reconstructing the Great Clashes of the Ancient World</a>. Simpel zu produzieren (ausdrucken, falten, kleben, fertig) und geben dem Schlachtfeld ein 3D-Feeling bei geringem Aufwand an Zeit und Geld.</p>
</div>
</div>
@@ -1072,13 +1039,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>17.11.2015</span></aside>
<aside class="archive-day-marker"><span>2015-11-17</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Ich hab es tatsächlich endlich mal geschafft <a href="https://boardgamegeek.com/boardgame/96848/mage-knight-board-game">Mage Knight Board Game</a> auf den Tisch zu bringen für ein Solo-Lernspiel. Noch mit reduzierten Walkthrough Regeln, um erstmal einen Griff an den Regelrahmen zu bekommen, dann gehts beim nächsten Solo-Spiel mal richtig in den Walkthrough mit vollen Regeln. Hat schon ziemlich viele Ecken an die man denken muss und leider ist der Setup doch ein bischen aufwändiger (allerdings auch nicht viel schlimmer als zum Beispiel bei The Gallerist), da muss ich mir noch was für die Organisation einfallen lassen. Aber so ganz grundsätzlich ist das schon ein beeindruckendes Spiel. Mir liegen ja solche mehr Puzzle-orientierten Coop oder Solo Spiele und das Thema liegt mir auf jeden Fall. Das wird definitiv wiederholt. Und auf die Neuauflagen der Erweiterungen warte ich auch ganz gespannt, speziell die Verloschollene Legion und die ganz neue Erweiterung bieten ja eine Menge für das Solo-Spiel.</p>
<p>Ich hab es tatsächlich endlich mal geschafft <a href="https://boardgamegeek.com/boardgame/96848/mage-knight-board-game">Mage Knight Board Game</a> auf den Tisch zu bringen für ein Solo-Lernspiel. Noch mit reduzierten Walkthrough Regeln, um erstmal einen Griff an den Regelrahmen zu bekommen, dann gehts beim nächsten Solo-Spiel mal richtig in den Walkthrough mit vollen Regeln. Hat schon ziemlich viele Ecken an die man denken muss und leider ist der Setup doch ein bischen aufwändiger (allerdings auch nicht viel schlimmer als zum Beispiel bei The Gallerist), da muss ich mir noch was für die Organisation einfallen lassen. Aber so ganz grundsätzlich ist das schon ein beeindruckendes Spiel. Mir liegen ja solche mehr Puzzle-orientierten Coop oder Solo Spiele und das Thema liegt mir auf jeden Fall. Das wird definitiv wiederholt. Und auf die Neuauflagen der Erweiterungen warte ich auch ganz gespannt, speziell die Verloschollene Legion und die ganz neue Erweiterung bieten ja eine Menge für das Solo-Spiel.</p>
</div>
</div>
@@ -1091,13 +1057,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>06.11.2015</span></aside>
<aside class="archive-day-marker"><span>2015-11-06</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/77423/lord-rings-card-game">The Lord of the Rings: The Card Game</a> ist ja ein regelmäßiger Gast auf dem Tisch, jedenfalls wenn ich Solo spiele. Nicht jede Runde ist bemerkenswert, auch wenn die Spiele eigentlich immer Spaß machen. Aber gestern habe ich mein renoviertes Ent-Deck das erste Mal ins Rennen geschickt, diesmal gegen das erste Szenario des Schwarzen Reiters. Und das Ergebnis war beeindruckend. Das Deck lief überhaupt nicht flüssig an - nur wenige Ents, nur eines des Ent-Grabe-Ereignisse (wo man aus den top-5 alle Ent-Karten nehmen kann). Kein Shadowmane für Gandalf. Also eigentlich nicht wirklich optimal - und trotzdem hat das Deck extrem gut gehalten. So richtig bedrohlich wurde die Situation nie, die schwarzen Reiter (allerdings auch mit viel Glück, keiner von denen hat sein Reittier bekommen) waren keine echte Gefahr, da ich sie fast immer aus dem Staging holen konnte um sie sauber zu blocken und dann mit ein paar Ents zu zerpflücken. Dank einiger Schneller Knoten waren dann auch die Hobbits immer verfügbar um sie in Verstecken-Prüfungen zu entsenden und im Notfall konnten die mit Ents noch verbessert werden. Und Baumbart war als solider Verteidiger und Quester und zusätzliche Ressourcenquelle auch bald dabei. Der Wechsel weg von Baumbart als Helden hin zu Gandalf gibt mir definitiv ein gutes Stück Kontrolle über das Spielerdeck, was bei sperrigem Anlaufen des Decks deutlich hilft - gerade bei schlechterer Entwicklung der ersten Runden war die alte Version schnell überfordert und hier musste ich schon sehr schnell gegen 9-11 Bedrohung im Aufmarsch antreten. Ein wirklich nettes Deck und ich glaub das kann ich mal gegen die ganze Kampagne antreten lassen.</p>
<p><a href="https://boardgamegeek.com/boardgame/77423/lord-rings-card-game">The Lord of the Rings: The Card Game</a> ist ja ein regelmäßiger Gast auf dem Tisch, jedenfalls wenn ich Solo spiele. Nicht jede Runde ist bemerkenswert, auch wenn die Spiele eigentlich immer Spaß machen. Aber gestern habe ich mein renoviertes Ent-Deck das erste Mal ins Rennen geschickt, diesmal gegen das erste Szenario des Schwarzen Reiters. Und das Ergebnis war beeindruckend. Das Deck lief überhaupt nicht flüssig an - nur wenige Ents, nur eines des Ent-Grabe-Ereignisse (wo man aus den top-5 alle Ent-Karten nehmen kann). Kein Shadowmane für Gandalf. Also eigentlich nicht wirklich optimal - und trotzdem hat das Deck extrem gut gehalten. So richtig bedrohlich wurde die Situation nie, die schwarzen Reiter (allerdings auch mit viel Glück, keiner von denen hat sein Reittier bekommen) waren keine echte Gefahr, da ich sie fast immer aus dem Staging holen konnte um sie sauber zu blocken und dann mit ein paar Ents zu zerpflücken. Dank einiger Schneller Knoten waren dann auch die Hobbits immer verfügbar um sie in Verstecken-Prüfungen zu entsenden und im Notfall konnten die mit Ents noch verbessert werden. Und Baumbart war als solider Verteidiger und Quester und zusätzliche Ressourcenquelle auch bald dabei. Der Wechsel weg von Baumbart als Helden hin zu Gandalf gibt mir definitiv ein gutes Stück Kontrolle über das Spielerdeck, was bei sperrigem Anlaufen des Decks deutlich hilft - gerade bei schlechterer Entwicklung der ersten Runden war die alte Version schnell überfordert und hier musste ich schon sehr schnell gegen 9-11 Bedrohung im Aufmarsch antreten. Ein wirklich nettes Deck und ich glaub das kann ich mal gegen die ganze Kampagne antreten lassen.</p>
</div>
</div>
@@ -1110,13 +1075,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>03.11.2015</span></aside>
<aside class="archive-day-marker"><span>2015-11-03</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/125153/gallerist">The Gallerist</a> kam heute zum ersten Solospiel auf den Tisch. Wow. Das ist schon fast Arbeit. Macht aber sehr viel Spaß, auch wenn ich im ersten Solospiel noch viel zu viele Fehler gemacht habe - das Spielsystem ist schon gut erkennbar gewesen. Die Aktionen machen Sinn und sind thematisch hervorragend fundiert, was das Lernen deutlich vereinfacht. Und das Beste: es macht echt Spaß. Die zwei Stunden gingen um wie nix. Das wird definitiv wiederholt.</p>
<p><a href="https://boardgamegeek.com/boardgame/125153/gallerist">The Gallerist</a> kam heute zum ersten Solospiel auf den Tisch. Wow. Das ist schon fast Arbeit. Macht aber sehr viel Spaß, auch wenn ich im ersten Solospiel noch viel zu viele Fehler gemacht habe - das Spielsystem ist schon gut erkennbar gewesen. Die Aktionen machen Sinn und sind thematisch hervorragend fundiert, was das Lernen deutlich vereinfacht. Und das Beste: es macht echt Spaß. Die zwei Stunden gingen um wie nix. Das wird definitiv wiederholt.</p>
</div>
</div>
@@ -1129,13 +1093,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>02.11.2015</span></aside>
<aside class="archive-day-marker"><span>2015-11-02</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Auswahl für das nächste große Spiel das Juliana und ich zusammen lernen war am Wochende <a href="https://boardgamegeek.com/boardgame/164338/golden-ages">The Golden Ages</a> und das erste Lernspiel haben wir hinter uns. War noch etwas holperig, das Spiel hat doch deutlich mehr bewegliche Teile als alles was wir bisher gespielt haben. Aber Spaß hat es gemacht und die Struktur ist durchaus logisch. Man muss nur an einiges beim Epochenwechsel denken. Alles in allem aber ein wirklich schönes Spiel mit einer Menge an Möglichkeiten und variablem Aufbau, das dürfte uns für eine gute Weile beschäftigen. Aber ich glaube Progress wird es nicht so schnell vom Thron stoßen.</p>
<p>Auswahl für das nächste große Spiel das Juliana und ich zusammen lernen war am Wochende <a href="https://boardgamegeek.com/boardgame/164338/golden-ages">The Golden Ages</a> und das erste Lernspiel haben wir hinter uns. War noch etwas holperig, das Spiel hat doch deutlich mehr bewegliche Teile als alles was wir bisher gespielt haben. Aber Spaß hat es gemacht und die Struktur ist durchaus logisch. Man muss nur an einiges beim Epochenwechsel denken. Alles in allem aber ein wirklich schönes Spiel mit einer Menge an Möglichkeiten und variablem Aufbau, das dürfte uns für eine gute Weile beschäftigen. Aber ich glaube Progress wird es nicht so schnell vom Thron stoßen.</p>
</div>
</div>
@@ -1148,13 +1111,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>26.10.2015</span></aside>
<aside class="archive-day-marker"><span>2015-10-26</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/140717/progress-evolution-technology">Progress: Evolution of Technology</a> hat sich in der letzten Zeit zum totallen Renner mit Juliana entwickelt. Wir haben es praktisch jeden Tag gespielt, in der Zeit in der ich Krank war sogar 2-3 mal am Tag. Und wir haben beide immer noch gewaltigen Spaß daran. Die Vorteile des Spiels sind vielfältig, aber abgesehen vom interessanten Thema sind für uns die Highlights, dass alle Aktionen Sinn machen im Kontext des Spielthemas. Karten die aufeinander aufbauen sind logisch verbunden (in dem meisten Fällen jedenfalls) und auch das was an Verbesserungen kommt, ist durchaus mit der Karte verbunden. Dazu kommt, dass die Karten selber keine Texte außer dem Namen haben - man muss nicht erst lange lesen um zu verstehen was passiert, alles ist mit klaren Symbolen gekennzeichnet. Dazu kommt dann auch noch, dass alle Kulturverbesserungen, die man sich erarbeitet, klar auf dem Spielerboard erkennbar sind. Genauso wie die Position in den Kulturbereichen (Prestige, Bevölkerungsentwicklung und Militär) auf dem Zusatzboard. Die verfügbaren Hilfsmittel sind leicht zu benutzen und zu verstehen. Alles das hilft dabei, dass man wirklich einfach locker spielen kann, ohne von Verständnisfragen (gerade bei Juliana durch die Sprachdifferenz entscheidend) aufgehalten zu werden. Obendrauf dann noch ein für uns wirklich spannendes Spielthema und schon ist unsere Abendunterhaltung gesichert. Wir sind mitlerweile bei 1-1,5 Stunden angelangt, allerdings spielen wir auch immer mit dem vierten Zeitalter. Meilensteine und Philosophen haben wir derzeit noch nicht mit ins Programm genommen, wird aber vielleicht doch bald mal interessant, aber bei 20 Spielen zusammen haben wir bis jetzt noch nicht das Gefühl, dass wir das Grundspiel ausgereizt hätten.</p>
<p><a href="https://boardgamegeek.com/boardgame/140717/progress-evolution-technology">Progress: Evolution of Technology</a> hat sich in der letzten Zeit zum totallen Renner mit Juliana entwickelt. Wir haben es praktisch jeden Tag gespielt, in der Zeit in der ich Krank war sogar 2-3 mal am Tag. Und wir haben beide immer noch gewaltigen Spaß daran. Die Vorteile des Spiels sind vielfältig, aber abgesehen vom interessanten Thema sind für uns die Highlights, dass alle Aktionen Sinn machen im Kontext des Spielthemas. Karten die aufeinander aufbauen sind logisch verbunden (in dem meisten Fällen jedenfalls) und auch das was an Verbesserungen kommt, ist durchaus mit der Karte verbunden. Dazu kommt, dass die Karten selber keine Texte außer dem Namen haben - man muss nicht erst lange lesen um zu verstehen was passiert, alles ist mit klaren Symbolen gekennzeichnet. Dazu kommt dann auch noch, dass alle Kulturverbesserungen, die man sich erarbeitet, klar auf dem Spielerboard erkennbar sind. Genauso wie die Position in den Kulturbereichen (Prestige, Bevölkerungsentwicklung und Militär) auf dem Zusatzboard. Die verfügbaren Hilfsmittel sind leicht zu benutzen und zu verstehen. Alles das hilft dabei, dass man wirklich einfach locker spielen kann, ohne von Verständnisfragen (gerade bei Juliana durch die Sprachdifferenz entscheidend) aufgehalten zu werden. Obendrauf dann noch ein für uns wirklich spannendes Spielthema und schon ist unsere Abendunterhaltung gesichert. Wir sind mitlerweile bei 1-1,5 Stunden angelangt, allerdings spielen wir auch immer mit dem vierten Zeitalter. Meilensteine und Philosophen haben wir derzeit noch nicht mit ins Programm genommen, wird aber vielleicht doch bald mal interessant, aber bei 20 Spielen zusammen haben wir bis jetzt noch nicht das Gefühl, dass wir das Grundspiel ausgereizt hätten.</p>
</div>
</div>
@@ -1167,13 +1129,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>16.10.2015</span></aside>
<aside class="archive-day-marker"><span>2015-10-16</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://medium.com/bad-words/why-twitter-s-dying-and-what-you-can-learn-from-it-9ed233e37974">Why Twitters Dying (And What You Can Learn From It) — Bad Words — Medium</a> ist ein interessanter Artikel über das, was der Autor als zentrales Problem sozialer Medien sieht, das von den Betreibern ignoriert wird: Missbrauch, oder (weil &quot;abuse&quot; sich nicht trivial pbersetzen lässt) das asoziale Verhalten vieler in diesen Medien, das die normalen Benutzer einfavh vertrei t und manches Netz zur reinen Selbstverkündungsmaschine für Presseorgane verkommen lässt. Wobei den hinteren Teil des Artikels, über die digitale Revolution, der ist dann docj etwas arg träumerisch.</p>
<p><a href="https://medium.com/bad-words/why-twitter-s-dying-and-what-you-can-learn-from-it-9ed233e37974">Why Twitters Dying (And What You Can Learn From It) — Bad Words — Medium</a> ist ein interessanter Artikel über das, was der Autor als zentrales Problem sozialer Medien sieht, das von den Betreibern ignoriert wird: Missbrauch, oder (weil &quot;abuse&quot; sich nicht trivial pbersetzen lässt) das asoziale Verhalten vieler in diesen Medien, das die normalen Benutzer einfavh vertrei t und manches Netz zur reinen Selbstverkündungsmaschine für Presseorgane verkommen lässt. Wobei den hinteren Teil des Artikels, über die digitale Revolution, der ist dann docj etwas arg träumerisch.</p>
</div>
</div>
@@ -1186,13 +1147,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>08.10.2015</span></aside>
<aside class="archive-day-marker"><span>2015-10-08</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/157917/lord-ice-garden">The Lord of the Ice Garden</a> hab ich über Spieleschmiede gefördert und erhalten. Und das Spiel ist eine optische Granate - tolle grafische Aufmachung und klasse Miniaturen. Dazu dann ein sehr interessantes Spiel und eine wirklich nicht triviale solo Variante, die tatsächlich einiges vom originalen Spiel erhält. Allerdings sehr weit oben im Komplexitätsgrad, das braucht schon noch mehr Runden, bis ich das wirklich voll verstanden habe um es jemandem zu erklären. Aber es bietet eine Kombination aus Worker-Placement und Area-Domination zusammen mit variablen Spielerfähigkeiten und Upgrademöglichkeiten wie ich es so noch nirgendwo gesehen habe. Absolut wow.</p>
<p><a href="https://boardgamegeek.com/boardgame/157917/lord-ice-garden">The Lord of the Ice Garden</a> hab ich über Spieleschmiede gefördert und erhalten. Und das Spiel ist eine optische Granate - tolle grafische Aufmachung und klasse Miniaturen. Dazu dann ein sehr interessantes Spiel und eine wirklich nicht triviale solo Variante, die tatsächlich einiges vom originalen Spiel erhält. Allerdings sehr weit oben im Komplexitätsgrad, das braucht schon noch mehr Runden, bis ich das wirklich voll verstanden habe um es jemandem zu erklären. Aber es bietet eine Kombination aus Worker-Placement und Area-Domination zusammen mit variablen Spielerfähigkeiten und Upgrademöglichkeiten wie ich es so noch nirgendwo gesehen habe. Absolut wow.</p>
</div>
</div>
@@ -1205,14 +1165,13 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>07.10.2015</span></aside>
<aside class="archive-day-marker"><span>2015-10-07</span></aside>
<div class="archive-day-posts">
<div class="post">
<p>Heilige Scheiße, Per6 ist offiziell draußen!</p>
<p>Holy Smokes, Per6 is officially out!</p>
<p>Quelle: <em><a href="http://www.10zenmonkeys.com/2015/10/06/the-night-larry-wall-unveiled-perl-6/">The Night Larry Wall Unveiled Perl 6 | 10 Zen Monkeys</a></em></p>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>Esmeralda</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,7 +14,7 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/en/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/en/atom.xml" />
<link rel="alternate" hreflang="en" href="/en/2005/11/13/esmeralda" />
<link rel="alternate" hreflang="en" href="/2005/11/13/esmeralda/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
@@ -27,14 +27,13 @@
<link rel="stylesheet" href="/en/pagefind/pagefind-ui.css" />
<script defer src="/en/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
<nav class="language-switcher" aria-label="Language">
<a class="language-switcher-badge" href="/en/" data-lang-prefix="" title="de">🇩🇪</a>
<a class="language-switcher-badge" href="/" data-lang-prefix="" title="de">🇩🇪</a>
@@ -49,7 +48,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..." data-search-no-results="No results found"></div>
</div>
</div>
</nav>
@@ -69,7 +68,7 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/">Home</a>
<a class="blog-menu-link" href="/">Home</a>
</li>
@@ -84,91 +83,91 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2022/">Bilderarchiv 2022</a>
<a class="blog-menu-link" href="/bilderarchiv-2022/">Bilderarchiv 2022</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2016/">Bilderarchiv 2016</a>
<a class="blog-menu-link" href="/bilderarchiv-2016/">Bilderarchiv 2016</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2015/">Bilderarchiv 2015</a>
<a class="blog-menu-link" href="/bilderarchiv-2015/">Bilderarchiv 2015</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2014/">Bilderarchiv 2014</a>
<a class="blog-menu-link" href="/bilderarchiv-2014/">Bilderarchiv 2014</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2013/">Bilderarchiv 2013</a>
<a class="blog-menu-link" href="/bilderarchiv-2013/">Bilderarchiv 2013</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2012/">Bilderarchiv 2012</a>
<a class="blog-menu-link" href="/bilderarchiv-2012/">Bilderarchiv 2012</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2011/">Bilderarchiv 2011</a>
<a class="blog-menu-link" href="/bilderarchiv-2011/">Bilderarchiv 2011</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2010/">Bilderarchiv 2010</a>
<a class="blog-menu-link" href="/bilderarchiv-2010/">Bilderarchiv 2010</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2009/">Bilderarchiv 2009</a>
<a class="blog-menu-link" href="/bilderarchiv-2009/">Bilderarchiv 2009</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2008/">Bilderarchiv 2008</a>
<a class="blog-menu-link" href="/bilderarchiv-2008/">Bilderarchiv 2008</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2005/">Bilderarchiv 2005</a>
<a class="blog-menu-link" href="/bilderarchiv-2005/">Bilderarchiv 2005</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2004/">Bilderarchiv 2004</a>
<a class="blog-menu-link" href="/bilderarchiv-2004/">Bilderarchiv 2004</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2003/">Bilderarchiv 2003</a>
<a class="blog-menu-link" href="/bilderarchiv-2003/">Bilderarchiv 2003</a>
</li>
@@ -183,42 +182,42 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/picture/">Fotoblog</a>
<a class="blog-menu-link" href="/category/picture/">Fotoblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/tag-cloud/">Tag Cloud</a>
<a class="blog-menu-link" href="/tag-cloud/">Tag Cloud</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/spielelog/">Spieleblog</a>
<a class="blog-menu-link" href="/category/spielelog/">Spieleblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/kochbuch/">Kochbuch</a>
<a class="blog-menu-link" href="/category/kochbuch/">Kochbuch</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/wiki/">Wiki</a>
<a class="blog-menu-link" href="/wiki/">Wiki</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/impressum/">Impressum</a>
<a class="blog-menu-link" href="/impressum/">Impressum</a>
</li>
@@ -278,30 +277,29 @@
<div class="single-post-taxonomy" aria-label="Taxonomy">
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/en/category/picture/">picture</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/category/picture/">picture</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/fotografie/">fotografie</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/fotografie/">fotografie</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/makro/">makro</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/makro/">makro</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/natur/">natur</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/natur/">natur</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/spinne/">spinne</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/spinne/">spinne</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/tiere/">tiere</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/tiere/">tiere</a>
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><h3><a href="/media/esmeralda/">Esmeralda</a></h3>
<p><img src="/media/2005/11/eb0cf9d7-6fbd-4b74-9be3-759d6e16f240.jpg" alt="Esmeralda"></p>
</div>
<p><img src="/media/2005/11/eb0cf9d7-6fbd-4b74-9be3-759d6e16f240.jpg" alt="Esmeralda" /></p></div>
</article>
</main>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>cmux — The Terminal for Multitasking</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,7 +14,7 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/en/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/en/atom.xml" />
<link rel="alternate" hreflang="en" href="/en/2026/03/13/cmux-das-terminal-fur-multitasking" />
<link rel="alternate" hreflang="en" href="/2026/03/13/cmux-das-terminal-fur-multitasking/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
@@ -27,14 +27,13 @@
<link rel="stylesheet" href="/en/pagefind/pagefind-ui.css" />
<script defer src="/en/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
<nav class="language-switcher" aria-label="Language">
<a class="language-switcher-badge" href="/en/" data-lang-prefix="" title="de">🇩🇪</a>
<a class="language-switcher-badge" href="/" data-lang-prefix="" title="de">🇩🇪</a>
@@ -49,7 +48,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..." data-search-no-results="No results found"></div>
</div>
</div>
</nav>
@@ -69,7 +68,7 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/">Home</a>
<a class="blog-menu-link" href="/">Home</a>
</li>
@@ -84,91 +83,91 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2022/">Bilderarchiv 2022</a>
<a class="blog-menu-link" href="/bilderarchiv-2022/">Bilderarchiv 2022</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2016/">Bilderarchiv 2016</a>
<a class="blog-menu-link" href="/bilderarchiv-2016/">Bilderarchiv 2016</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2015/">Bilderarchiv 2015</a>
<a class="blog-menu-link" href="/bilderarchiv-2015/">Bilderarchiv 2015</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2014/">Bilderarchiv 2014</a>
<a class="blog-menu-link" href="/bilderarchiv-2014/">Bilderarchiv 2014</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2013/">Bilderarchiv 2013</a>
<a class="blog-menu-link" href="/bilderarchiv-2013/">Bilderarchiv 2013</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2012/">Bilderarchiv 2012</a>
<a class="blog-menu-link" href="/bilderarchiv-2012/">Bilderarchiv 2012</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2011/">Bilderarchiv 2011</a>
<a class="blog-menu-link" href="/bilderarchiv-2011/">Bilderarchiv 2011</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2010/">Bilderarchiv 2010</a>
<a class="blog-menu-link" href="/bilderarchiv-2010/">Bilderarchiv 2010</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2009/">Bilderarchiv 2009</a>
<a class="blog-menu-link" href="/bilderarchiv-2009/">Bilderarchiv 2009</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2008/">Bilderarchiv 2008</a>
<a class="blog-menu-link" href="/bilderarchiv-2008/">Bilderarchiv 2008</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2005/">Bilderarchiv 2005</a>
<a class="blog-menu-link" href="/bilderarchiv-2005/">Bilderarchiv 2005</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2004/">Bilderarchiv 2004</a>
<a class="blog-menu-link" href="/bilderarchiv-2004/">Bilderarchiv 2004</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2003/">Bilderarchiv 2003</a>
<a class="blog-menu-link" href="/bilderarchiv-2003/">Bilderarchiv 2003</a>
</li>
@@ -183,42 +182,42 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/picture/">Fotoblog</a>
<a class="blog-menu-link" href="/category/picture/">Fotoblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/tag-cloud/">Tag Cloud</a>
<a class="blog-menu-link" href="/tag-cloud/">Tag Cloud</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/spielelog/">Spieleblog</a>
<a class="blog-menu-link" href="/category/spielelog/">Spieleblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/kochbuch/">Kochbuch</a>
<a class="blog-menu-link" href="/category/kochbuch/">Kochbuch</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/wiki/">Wiki</a>
<a class="blog-menu-link" href="/wiki/">Wiki</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/impressum/">Impressum</a>
<a class="blog-menu-link" href="/impressum/">Impressum</a>
</li>
@@ -278,32 +277,24 @@
<div class="single-post-taxonomy" aria-label="Taxonomy">
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/en/category/aside/">aside</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/category/aside/">aside</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/programmierung/">programmierung</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/programmierung/">programmierung</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/mac-os-x/">mac-os-x</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/mac-os-x/">mac-os-x</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/sysadmin/">sysadmin</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/sysadmin/">sysadmin</a>
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://www.cmux.dev/de">cmux — The Terminal for Multitasking</a> is a pretty brilliant terminal program for CLI workflows, which are experiencing a resurgence especially through agentic coding. And what I love about it: it has clean persistence of open workspaces where you can have multiple tabs, so I don&#39;t have to keep my work environment open all the time. I&#39;ve always liked having various directories open directly because I switch back and forth between several while programming, and this works much better with CMUX than with anything else.</p>
</div>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://www.cmux.dev/de">cmux — The Terminal for Multitasking</a> is a pretty brilliant terminal program for CLI workflows, which are experiencing a resurgence especially through agentic coding. And what I love about it: it has clean persistence of open workspaces where you can have multiple tabs, so I don't have to keep my work environment open all the time. I've always liked having various directories open directly because I switch back and forth between several while programming, and this works much better with CMUX than with anything else.</p></div>
</article>
<div class="single-post-backlinks" aria-label="Backlinks">
<span class="single-post-backlinks-label">Linked from</span>
<a class="single-post-taxonomy-bubble single-post-backlink-bubble" href="/en/2026/03/13/ghostty">ghostty</a>
</div>
</main>
</body>
</html>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Content-type: matter-transport/sentient-life-form</title>
<title>Ghostty</title>
<link rel="stylesheet" href="/assets/pico.slate.min.css" />
<link rel="stylesheet" href="/assets/lightbox.min.css" />
@@ -14,7 +14,7 @@
<link rel="alternate" type="application/rss+xml" title="RSS" href="/en/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="Atom" href="/en/atom.xml" />
<link rel="alternate" hreflang="en" href="/en/2026/03/13/ghostty" />
<link rel="alternate" hreflang="en" href="/2026/03/13/ghostty/" />
<script defer src="/assets/highlight.min.js"></script>
<script defer src="/assets/code-enhancements.js"></script>
@@ -27,14 +27,13 @@
<link rel="stylesheet" href="/en/pagefind/pagefind-ui.css" />
<script defer src="/en/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
<nav class="language-switcher" aria-label="Language">
<a class="language-switcher-badge" href="/en/" data-lang-prefix="" title="de">🇩🇪</a>
<a class="language-switcher-badge" href="/" data-lang-prefix="" title="de">🇩🇪</a>
@@ -49,7 +48,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Search..." data-search-no-results="No results found"></div>
</div>
</div>
</nav>
@@ -69,7 +68,7 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/">Home</a>
<a class="blog-menu-link" href="/">Home</a>
</li>
@@ -84,91 +83,91 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2022/">Bilderarchiv 2022</a>
<a class="blog-menu-link" href="/bilderarchiv-2022/">Bilderarchiv 2022</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2016/">Bilderarchiv 2016</a>
<a class="blog-menu-link" href="/bilderarchiv-2016/">Bilderarchiv 2016</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2015/">Bilderarchiv 2015</a>
<a class="blog-menu-link" href="/bilderarchiv-2015/">Bilderarchiv 2015</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2014/">Bilderarchiv 2014</a>
<a class="blog-menu-link" href="/bilderarchiv-2014/">Bilderarchiv 2014</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2013/">Bilderarchiv 2013</a>
<a class="blog-menu-link" href="/bilderarchiv-2013/">Bilderarchiv 2013</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2012/">Bilderarchiv 2012</a>
<a class="blog-menu-link" href="/bilderarchiv-2012/">Bilderarchiv 2012</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2011/">Bilderarchiv 2011</a>
<a class="blog-menu-link" href="/bilderarchiv-2011/">Bilderarchiv 2011</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2010/">Bilderarchiv 2010</a>
<a class="blog-menu-link" href="/bilderarchiv-2010/">Bilderarchiv 2010</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2009/">Bilderarchiv 2009</a>
<a class="blog-menu-link" href="/bilderarchiv-2009/">Bilderarchiv 2009</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2008/">Bilderarchiv 2008</a>
<a class="blog-menu-link" href="/bilderarchiv-2008/">Bilderarchiv 2008</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2005/">Bilderarchiv 2005</a>
<a class="blog-menu-link" href="/bilderarchiv-2005/">Bilderarchiv 2005</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2004/">Bilderarchiv 2004</a>
<a class="blog-menu-link" href="/bilderarchiv-2004/">Bilderarchiv 2004</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/bilderarchiv-2003/">Bilderarchiv 2003</a>
<a class="blog-menu-link" href="/bilderarchiv-2003/">Bilderarchiv 2003</a>
</li>
@@ -183,42 +182,42 @@
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/picture/">Fotoblog</a>
<a class="blog-menu-link" href="/category/picture/">Fotoblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/tag-cloud/">Tag Cloud</a>
<a class="blog-menu-link" href="/tag-cloud/">Tag Cloud</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/spielelog/">Spieleblog</a>
<a class="blog-menu-link" href="/category/spielelog/">Spieleblog</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/category/kochbuch/">Kochbuch</a>
<a class="blog-menu-link" href="/category/kochbuch/">Kochbuch</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/wiki/">Wiki</a>
<a class="blog-menu-link" href="/wiki/">Wiki</a>
</li>
<li class="blog-menu-item">
<a class="blog-menu-link" href="/en/impressum/">Impressum</a>
<a class="blog-menu-link" href="/impressum/">Impressum</a>
</li>
@@ -278,23 +277,22 @@
<div class="single-post-taxonomy" aria-label="Taxonomy">
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/en/category/aside/">aside</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-category" href="/category/aside/">aside</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/programmierung/">programmierung</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/programmierung/">programmierung</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/sysadmin/">sysadmin</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/sysadmin/">sysadmin</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/en/tag/mac-os-x/">mac-os-x</a>
<a class="single-post-taxonomy-bubble single-post-taxonomy-bubble-tag" href="/tag/mac-os-x/">mac-os-x</a>
</div>
<article class="single-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://ghostty.org/">Ghostty</a> is the foundation that <a href="/en/2026/03/13/cmux-das-terminal-fur-multitasking">cmux — The Terminal for Multitasking</a> was built on. Generally also a very nice terminal that responds noticeably faster than the standard terminal and already works very well. What I didn&#39;t like was that tabs weren&#39;t automatically reopened in the appropriate directories when the program was closed. I simply have too many persistent sessions that I keep coming back to. cmux just does that better.</p>
</div>
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
<div class="post"><p><a href="https://ghostty.org/">Ghostty</a> is the foundation that <a href="/en/2026/03/13/cmux-das-terminal-fur-multitasking/">cmux — The Terminal for Multitasking</a> was built on. Generally also a very nice terminal that responds noticeably faster than the standard terminal and already works very well. What I didn't like was that tabs weren't automatically reopened in the appropriate directories when the program was closed. I simply have too many persistent sessions that I keep coming back to. cmux just does that better.</p></div>
</article>
</main>

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,6 @@
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script defer src="/pagefind/pagefind-ui.js"></script>
</head>
<body>
<main>
@@ -47,7 +46,7 @@
</svg>
</button>
<div class="blog-search-panel" data-blog-search-panel hidden>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..."></div>
<div id="blog-search" data-blog-search-root data-search-placeholder="Suchen..." data-search-no-results="Keine Ergebnisse gefunden"></div>
</div>
</div>
</nav>
@@ -236,7 +235,7 @@
class="blog-menu-calendar-button"
data-blog-calendar-toggle
data-blog-calendar-year="2026"
data-blog-calendar-month="4"
data-blog-calendar-month="7"
aria-label="Kalender öffnen"
title="Kalender öffnen"
>
@@ -257,7 +256,7 @@
hidden
>
<header class="blog-calendar-header">
<strong>Archivkalender</strong>
<strong>Archiv</strong>
<button
type="button"
class="blog-calendar-close"
@@ -285,13 +284,22 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>01.04.2026</span></aside>
<aside class="archive-day-marker"><span>2026-07-17</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/stonerl/Thaw">stonerl/Thaw: Menu bar manager for macOS 26</a> - hilft gegen das Notch auf Apple MacBooks. Schon peinlich, dass man ein extra Programm braucht, um das zu machen, und dass das System das nicht von Hause aus unterstützt.</p>
<p><a href="https://huggingface.co/mlx-community/ThinkingCap-Qwen3.6-27B-OptiQ-4bit">mlx-community/ThinkingCap-Qwen3.6-27B-OptiQ-4bit · Hugging Face</a> - mit dem Modell muss ich mal rumspielen, das verspricht besseres Reasoning mit weniger Token und liefert gute Performance auf meinem MacBook Pro - wenn das sich bewährt, könnte das für lokale Agents durchaus interessant sein.</p>
</div>
<div class="post">
<p><a href="https://www.hunk.dev/">hunk — review-first terminal diff viewer</a> ist ein Diff-Viewer, der speziell mit <a href="/2026/07/17/herdr-one-terminal-for-the-whole-herd">Herdr: one terminal for the whole herd</a> zusammenarbeitet. Ein Agent kann Diff erzeugen und über einen Skill Kommentare in den Diff einarbeiten und dann lässt sich das in Hunk anzeigen und der Benutzer kann ebenfalls im Diff kommentieren - also klassischer Review-Prozess, nur eben rein lokal und im Terminal.</p>
</div>
<div class="post">
<p><a href="https://herdr.dev/">Herdr: one terminal for the whole herd</a> - ich bin ja alter screen User, aber ich glaube es wird wirklich Zeit, dass ich mich mal mental modernisiere. Herdr ist screen auf steroiden. Es hat eine Client-Server Architektur, remote-Installation und Nutzung, und eine sehr gute Agent-Integration. Agent Sessions tauchen in der Übersicht auf, genauso wie Projektstatus (Branch etc.), alles schön in einem Sidebar. Tabs können geöffnet werden, Screens gesplittet werden und obwohl es eine Terminal-Anwendung ist, unterstützt es die Maus. Fühlt sich wirklich gut in der Benutzung an.</p>
</div>
</div>
@@ -304,13 +312,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>29.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-07-14</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/Fission-AI/OpenSpec?tab=readme-ov-file">Fission-AI/OpenSpec: Spec-driven development (SDD) for AI coding assistants.</a> ist eine leichtgewichtigere Variante zu <a href="/2026/03/27/juxt-allium-a-language-for-sharpening-intent-alongside-implementation">juxt/allium: A language for sharpening intent alongside implementation.</a>. Könnte auch ganz interessant sein, das Tooling ist weiter ausgebaut, dafür die Sprache schwächer. Allium ist wesentlich formaler und in Richtung Pseudocode.</p>
<p><a href="https://github.com/JustVugg/colibri">JustVugg/colibri: Run GLM-5.2 (744B MoE) on a 25GB-RAM consumer machine — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦</a> - eines der wildesten Projekte in der letzten Zeit, weil darüber ein frontier Modell auf völlig ungeeigneter Hardware betrieben werden kann. Mit grausigen Token/Sekunde, aber laufen. Und es gibt schon Metal und Cuda Backends, also ein bisschen Beschleunigung ist schon da.</p>
</div>
</div>
@@ -323,25 +330,22 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>27.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-07-13</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/juxt/allium">juxt/allium: A language for sharpening intent alongside implementation.</a> ist ein sehr spannendes Projekt. Und zwar eine Spezifikationssprache für Verhalten von Softwaresystemen. Eine Sprache, für die es keine Laufzeitumgebung oder Compiler gibt, außer der LLM. Implementiert als Agent Skills. Super spannend, weil es unter anderem auch ein Distill gibt, mit dem man bestehende Software analysieren und retroaktiv Spezifikationen aufbauen kann, oder im Interviewverfahren mit der KI eine Spezifikation ausarbeiten kann, die für ein LLM deutlich präziser verständlich ist als allgemeines Englisch. Witziges Detail am Rande: ich habe Allium mit Qwen3-Coder-Next ausprobiert, meinem derzeitigen Lieblingsmodell für lokales Hosting, in <a href="/2026/03/14/pi-dev">pi.dev</a>. Ich konnte das binary für Allium (ein Syntaxchecker und Linter) nicht mit homebrew installieren, pi.dev hat einfach kurzerhand das Binary runtergeladen und selber installiert.</p>
<p><a href="https://github.com/567-labs/instructor">567-labs/instructor: structured outputs for llms</a> - und hier die Alternative zu <a href="/2026/07/13/dottxt-ai-outlines-structured-outputs">dottxt-ai/outlines: Structured Outputs</a>, aber hinter dem LLM und nicht in der Generierung. Im Prinzip ist das die klassische Korrekturschleife für LLM Antworten, die man ständig baut. Also parsen, prüfen, validieren, konkrete Fehler zurückmelden und Prompt anpassen und wiederholen, bis es passt.</p>
</div>
<div class="post">
<p><a href="https://github.com/scitrera/cuda-containers">scitrera/cuda-containers: Scitrera builds of various CUDA containers for version consistency, starting primarily with NVIDIA DGX Spark Containers</a> bin ja im Moment ein großer Fan von eugr/vllm-node als Basispaket, weil es immer aktuelle Versionen für vLLM bietet, aber wenn ich mal mit sglang spielen will, ist das hier vermutlich das ähnlichste Projekt. Mich interessiert da speziell EAGLE-3 spekulatives Dekodieren - im Prinzip werden dabei über ein sehr kleines Modell parallel Token generiert und das Hauptmodell guckt was passt und nimmt das, oder generiert selber wenn nötig. Dadurch kann man oft ein drittel der Token über ein viel schnelleres banales Modell im &lt;3B Bereich generieren lassen und nur jedes dritte wirklich über das große Modell.</p>
<p><a href="https://github.com/dottxt-ai/outlines">dottxt-ai/outlines: Structured Outputs</a> - ein sehr spannendes Tooling das sich direkt in vLLM einhängt und Replies von LLMs gegen definierte Schemas für Antworten prüft und direkt in der Generierung illegale Token rejected und damit garantiert, dass der Output den erwarteten Formaten entspricht. Gerade für kleinere Modelle interessant die oft Schwächen bei Toolformaten haben. Gerade für stringente Workflows in denen man genau weiss, was das LLM antworten soll, kann das hilfreich sein.</p>
</div>
<div class="post">
<p><a href="https://github.com/thushan/olla">thushan/olla: High-performance lightweight proxy and load balancer for LLM infrastructure. Intelligent routing, automatic failover and unified model discovery across local and remote inference backend</a> ist nach dem Debakel von LiteLLM (gehackte supply-chain mit Datenextraktor im Paket) vielleicht die bessere Wahl. Für mich auf jeden Fall interessant, weil ich einfach nur zwei Modelle betreiben will und diese unter einem Endpunkt verfügbar machen will, und da sind alle anderen Pakete deutlicher Overkill.</p>
<p><a href="https://github.com/datalab-to/marker">datalab-to/marker: Convert PDF to markdown + JSON quickly with high accuracy</a> - eine interessante Lösung für ein PDF Preprocessing, dass aus PDF Markdown macht, aber die Struktur und die Lesefolge berücksichtigt. Gerade für Dokumentenverständnis ist das essentiell, da Tabellen in ihrerer Anordnung eine Menge an Information transportieren und das tool dabei helfen kann.</p>
</div>
</div>
@@ -354,13 +358,17 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>25.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-07-06</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://forums.developer.nvidia.com/t/running-mistral-small-4-119b-nvfp4-on-nvidia-dgx-spark-gb10/363863/26">Running Mistral Small 4 119B NVFP4 on NVIDIA DGX Spark (GB10) - DGX Spark / GB10 User Forum / DGX Spark / GB10 - NVIDIA Developer Forums</a> - live-saver Diskussion in den NVIDIA Foren. Mit dem was da steht habe ich Mistral Small 4 zum Fliegen bekommen. Und es läuft sauber mit 150K Context und 100 Token/Sekunde in der Generierung. Wow. Das ist das erste Mal, dass ich die Power der Kiste wirklich bemerke.</p>
<p><a href="https://github.com/jakobdylanc/llmcord">jakobdylanc/llmcord: Make Discord your LLM frontend - Supports any OpenAI compatible API (OpenRouter, Ollama and more)</a> ist ein discord bot in Python geschrieben, der ein LLM integriert und als Chatbot in Discord verfügbar macht. Das ganze als simpler Docker Container. Leicht einzurichten und simpel.</p>
</div>
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/391163/forest-shuffle">Forest Shuffle</a> ist ein netter Tableau-Builder mit ziemlich coolen Kartencombos. Das ganze ist relativ leicht zu lernen und zu spielen, einzig der etwas geringe Kartenpool könnte den Wiederspiel-Reiz etwas dämpfen, aber die Erweiterungen helfen dabei.</p>
</div>
</div>
@@ -373,37 +381,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>22.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-07-05</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://zed.dev/blog/fastest-ai-code-editor">Zed: The Fastest AI Code Editor — Zed&#39;s Blog</a> ist ein ziemlich cooler Editor, der endlich auch mit einer guten VIM Implementation daher kommt. Unterstützt Coding Agents und ist offen für Open Weight und home-hosted Modelle. Gefällt mir ehrlich gesagt sehr gut und ist wirklich schnell. Und ist in Rust geschrieben, was mich nochmal mehr begeistert.</p>
</div>
<div class="post">
<p><a href="https://mistral.ai/news/mistral-small-4">Introducing Mistral Small 4 | Mistral AI</a> ist ein weiterer interessanter Kandidat für die <a href="/2026/03/22/asus-ascent-gx10-asus-deutschland">ASUS Ascent GX10ASUS Deutschland</a>, vor allem brauche ich dort keine Side-car Modelle für Vision, weil das Modell selber auch schon gleich Vision mitbring. Und als MoE Modell dürfte es auch gut Speed hinlegen.</p>
</div>
<div class="post">
<p><a href="https://huggingface.co/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4">nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 · Hugging Face</a> wird wahrscheinlich das erste große Modell auf der <a href="/2026/03/22/asus-ascent-gx10-asus-deutschland">ASUS Ascent GX10ASUS Deutschland</a> werden, weil es optimiert auf NVFP4 trainiert wurde - und damit nicht eine &quot;verdummung&quot; erfährt, wegen der Quantisierung, sondern ganz normal wie erwartet funktioniert. Und es ist auf agentische Workflows optimiert, was OpenClaw begünstigen sollte, genauso wie der 1M Context, der in dem Modell vermutlich sogar genutzt werden kann (andere Architektur als klassische Transformer basierte Modelle).</p>
</div>
<div class="post">
<p><a href="https://tailscale.com/wireguard-vpn">WireGuard® for Enterprise</a> klingt komisch auf einem rein privaten Blog mit dem Namen, aber die haben recht günstige Tarife für VPN-ähnliche Konstrukte, mit denen man sehr leicht seine lokalen Geräte zu Hause auch von Unterwegs erreichbar machen kann. Gibt sicherlich auch andere Alternativen, aber ich glaube wenn ich meinen Agent lokal laufen lasse, stecke ich das Geld für Subscriptions lieber in einen ordentlichen VPN Dienst, da habe ich generell mehr von. Update: ist kostenlos für Privatbenutzer bis 3 User. Nett.</p>
</div>
<div class="post">
<p><a href="https://www.asus.com/de/networking-iot-servers/desktop-ai-supercomputer/ultra-small-ai-supercomputers/asus-ascent-gx10/">ASUS Ascent GX10ASUS Deutschland</a> kommt in den nächsten Tagen. KI Bolide, der es mir erlauben wird, auch größere Modelle lokal zu betreiben und z.B. einen OpenClaw Agent autonom zu Hause zu betreiben, ohne dafür dann auch noch Subscriptions zu brauchen. Bin schon echt gespannt, was damit geht. Kommt mit angepasstem Ubuntu, auch ein nettes Detail.</p>
<p><a href="https://lmcache.ai/en/">LMCache Building the foundation of AI memory tensor with KV Cache Infrastructure</a> ist ein kv cache manager, der den cache über GPU Speicher und SSD verteilen kann und auch hilft, wenn ein Modell über mehrere Maschinen verteilt läuft. Ich selbst benutze ihn primär als offloader für kvcache auf die Platte beim Qwen3.6 27B, weil das einen etwas heftigeren Bedarf an Speicher für kvcache hat als das 35B Modell.</p>
</div>
</div>
@@ -416,19 +399,19 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>19.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-06-30</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/topoteretes/cognee">topoteretes/cognee: Knowledge Engine for AI Agent Memory in 6 lines of code</a> ist auch etwas, das ich mir für später merke. Im Prinzip ein Wissens-Graph der über eine LLM gesteuert aufgebaut wird, um Memory für eine andere LLM verfügbar zu machen. Sicherlich spannend damit zu spielen, wenn ich mal eine gute lokale Hardware habe, auf der ich größere Modelle betreiben kann. Aber für jetzt nur ein Merker.</p>
<p><a href="https://supacode.sh/">Supacode</a> ist ein weiteres sehr nettes Terminal dass glaube ich
<a href="/2026/03/13/cmux-das-terminal-fur-multitasking">cmux — Das Terminal für Multitasking</a>
für mich ersetzen kann. Es wirkt jedenfalls sehr gut auf den ersten Blick, irgendwie ausgereifter als cmux.</p>
</div>
<div class="post">
<p><a href="https://www.docker.com/blog/docker-model-runner-vllm-metal-macos/">Docker Model Runner Adds vLLM Support on macOS | Docker</a> - erstmal nur gemerkt, das könnte später interessant werden, weil ich darüber Modelle über Docker mit vllm betreiben kann, bei gleichzeitiger Nutzung von Apple Silicon. Interessant daran ist hier, dass es als Docker Image fertig konfiguriert kommt und ich mich nicht mit Setup rumschlagen muss. Im Moment arbeite ich ja eher mit meinem eigenen <a href="/2026/03/18/rfc1437-mlxserver-a-simple-mlx-based-server-for-small-models-to-run-locally">rfc1437/MLXServer: a simple MLX based server for small models to run locally</a>, einfach weil ich es eh nur für den Offline-Betrieb brauche, aber für später könnte vllm-metal sehr spannend sein.</p>
<p><a href="https://huggingface.co/deepreinforce-ai/Ornith-1.0-35B">deepreinforce-ai/Ornith-1.0-35B · Hugging Face</a> ist ein Modell basierend auf Qwen3.5 35B 8bit mit Post-Training für explizite Unterstützung von Coding. Es soll deutlich besser für Coding sein als andere 35B MoE Modelle und auch gegenüber Qwen 3.6 35B besser abschneiden. Erste Experimente von mir zeigen soliden Tool-Use mit wenig Fehlersituationen, das lässt schon mal hoffen. Ich könnte mir gut vorstellen das als Basismodell für einen Hermes Agent einzusetzen, zum Beispiel, einfach weil es rasend schnell ist (lokal auf meinem M5 Max MacBook Pro &gt;80 Token pro Sekunde).</p>
</div>
</div>
@@ -441,19 +424,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>18.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-06-21</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/rfc1437/MLXServer">rfc1437/MLXServer: a simple MLX based server for small models to run locally</a> ist ein Tool, das ich mir (KI-gestützt) gebaut habe, um kleine Modelle direkt lokal auszuführen, ohne großen Ballast. Das frisst nicht viel Speicher, hat gleich einen lokalen Chat eingebaut für eigene Experimente und fühlt sich für mich deutlich praktischer an als die großen Alternativen - weniger Knöpfe zum Einstellen, aber dadurch auch weniger Verwirrung. Ich will ja einfach nur ein kleines Modell lokal betreiben für mein Blog on-the-road.</p>
</div>
<div class="post">
<p><a href="https://huggingface.co/mlx-community/Qwen3.5-9B-MLX-4bit">mlx-community/Qwen3.5-9B-MLX-4bit · Hugging Face</a> ist noch ein weiteres nettes kleines Modell - größer als die anderen, dadurch aber auch etwas konsistenter in der Ausführung, aber immer noch echt fix. Und ist so die Obergrenze, was man auf einem MacBook Air M4 mit 16G Ram betreiben kann, ohne den Rechner zu crashen.</p>
<p><a href="https://github.com/DietrichGebert/ponytail">DietrichGebert/ponytail: Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote.</a> - ein sehr interessantes Plugin für diverse coding agents (unter anderem Pi und OpenCode, die ich am häufigsten benutze, aber auch alle großen bekannten Agenten aus dem kommerziellen Umfeld), mit dem unnötiger Code gefunden werden kann. Es zielt direkt auf die typischen Schwächen von angentisch erstelltem Code, der oft unnötige Abstraktionen und Indirektionen und Code-Duplikate enthält, die deutlich einfacher abbildbar sind. Auch Re-Implementierungen von Sachen die schon im Standard enthalten sind oder unnötige Abhängigkeiten von externen Bibliotheken, die man leicht durch wenige Inlines und Standardfunktionen ersetzen kann.</p>
</div>
</div>
@@ -466,13 +442,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>16.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-06-20</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://huggingface.co/google/gemma-3-4b-it">google/gemma-3-4b-it · Hugging Face</a> ist ein ziemlich nettes Modell, dass für viele europäische Sprachen trainiert ist und daher gut für Übersetzungen lokal benutzt werden kann - es lädt unter 4G in den Speicher und belegt im Betrieb in der Interferenz ca. 6.5G. Und es hat Vision Capability, kann also auch benutzt werden um Bildbeschreibungen zu bekommen. Ideal, um z.B. mit bDS als lokales Modell benutzt zu werden wenn man offline unterwegs sein will. Und deutlich kleiner als <a href="/2026/03/02/mlx-community-gemma-3-12b-it-4bit-hugging-face">mlx-community/gemma-3-12b-it-4bit · Hugging Face</a> - das war bei meinem Macbook Air grenzwertig.</p>
<p><a href="https://huggingface.co/AbarthJoe/Qwopus3.6-27B-v2-oQ8-mtp">AbarthJoe/Qwopus3.6-27B-v2-oQ8-mtp · Hugging Face</a> ist ein sehr interessantes Modell: Qwen 3.6 27B - eines der besten dichten Modelle für Coding - mit fine-tuning basierend auf Opus Thinking Bubbles. Das Ziel ist eine gute Basis für ein Coding-Modell zu haben, dass mit relativ geringem Speicher auskommen kann. Und in dieser Version ist es die MLX konvertierte Variante mit 8bit Quantisierung und allen Gewichten für natives MTP. Auch wenn das nicht der aller schnellste Renner ist, ist es immerhin benutzbar und ziemlich gut schon bei der Analyse von Code in meinen kleinen Tests.</p>
</div>
</div>
@@ -485,19 +460,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>15.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-06-18</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://inferencer.com/">Inferencer | Run and Deeply Control Local AI Models</a> ist ein interessantes Tool, mit dem man LLMs lokal betreiben kann. Das kann natürlich LM Studio oder <a href="/2026/03/01/ollama">Ollama</a> oder <a href="/2026/03/02/waybarrios-vllm-mlx-openai-and-anthropic-compatible-server-for-apple-silicon">vllm-mlx</a> auch. Aber Inferencer hat ein Feature namens &quot;Model streaming&quot;, das ziemlich cool ist: es kann Modelle betreiben, die eigentlich zu groß für den Speicher sind. Natürlich tauscht man da Zeit gegen Speicher, aber für ein lokales Modell zur Bildbetitelung oder ähnliche kleinere Sachen kann man das durchaus mal benutzen. Ich habe aber das Gefühl, dass das Modell dadurch irgendwie fragiler wird - z.B. nutzt es Tools plötzlich nicht mehr richtig (ich habs mit gemma3 12b probiert, das so gerade an der Speichergrenze meines Laptops kratzt).</p>
</div>
<div class="post">
<p><a href="https://pagefind.app/">Pagefind | Pagefind — Static low-bandwidth search at scale</a> ist eine statische Suchmaschine für statisch generiertes HTML, so wie mein Blog. Und wird demnächst die Suche auf diesem Blog betreiben. Keine externen abhängigkeiten, kein Server, keine Komplexität der Infrastruktur - einfach nur ein paar zusätzliche Files, die mit hochgeladen werden. Und natürlich aktives JavaScript im Browser.</p>
<p><a href="https://github.com/antirez/ds4">antirez/ds4: DeepSeek 4 Flash and PRO local inference engine for Metal, CUDA and ROCm</a> ist ein ziemlich interessantes Projekt, mit dem Deepseek (spezielle Q2 Quants) möglichst effizient auf verschiedenen UMA Notebooks läuft. Unterstützt sind MacBook, DGX Spark und ROCm Strix Halo Rechner. Ziemlich cool, wenn das auch funktioniert - Deepseek V4 Flash ist schon ziemlich gut als Basismodell für verschiedene Zwecke, wie zum Beispiel als Main-Modell für Hermes.</p>
</div>
</div>
@@ -510,13 +478,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>14.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-05-30</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://shittycodingagent.ai/">pi.dev</a> ist ein minimalistischer Harness für agentic coding, dessen Fokus nicht auf Features, sondern auf Erweiterbarkeit liegt. Die Grundidee ist solide:. eine sehr einfache Harness die durch Typescript-Plugins erweitert werden kann, so dass sich die Harness an den eigenen Workflow anpassen und an die eigenen Anforderungen. Werde ich mir vielleicht demnächst mal angucken.</p>
<p><a href="https://omlx.ai/">oMLX — LLM inference, optimized for your Mac</a> ist so ein bisschen wie mein MLX Server, aber hat mehr Fokus auf effiziente Ausführung und als Gimmick einen persistierten KV Cache, so dass alte Prefixe wiederbelebt werde. Könnte ich mit meinem kommenden MacBook Pro mal testen, ob das interessant für mich ist, insbesondere das Beispiel mit dem Qwen 3.5 122B A10B sieht gut aus, das ist auch im Moment auf meine DGX mein favorisiertes Modell.</p>
</div>
</div>
@@ -529,43 +496,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>13.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-05-29</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/steveyegge/beads">steveyegge/beads: Beads - A memory upgrade for your coding agent</a> ist ein Todo-Listen-Tool für Agenten. Im Prinzip ein Gedächtnis für Projekte über das sich Agenten steuern können (hinterlegen von Tasks und Features) und auch komplexere Abläufe gesteuert werden können, bei denen ein Agent über eine ganze Reihe von Issues laufen muss und diese lösen muss, wo Tasks abhängigkeiten haben können und Agenten nur den Satz an Aufgaben sehen, die auch wirklich gerade angegangen werden können. Interessant für Projekte, in denen man Agenten in Loops laufen lassen will, um größere Aufgaben zu lösen.</p>
</div>
<div class="post">
<p><a href="https://github.com/dolthub/doltgresql">dolthub/doltgresql: DoltgreSQL - Version Controlled PostgreSQL</a> ist der PostgreSQL-flavored Partner von <a href="/2026/03/13/dolthub-dolt-dolt-git-for-data">dolthub/dolt: Dolt Git for Data</a> und bietet die gleichen Features, nur mit Anlehnung an PostgreSQL und einem Binär-Interface das zu PostgreSQL kompatibel ist.</p>
</div>
<div class="post">
<p><a href="https://github.com/dolthub/dolt?tab=readme-ov-file">dolthub/dolt: Dolt Git for Data</a> ist eine SQL Datenbank, die intern mit Mechanismen wie git arbeitet und dadurch Daten-Branches und Merges unterstützt und allgemein die Möglichkeit bietet, dass Daten in der Datenbank versioniert und mit History funktionieren. Und das ganze auch analog zu git erlaubt die Datenchanges über die Versionsverwaltung zu verteilen. Im Prinzip &quot;slow transaction shipping&quot;.</p>
</div>
<div class="post">
<p><a href="https://github.com/paperclipai/paperclip">paperclipai/paperclip: Open-source orchestration for zero-human companies</a> ist ein Ansatz um Agenten zu orchestrieren und zu verwalten. Das interessante hier ist, dass es mit einer Organisation angelehnt an eine Firmenorganisation arbeitet und mit Mitteln die Kommunikation der Agenten abbildet, die eine gute Transparenz bieten könnte. Ich habe es noch nicht ausprobiert, aber neben <a href="https://github.com/steveyegge/gastown">gastown</a> eines der interessanteren Projekte zu diesem Thema. Das ganze Umfeld von Agent-Orchestrierung ist alles noch recht neu, von daher ist das noch ein großes Feld an Experimenten, aber spannend zu beobachten.</p>
</div>
<div class="post">
<p><a href="https://ghostty.org/">Ghostty</a> ist die Basis, auf der <a href="/2026/03/13/cmux-das-terminal-fur-multitasking">cmux — Das Terminal für Multitasking</a> aufgebaut wurde. Generell ebenfalls ein sehr nettes Terminal, das auch deutlich schneller reagiert als das Standardterminal und auch schon sehr gut funktioniert. Was mir nicht gefiel, war dass Tabs nicht automatisch in den passenden Pfaden wieder neu geöffnet wurden, wenn das Programm beendet wurde. Ich habe einfach zu lange persistente Sessions, die ich immer wieder aufgreife. Das macht cmux einfach besser.</p>
</div>
<div class="post">
<p><a href="https://www.cmux.dev/de">cmux — Das Terminal für Multitasking</a> ist ein ziemlich geniales Terminal-Programm für die gerade durch Agentic Coding wieder verstärkt kommenden CLI Workflows. Und was mich begeistert: es hat eine saubere Persistierung von offenen Workspaces in denen man dann mehrere Tabs haben kann, so dass ich meine Arbeitsumgebung nicht ständig offen lassen muss. Ich habe immer gerne diverse Verzeichnisse direkt offen, weil ich zwischen mehreren hin und her wechsel wärend der Programmierung, das läuft mit CMUX deutlich besser als mit allen anderen.</p>
<p><a href="https://mtplx.com/">MTPLX — Twice as fast on MLX</a> gemerkt für später, wenn mein neues MacBook Pro kommt. Dann habe ich die nötige Hardware, um das Modell laufen zu lassen und MTPLX wird dann extrem wichtig für die Geschwindigkeit.</p>
</div>
</div>
@@ -578,13 +514,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>12.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-05-28</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://www.nuget.org/packages/Photino.Blazor.CustomWindow">NuGet Gallery | Photino.Blazor.CustomWindow 1.3.1</a> ist eine Library für Photino Blazor, mit der man Fenster Chrome-less machen kann. Also Titelbalken und Standarddekorationen entfernen kann. Idee dahinter ist darüber mehr Kontrolle über das Look-and-Feel zu bekommen und kompaktere UIs zu schaffen. Mit dieser Library bekommt man dann Grundfunktionen zurück wie Fenstergrößenänderungen und andere Standardelemente, die Benutzer erwarten, aber unter voller Kontrolle der Anwendung.</p>
<p><a href="https://livebook.dev/">Livebook.dev</a> ist sowas wie Jupyter Notebooks, aber für Elixir. Sieht sehr spannend aus und hat viele eingebaute Elixir Features und Erweiterungen mit im Bauch (z.B. Bumblebee für AI Anwendungen).</p>
</div>
</div>
@@ -597,13 +532,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>07.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-04-27</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://velvetshark.com/openclaw-memory-masterclass">OpenClaw Memory Masterclass: The complete guide to agent memory that survives • VelvetShark</a> - interessante Zusammenstellung des Memory-Systems und der Fallstricke mit Compaction bei Openclaw. Der Agent ist ja dazu da, über lange Zeit zu laufen, dadurch ist aber auch immer die Gefahr, dass Compaction gerade in komplexeren Situationen mitten drin zuschlägt. Und openclaw läuft autonom, also will man sicher sein, dass es auch kontinuierlich weitergeht.</p>
<p><a href="https://opencode.ai/workspace/wrk_01KEYV5DBEPH20CZFAF98C5W01/go">opencode</a> - nach den hundsmiserablen Preiserhöhungen der diversen Coding Agent Betreibern (Microsoft jetzt als letztes mit massiven Preiserhöhungen) wirds immer wichtiger auf alternativen zu gucken und OpenCode Go ist im Moment ein durchaus ernstzunehmendes Angebot mit einigen ziemlich guten Modellen im Mix, die auch noch open weight sind und dadurch auch über mehr als einen Anbieter betrieben werden können. Hat sich bei mir für meinen OpenClaw gut bewährt bisher.</p>
</div>
</div>
@@ -616,13 +550,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>05.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-04-10</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/unum-cloud/USearch">unum-cloud/USearch</a> - drin was drauf steht. Also eine Library, die einen Index für Vektoren bietet, die z.B. aus Embedding stammen können und so semantisch ähnliche Texte finden können. Nicht Text-ähnlich, sondern semantisch, also Inhalt. Spannendes Thema, die dafür nötigen Modelle sind mit den LLMs verwandt, aber eben nicht large, sondern small - sie brauchen nicht voll zu verstehen und zu generieren, weil sie nur Vektoren erstellen, die dann gegeneinander verglichen werden können und je höher die Ähnlichkeit, desto höher die Ähnlichkeit der Texte im Thema. Cooles kleines Feature für bDS.</p>
<p><a href="https://github.com/Dicklesworthstone/pi_agent_rust">Dicklesworthstone/pi_agent_rust: High-performance AI coding agent CLI written in Rust with zero unsafe code</a> ist ein ganz interessantes Projekt, das eine Reimplementierung von <a href="/en/2026/03/14/pi-dev/">pi.dev</a> in Rust ist, mit stärkerem Fokus auf Sicherheit, aber weiterhin der Offenheit von Pi, also nicht tausende von Tools und Workflows schon im Kern, sondern einfach ein m inimalistischer Agent.</p>
</div>
</div>
@@ -635,19 +568,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>02.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-04-04</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/waybarrios/vllm-mlx">waybarrios/vllm-mlx: OpenAI and Anthropic compatible server for Apple Silicon.</a> Den benutze ich, um <a href="/2026/03/02/mlx-community-gemma-3-12b-it-4bit-hugging-face">mlx-community/gemma-3-12b-it-4bit</a> auf meinem MacBook Air zu betreiben. Klappt sehr gut, kleines Shell-Script zum Starten des Servers und dann bin ich autonom. Nicht so komfortabel wie Ollama, aber dafür unterstützt es perfekt Apple&#39;s MLX und nutzt damit Silicon gut aus.</p>
</div>
<div class="post">
<p><a href="https://huggingface.co/mlx-community/gemma-3-12b-it-4bit">mlx-community/gemma-3-12b-it-4bit · Hugging Face</a> ist das bisher beste Modell für lokalen Betrieb, mit dem ich die Bildbetitelung und sogar lokalen Chat realisieren kann. Nicht das schnellste, da es schon recht groß ist, aber für Offline-Betrieb absolut geeignet, wenn ich mir da ein paar Mechanismen für Batchverarbeitung von Bildern etc. einfallen lasse. Das könnte gerade für Urlaubszeiten super spannend sein. Eine Bildbeschreibung liegt dann zwar bei einer Minute, aber hey, dafür keine Abhängigkeiten.</p>
<p><a href="https://github.com/nearai/ironclaw?tab=readme-ov-file">nearai/ironclaw: IronClaw is OpenClaw inspired implementation in Rust focused on privacy and security</a> könnte irgendwann mal interessant werden. Im Prinzip OpenClaw aber in Rust. Mir sind bei OpenClaw eine ganze Reihe von Sachen um die Ohren geflogen. OpenAI subscriptions (die 20 pro Monat) ist zu klein für einen Agenten, das Wochenlimit läuft zu schnell voll. Wechsel auf billigere open weight Modelle hat dann gezeigt, dass bei OpenClaw wirklich nur die primären Pfade im Code getestet werden, da sind mir reihenweise Sachen um die Ohren geflogen, die vorher funkmtioniert haben. Dazu kommt dann noch, dass es eine ganze Reihe von erfolgreichen Supply Chain Attacks gegen NPM gegeben hat - offensichtlich ist die Absicherung von NPM mehr als mangelhaft. Also erstmal den OpenClaw VPS gelöscht. Das Leben ist zu kurz für schlechte Software.</p>
</div>
</div>
@@ -660,19 +586,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>01.03.2026</span></aside>
<aside class="archive-day-marker"><span>2026-04-01</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://models.dev/">Models.dev — An open-source database of AI models</a> ist eine sehr praktische Seite, die für alle möglichen Anbieter und alle möglichen LLMs Rahmenparameter liefert, inklusive sogar API Preise. Und technische Parameter wie Input/Output Tokens.</p>
</div>
<div class="post">
<p><a href="https://ollama.com/">Ollama</a> - eine Runtime-Umgebung für LLMs, die es erlaubt Modelle lokal zu betreiben. Mein Lieblingsmodell zur Zeit: <a href="https://ollama.com/library/qwen2.5vl:7b-q4_K_M">qwen2.5vl:7b-q4_K_M</a>. Mit nur 6.6 GB Größe läuft das problemlos auf einem MacBook Air M4 und hat noch genug Speicher und Kapazität frei um Programme nebenbei laufen zu lassen. Das Modell ist im Chat erstaunlich brauchbar und vor allem hat es klasse Vision-Fähigkeiten. Ideal um für Bilder Titel, Alt-Texte oder Zusammenfassungen zu liefern, ohne dafür Geld an große Provider abzudrücken. Und ein wichtiger Baustein, um bDS wieder zurück zu full-offline zu bringen.</p>
<p><a href="https://github.com/stonerl/Thaw">stonerl/Thaw: Menu bar manager for macOS 26</a> - hilft gegen das Notch auf Apple MacBooks. Schon peinlich, dass man ein extra Programm braucht, um das zu machen, und dass das System das nicht von Hause aus unterstützt.</p>
</div>
</div>
@@ -685,31 +604,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>28.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-03-29</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/mistralai/mistral-vibe">mistralai/mistral-vibe: Minimal CLI coding agent by Mistral</a> - begleitend zum <a href="/2026/02/28/ai-studio-mistral-ai">AI Studio - Mistral AI</a> gibt es die Vibe-Coding Oberfläche zu Devstral auch als Open Source. Sehr nett, weil es ein gutes Paar macht. Wird definitiv bei mir etwas ausprobiert, auch wenn ich sicherlich für große Projekte dann eher zu den Boliden (Opus 4.6) greifen würde.</p>
</div>
<div class="post">
<p><a href="https://console.mistral.ai/home">AI Studio - Mistral AI</a> - da in den USA ja doch wieder die Lage etwas angespannter wird, und einfach weil man immer mal gucken sollte, was außerhalb der USA passiert, hier ein Link auf eine europäische Alternative zu den großen US Betreibern. Mistral bietet mit Devstral 2 ein Coding-Modell an, das nicht nur open weights ist (also frei zu bekommen und zu betreiben, wenn man die nötige Hardware hat), sondern auch im Betrieb bei Nutzung von Mistral selber recht günstig ist. Und die Leistung liegt etwas oberhalb Claude Haiku 4.5, und zwar unterhalb Sonnet 4.5, aber nicht super weit. Also durchaus brauchbar und meine ersten Experimente waren nicht schlecht. Leider keine Vision-Fähigkeit, also für Experimente mit Bildern nicht so geeignet (und daher für mein bDS nicht ideal), aber trotzdem spannend genug um es im Auge zu behalten.</p>
</div>
<div class="post">
<p><a href="https://niklas-luhmann-archiv.de/bestand/zettelkasten/zettel/ZK_1_NB_1_1_V">ZK I Zettel 1 (1) - Niklas Luhmann-Archiv</a> - woher die Inspiration für mein Blog kommt, bzw. was mich immer unter der technischen Oberfläche bewegt hat.</p>
</div>
<div class="post">
<p>wer so wie ich gerne einen Überblick über UI-integration von LLMs haben möchte und sich fragt, wie A2UI und MCP Apps im Vergleich arbeiten und was sie bieten: <a href="https://ricmac.org/2025/12/19/agent-ui-standards-multiply-mcp-apps-and-googles-a2ui/">Agent UI Standards Multiply: MCP Apps and Googles A2UI - Richard MacManus</a> hilft. Ich habe in bDS ja A2UI implementiert, damit im internen Chat das LLM auch visuelle Aspekte nutzen kann, und das gefällt mir schon sehr gut. Aber die Idee, Teile meines UI auch in externe Agents einzubringen ist auch faszinierend. Auch wenn ich finde, dass &quot;lokales HTML/JS in einem IFrame&quot; irgendwie erstmal nach Hack klingt, aber vieles im LLM Umfeld gibt mir das Gefühl im Moment, einfach weil ja alles über einen normalen Text-Stream geschoben wird und man hofft, dass die LLMs sich an die Formate halten (selbst A2UI arbeitet so).</p>
<p><a href="https://github.com/Fission-AI/OpenSpec?tab=readme-ov-file">Fission-AI/OpenSpec: Spec-driven development (SDD) for AI coding assistants.</a> ist eine leichtgewichtigere Variante zu <a href="/en/2026/03/27/juxt-allium-a-language-for-sharpening-intent-alongside-implementation/">juxt/allium: A language for sharpening intent alongside implementation.</a>. Könnte auch ganz interessant sein, das Tooling ist weiter ausgebaut, dafür die Sprache schwächer. Allium ist wesentlich formaler und in Richtung Pseudocode.</p>
</div>
</div>
@@ -722,31 +622,22 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>27.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-03-27</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/cloudflare/cobweb">cloudflare/cobweb: COBOL to WebAssembly compiler</a> - ich lass das einfach mal hier liegen. Soll doch jemand anders den alten Kram wegräumen. <a href="https://blog.cloudflare.com/cloudflare-workers-now-support-cobol/">was soll man da auch anderes sagen</a>.</p>
<p><a href="https://github.com/juxt/allium">juxt/allium: A language for sharpening intent alongside implementation.</a> ist ein sehr spannendes Projekt. Und zwar eine Spezifikationssprache für Verhalten von Softwaresystemen. Eine Sprache, für die es keine Laufzeitumgebung oder Compiler gibt, außer der LLM. Implementiert als Agent Skills. Super spannend, weil es unter anderem auch ein Distill gibt, mit dem man bestehende Software analysieren und retroaktiv Spezifikationen aufbauen kann, oder im Interviewverfahren mit der KI eine Spezifikation ausarbeiten kann, die für ein LLM deutlich präziser verständlich ist als allgemeines Englisch. Witziges Detail am Rande: ich habe Allium mit Qwen3-Coder-Next ausprobiert, meinem derzeitigen Lieblingsmodell für lokales Hosting, in <a href="/en/2026/03/14/pi-dev/">pi.dev</a>. Ich konnte das binary für Allium (ein Syntaxchecker und Linter) nicht mit homebrew installieren, pi.dev hat einfach kurzerhand das Binary runtergeladen und selber installiert.</p>
</div>
<div class="post">
<p><a href="https://pyodide.org/en/stable/">Pyodide</a> ist so ein bisschen mein Lebensretter in bDS: ich muss nämlich gestehen, ich bin jetzt nicht wirklich super tief in TypeScript drin und hab auch eigentlich keine Lust das selber zu schreiben. Wenn die KI das macht, ist das ok, da gibt es genug Wissen auf das ein LLM zurückgreifen kann, aber ich will eigentlich nicht mich da tief einarbeiten. Und Python ist schon seit langem eine meiner favorisierten Sprachen. Und Pyodide bietet genau das: eine Portierung von CPython auf WebAssembly. Das bietet eine angenehme Sprache für Scripte und Makros, die aber auf alles zugreifen kann, was die Applikation macht und - wenn ich das mal will - auch Python Libraries ladenm kann.</p>
<p><a href="https://github.com/scitrera/cuda-containers">scitrera/cuda-containers: Scitrera builds of various CUDA containers for version consistency, starting primarily with NVIDIA DGX Spark Containers</a> bin ja im Moment ein großer Fan von eugr/vllm-node als Basispaket, weil es immer aktuelle Versionen für vLLM bietet, aber wenn ich mal mit sglang spielen will, ist das hier vermutlich das ähnlichste Projekt. Mich interessiert da speziell EAGLE-3 spekulatives Dekodieren - im Prinzip werden dabei über ein sehr kleines Modell parallel Token generiert und das Hauptmodell guckt was passt und nimmt das, oder generiert selber wenn nötig. Dadurch kann man oft ein drittel der Token über ein viel schnelleres banales Modell im &lt;3B Bereich generieren lassen und nur jedes dritte wirklich über das große Modell.</p>
</div>
<div class="post">
<p><a href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> - war ein Vorschlag der KI beim Bau von bDS und hat sich sehr bewährt. Sauberer ORM für TypeScript mit einer recht netten API, die mich stark an Django erinnert. Zusätzlich saubere Abbildung von Migrations, die dann aber einfach SQL erlauben, also auch komplexere Migrationen ermöglichen. Und bisher völlig unauffällig im Betrieb. Was besonders interessant ist: da gibts eine direkte Übersetzung nach GraphQL, so dass man die objekte dann auch nach außen auf ein API legen kann, was ich mir glaube ich mal angucken sollte (bzw. mich bei der KI beschweren sollte, dass es sich das mal anguckt). Ich bin ja immer ein Fan von flexiblen Standard-Integrationen um externe Tools anzubinden.</p>
</div>
<div class="post">
<p><a href="https://a2ui.org/">A2UI</a> ist ein interessantes Projekt für ein streaming-orientiertes Protokoll für Anwendungen mit LLM Integration. Die Grundidee ist Streaming von JSON Schnipseln aus denen sich dann inkrementell das UI zusammenbaut, und das LLM hat die Kontrolle darüber, was in das UI rein wandert. Es erlaubt dem LLM mehr als nur simple textuelle Beschreibungen oder einfache Ascii-Grafiken zu produzieren und gibt auch Rückfragen des LLM eine andere Optik. Das ganze kommt von Google und wird auf einem offenen <a href="https://github.com/google/A2UI">github Projekt</a> gepflegt. In bDS habe ich das ganze auch eingebaut und das hat schon was, wenn man über seine Blogposts eine Heatmap der Verteilung auf die Monate bekommen kann.</p>
<p><a href="https://github.com/thushan/olla">thushan/olla: High-performance lightweight proxy and load balancer for LLM infrastructure. Intelligent routing, automatic failover and unified model discovery across local and remote inference backend</a> ist nach dem Debakel von LiteLLM (gehackte supply-chain mit Datenextraktor im Paket) vielleicht die bessere Wahl. Für mich auf jeden Fall interessant, weil ich einfach nur zwei Modelle betreiben will und diese unter einem Endpunkt verfügbar machen will, und da sind alle anderen Pakete deutlicher Overkill.</p>
</div>
</div>
@@ -759,13 +650,12 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>23.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-03-25</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/158572/waggle-dance">Waggle Dance</a> . oldie, but goldie. Immer wieder spaßig wie dieses doch recht einfache Spiel die Leute mitnehmen kann, auch über 10 Jahren nach seiner Veröffentlichung.</p>
<p><a href="https://forums.developer.nvidia.com/t/running-mistral-small-4-119b-nvfp4-on-nvidia-dgx-spark-gb10/363863/26">Running Mistral Small 4 119B NVFP4 on NVIDIA DGX Spark (GB10) - DGX Spark / GB10 User Forum / DGX Spark / GB10 - NVIDIA Developer Forums</a> - live-saver Diskussion in den NVIDIA Foren. Mit dem was da steht habe ich Mistral Small 4 zum Fliegen bekommen. Und es läuft sauber mit 150K Context und 100 Token/Sekunde in der Generierung. Wow. Das ist das erste Mal, dass ich die Power der Kiste wirklich bemerke.</p>
</div>
</div>
@@ -778,48 +668,32 @@
<section class="archive-day-group">
<aside class="archive-day-marker"><span>22.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-03-22</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://opencode.ai/de">OpenCode | Der Open-Source AI-Coding-Agent</a> - ein open-source coding agent der so langsam Claude Code den Rang ablaufen könnte. Sehr gut in der Ausführung und vor allem Provider-agnostisch. Man kann jedes Modell anhängen, sogar ein selber lokal gehostetes mit LM Studio oder ollama. Wenn man einfach mal spielen will, kann man es auch einfach nur runterladen und loslegen, sogar ohne eine Kreditkarte zu hinterlegen (dann natürlich mit limitierten Tokens, aber für erste Experimente durchaus nutzbar. Und unabhängig von den großen Anbietern (ok, außer deren Modelle, wenn man die benutzen will - und für ernsthaftes Coding sind die leider noch notwendig). OpenCode bietet auch selber AI Modelle an, die dann abgerechnet werden, aber ein paar Modelle sind immer frei verfügbar und bieten damit durchaus ernsthaftes Experimentieren ohne Investment.</p>
<p><a href="https://zed.dev/blog/fastest-ai-code-editor">Zed: The Fastest AI Code Editor — Zed's Blog</a> ist ein ziemlich cooler Editor, der endlich auch mit einer guten VIM Implementation daher kommt. Unterstützt Coding Agents und ist offen für Open Weight und home-hosted Modelle. Gefällt mir ehrlich gesagt sehr gut und ist wirklich schnell. Und ist in Rust geschrieben, was mich nochmal mehr begeistert.</p>
</div>
<div class="post">
<h2 class="post-title"><a href="/2026/02/22/steve-yegge-on-vibe-coding">Steve Yegge on Vibe Coding</a></h2>
<p>Steve Yegge spricht im Interview über die Zukunft der Programmierung und die Herausforderungen des Vibe Codings. Der Autor teilt seine Erfahrungen und sieht einen Umbruch vergleichbar mit der Einführung von Hochsprachen.</p>
<p><a href="https://mistral.ai/news/mistral-small-4">Introducing Mistral Small 4 | Mistral AI</a> ist ein weiterer interessanter Kandidat für die <a href="/en/2026/03/22/asus-ascent-gx10-asus-deutschland/">ASUS Ascent GX10ASUS Deutschland</a>, vor allem brauche ich dort keine Side-car Modelle für Vision, weil das Modell selber auch schon gleich Vision mitbring. Und als MoE Modell dürfte es auch gut Speed hinlegen.</p>
</div>
<div class="post">
<p><a href="https://huggingface.co/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4">nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 · Hugging Face</a> wird wahrscheinlich das erste große Modell auf der <a href="/en/2026/03/22/asus-ascent-gx10-asus-deutschland/">ASUS Ascent GX10ASUS Deutschland</a> werden, weil es optimiert auf NVFP4 trainiert wurde - und damit nicht eine &quot;verdummung&quot; erfährt, wegen der Quantisierung, sondern ganz normal wie erwartet funktioniert. Und es ist auf agentische Workflows optimiert, was OpenClaw begünstigen sollte, genauso wie der 1M Context, der in dem Modell vermutlich sogar genutzt werden kann (andere Architektur als klassische Transformer basierte Modelle).</p>
</div>
<div class="post">
<h2 class="post-title"><a href="/2026/02/22/neue-software-langsam-benutzbar">bDS langsam benutzbar</a></h2>
<p><a href="https://tailscale.com/wireguard-vpn">WireGuard® for Enterprise</a> klingt komisch auf einem rein privaten Blog mit dem Namen, aber die haben recht günstige Tarife für VPN-ähnliche Konstrukte, mit denen man sehr leicht seine lokalen Geräte zu Hause auch von Unterwegs erreichbar machen kann. Gibt sicherlich auch andere Alternativen, aber ich glaube wenn ich meinen Agent lokal laufen lasse, stecke ich das Geld für Subscriptions lieber in einen ordentlichen VPN Dienst, da habe ich generell mehr von. Update: ist kostenlos für Privatbenutzer bis 3 User. Nett.</p>
</div>
<p>So langsam macht die neue Software Spaß, alles was man für die Benutzung und Erstellung braucht ist weitestgehend vorhanden. Ein paar kleine Schönheitsfehler hat sie moch und es gibt noch das eine oder andere Feature, das ich will, aber ich kann schon alles machen, was ich wirklich als Minimum brauche. Veröffentlichen geht jetzt einfach mit einem shell script:</p>
<pre><code class="language-bash" data-code-language="bash">#!/bin/sh
cd ~/Blogs/rfc1437.de/html
rsync -rav --delete \
--exclude=&#39;thumbnails/&#39; \
--exclude=&#39;media/&#39; \
* git.rfc1437.de:git-server/html/
cd ..
rsync -rav --delete thumbnails/ git.rfc1437.de:git-server/html/thumbnails/
rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/html/media/
</code></pre>
<p>Ja, das war jetzt nur um meine Source-Formatierung zu testen. Und?</p>
<div class="post">
<p><a href="https://www.asus.com/de/networking-iot-servers/desktop-ai-supercomputer/ultra-small-ai-supercomputers/asus-ascent-gx10/">ASUS Ascent GX10ASUS Deutschland</a> kommt in den nächsten Tagen. KI Bolide, der es mir erlauben wird, auch größere Modelle lokal zu betreiben und z.B. einen OpenClaw Agent autonom zu Hause zu betreiben, ohne dafür dann auch noch Subscriptions zu brauchen. Bin schon echt gespannt, was damit geht. Kommt mit angepasstem Ubuntu, auch ein nettes Detail.</p>
</div>
</div>
@@ -832,13 +706,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>16.02.2026</span></aside>
<aside class="archive-day-marker"><span>2026-03-19</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://git.rfc1437.de/hugo/rfc1437">Big Damn Stupid</a> - ne, Blogging Desktop Server. Im Moment mein Lieblingsprojekt an dem ich mit Vibe-Coding herumspiele und mir eine Software baue, mit der ich das Blog betreiben kann. Aber diesmal mit der klaren Perspektive, dass ich nicht wieder dem Bitrot oder der Komplexitätsspirale erliege. Simple Software mit brauchbarer Oberfläche für die Pflege der Blogbeiträge, aber die Speicherung als Markdown Files mit YAML Frontmatter um einfach stabil für die Zukunft zu sein. Das ganze dann mit sqlite für Caching und Volltextsuche und andere Komfort-Features und git für die Synchronisation der Blog-Daten, und git-lfs für die Bilder. Fühlt sich gerade richtig brauchbar an.</p>
<p><a href="https://github.com/topoteretes/cognee">topoteretes/cognee: Knowledge Engine for AI Agent Memory in 6 lines of code</a> ist auch etwas, das ich mir für später merke. Im Prinzip ein Wissens-Graph der über eine LLM gesteuert aufgebaut wird, um Memory für eine andere LLM verfügbar zu machen. Sicherlich spannend damit zu spielen, wenn ich mal eine gute lokale Hardware habe, auf der ich größere Modelle betreiben kann. Aber für jetzt nur ein Merker.</p>
</div>
<div class="post">
<p><a href="https://www.docker.com/blog/docker-model-runner-vllm-metal-macos/">Docker Model Runner Adds vLLM Support on macOS | Docker</a> - erstmal nur gemerkt, das könnte später interessant werden, weil ich darüber Modelle über Docker mit vllm betreiben kann, bei gleichzeitiger Nutzung von Apple Silicon. Interessant daran ist hier, dass es als Docker Image fertig konfiguriert kommt und ich mich nicht mit Setup rumschlagen muss. Im Moment arbeite ich ja eher mit meinem eigenen <a href="/en/2026/03/18/rfc1437-mlxserver-a-simple-mlx-based-server-for-small-models-to-run-locally/">rfc1437/MLXServer: a simple MLX based server for small models to run locally</a>, einfach weil ich es eh nur für den Offline-Betrieb brauche, aber für später könnte vllm-metal sehr spannend sein.</p>
</div>
</div>
@@ -851,25 +729,17 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>27.08.2023</span></aside>
<aside class="archive-day-marker"><span>2026-03-18</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/300930/schotten-totten-2">Schotten Totten 2</a> entwickelt sich zu einem unserer Lieblings-Spiele für zwei Spieler. Ein Remake des alten Schotten Totten (das es leider nicht mehr zu kaufen gibt unter dem Namen, nur als Battle Line, aber das eben nur auf Englisch). Im Gegensatz zum ersten Spiel jetzt asymmetrisch mit gerade genug Unterschied in der Spielweise, dass sich die beiden Seiten definitiv unterschiedlich anfühlen, aber trotzdem noch größtenteils die gleichen Sachen machen. Schnell aufgebaut, schnell gespielt und schnell weggeräumt mit genug Taktik um einen für ein paar Spiele zu fesseln. Und der Grafik-Stil ist einfach nett.</p>
<p><a href="https://github.com/rfc1437/MLXServer">rfc1437/MLXServer: a simple MLX based server for small models to run locally</a> ist ein Tool, das ich mir (KI-gestützt) gebaut habe, um kleine Modelle direkt lokal auszuführen, ohne großen Ballast. Das frisst nicht viel Speicher, hat gleich einen lokalen Chat eingebaut für eigene Experimente und fühlt sich für mich deutlich praktischer an als die großen Alternativen - weniger Knöpfe zum Einstellen, aber dadurch auch weniger Verwirrung. Ich will ja einfach nur ein kleines Modell lokal betreiben für mein Blog on-the-road.</p>
</div>
<div class="post">
<p><a href="https://boardgamegeek.com/boardgame/197405/tak">Tak</a> ist ein ziemlich interessantes Spiel: <a href="https://en.wikipedia.org/wiki/Tak_(game)">inspiriert aus einem Fantasy-Roman von Patrick Rothfuss</a>, zum Leben erweckt als eine fiktives &quot;klassisches&quot; Strategiespiel. Das schöne daran: man kann es sich problemlos selber machen, wenn man will und geschickt ist. Die Regeln sind super simpel und leicht gelernt, das Spiel aber trickreich mit vielen Möglichkeiten dem Gegner Fallen zu stellen. Wird vermutlich für unseren nächsten Urlaub mitgenommen, weil praktisch zum Draussen spielen.</p>
</div>
<div class="post">
<p>Weil mich Google Authenticator ärgert: <a href="https://www.linux.org/threads/in-depth-tutorial-how-to-set-up-2fa-totp-with-keepassxc-aegis-and-authy.36577/">In-depth tutorial: How to set up 2FA TOTP with KeepassXC, Aegis and Authy. | Linux.org</a>. Keepassxc ist deutlich netter, wie ich finde, und ist deutlich kontrollierbarer für mich.</p>
<p><a href="https://huggingface.co/mlx-community/Qwen3.5-9B-MLX-4bit">mlx-community/Qwen3.5-9B-MLX-4bit · Hugging Face</a> ist noch ein weiteres nettes kleines Modell - größer als die anderen, dadurch aber auch etwas konsistenter in der Ausführung, aber immer noch echt fix. Und ist so die Obergrenze, was man auf einem MacBook Air M4 mit 16G Ram betreiben kann, ohne den Rechner zu crashen.</p>
</div>
</div>
@@ -882,18 +752,12 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>19.08.2023</span></aside>
<aside class="archive-day-marker"><span>2026-03-16</span></aside>
<div class="archive-day-posts">
<div class="post">
<h2 class="post-title"><a href="/2023/08/19/mal-wieder-auf-linux">Mal wieder auf Linux ...</a></h2>
<p>... und natürlich tuts Surround-Sound meines Notebooks nicht mehr, aus Gründen. Und Fingerprint-Login macht weitaus weniger Sinn, wenn man nach Login dann doch das Passwort für den Keyring eingeben muss. Wenn der wenigstens erst beim ersten Bedarf nach einem Passwort käme, dann wärs ja ok, aber der geht direkt nach Login auf. Bah.</p>
<p>Ansonsten bin ich allerdings angenehm überrascht davon, wie gut die Linux-Unterstützung für das Lenovo T480 ist. Alles andere funktioniert bisher tadellos.</p>
<p><a href="https://huggingface.co/google/gemma-3-4b-it">google/gemma-3-4b-it · Hugging Face</a> ist ein ziemlich nettes Modell, dass für viele europäische Sprachen trainiert ist und daher gut für Übersetzungen lokal benutzt werden kann - es lädt unter 4G in den Speicher und belegt im Betrieb in der Interferenz ca. 6.5G. Und es hat Vision Capability, kann also auch benutzt werden um Bildbeschreibungen zu bekommen. Ideal, um z.B. mit bDS als lokales Modell benutzt zu werden wenn man offline unterwegs sein will. Und deutlich kleiner als <a href="/en/2026/03/02/mlx-community-gemma-3-12b-it-4bit-hugging-face/">mlx-community/gemma-3-12b-it-4bit · Hugging Face</a> - das war bei meinem Macbook Air grenzwertig.</p>
</div>
</div>
@@ -906,17 +770,150 @@ rsync -rav --exclude=&#39;*.meta&#39; --delete media/ git.rfc1437.de:git-server/
<section class="archive-day-group">
<aside class="archive-day-marker"><span>11.11.2022</span></aside>
<aside class="archive-day-marker"><span>2026-03-15</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://inferencer.com/">Inferencer | Run and Deeply Control Local AI Models</a> ist ein interessantes Tool, mit dem man LLMs lokal betreiben kann. Das kann natürlich LM Studio oder <a href="/en/2026/03/01/ollama/">Ollama</a> oder <a href="/en/2026/03/02/waybarrios-vllm-mlx-openai-and-anthropic-compatible-server-for-apple-silicon/">vllm-mlx</a> auch. Aber Inferencer hat ein Feature namens &quot;Model streaming&quot;, das ziemlich cool ist: es kann Modelle betreiben, die eigentlich zu groß für den Speicher sind. Natürlich tauscht man da Zeit gegen Speicher, aber für ein lokales Modell zur Bildbetitelung oder ähnliche kleinere Sachen kann man das durchaus mal benutzen. Ich habe aber das Gefühl, dass das Modell dadurch irgendwie fragiler wird - z.B. nutzt es Tools plötzlich nicht mehr richtig (ich habs mit gemma3 12b probiert, das so gerade an der Speichergrenze meines Laptops kratzt).</p>
</div>
<div class="post">
<p><a href="https://pagefind.app/">Pagefind | Pagefind — Static low-bandwidth search at scale</a> ist eine statische Suchmaschine für statisch generiertes HTML, so wie mein Blog. Und wird demnächst die Suche auf diesem Blog betreiben. Keine externen abhängigkeiten, kein Server, keine Komplexität der Infrastruktur - einfach nur ein paar zusätzliche Files, die mit hochgeladen werden. Und natürlich aktives JavaScript im Browser.</p>
</div>
</div>
</section>
<h2 class="post-title"><a href="/2022/11/11/autumn-leaves">Herbstblätter</a></h2>
<p><img src="/media/2022/11/65232be1-264f-428e-93cb-1fd3788b2f47.jpg" alt="Herbstblatt gegen unscharfen Hintergrund mit anderen Blättern und Himmel"></p>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-14</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://shittycodingagent.ai/">pi.dev</a> ist ein minimalistischer Harness für agentic coding, dessen Fokus nicht auf Features, sondern auf Erweiterbarkeit liegt. Die Grundidee ist solide:. eine sehr einfache Harness die durch Typescript-Plugins erweitert werden kann, so dass sich die Harness an den eigenen Workflow anpassen und an die eigenen Anforderungen. Werde ich mir vielleicht demnächst mal angucken.</p>
</div>
</div>
</section>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-13</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/steveyegge/beads">steveyegge/beads: Beads - A memory upgrade for your coding agent</a> ist ein Todo-Listen-Tool für Agenten. Im Prinzip ein Gedächtnis für Projekte über das sich Agenten steuern können (hinterlegen von Tasks und Features) und auch komplexere Abläufe gesteuert werden können, bei denen ein Agent über eine ganze Reihe von Issues laufen muss und diese lösen muss, wo Tasks abhängigkeiten haben können und Agenten nur den Satz an Aufgaben sehen, die auch wirklich gerade angegangen werden können. Interessant für Projekte, in denen man Agenten in Loops laufen lassen will, um größere Aufgaben zu lösen.</p>
</div>
<div class="post">
<p><a href="https://github.com/dolthub/doltgresql">dolthub/doltgresql: DoltgreSQL - Version Controlled PostgreSQL</a> ist der PostgreSQL-flavored Partner von <a href="/en/2026/03/13/dolthub-dolt-dolt-git-for-data/">dolthub/dolt: Dolt Git for Data</a> und bietet die gleichen Features, nur mit Anlehnung an PostgreSQL und einem Binär-Interface das zu PostgreSQL kompatibel ist.</p>
</div>
<div class="post">
<p><a href="https://github.com/dolthub/dolt?tab=readme-ov-file">dolthub/dolt: Dolt Git for Data</a> ist eine SQL Datenbank, die intern mit Mechanismen wie git arbeitet und dadurch Daten-Branches und Merges unterstützt und allgemein die Möglichkeit bietet, dass Daten in der Datenbank versioniert und mit History funktionieren. Und das ganze auch analog zu git erlaubt die Datenchanges über die Versionsverwaltung zu verteilen. Im Prinzip &quot;slow transaction shipping&quot;.</p>
</div>
<div class="post">
<p><a href="https://github.com/paperclipai/paperclip">paperclipai/paperclip: Open-source orchestration for zero-human companies</a> ist ein Ansatz um Agenten zu orchestrieren und zu verwalten. Das interessante hier ist, dass es mit einer Organisation angelehnt an eine Firmenorganisation arbeitet und mit Mitteln die Kommunikation der Agenten abbildet, die eine gute Transparenz bieten könnte. Ich habe es noch nicht ausprobiert, aber neben <a href="https://github.com/steveyegge/gastown">gastown</a> eines der interessanteren Projekte zu diesem Thema. Das ganze Umfeld von Agent-Orchestrierung ist alles noch recht neu, von daher ist das noch ein großes Feld an Experimenten, aber spannend zu beobachten.</p>
</div>
<div class="post">
<p><a href="https://ghostty.org/">Ghostty</a> ist die Basis, auf der <a href="/en/2026/03/13/cmux-das-terminal-fur-multitasking/">cmux — Das Terminal für Multitasking</a> aufgebaut wurde. Generell ebenfalls ein sehr nettes Terminal, das auch deutlich schneller reagiert als das Standardterminal und auch schon sehr gut funktioniert. Was mir nicht gefiel, war dass Tabs nicht automatisch in den passenden Pfaden wieder neu geöffnet wurden, wenn das Programm beendet wurde. Ich habe einfach zu lange persistente Sessions, die ich immer wieder aufgreife. Das macht cmux einfach besser.</p>
</div>
<div class="post">
<p><a href="https://www.cmux.dev/de">cmux — Das Terminal für Multitasking</a> ist ein ziemlich geniales Terminal-Programm für die gerade durch Agentic Coding wieder verstärkt kommenden CLI Workflows. Und was mich begeistert: es hat eine saubere Persistierung von offenen Workspaces in denen man dann mehrere Tabs haben kann, so dass ich meine Arbeitsumgebung nicht ständig offen lassen muss. Ich habe immer gerne diverse Verzeichnisse direkt offen, weil ich zwischen mehreren hin und her wechsel wärend der Programmierung, das läuft mit CMUX deutlich besser als mit allen anderen.</p>
</div>
</div>
</section>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-12</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://www.nuget.org/packages/Photino.Blazor.CustomWindow">NuGet Gallery | Photino.Blazor.CustomWindow 1.3.1</a> ist eine Library für Photino Blazor, mit der man Fenster Chrome-less machen kann. Also Titelbalken und Standarddekorationen entfernen kann. Idee dahinter ist darüber mehr Kontrolle über das Look-and-Feel zu bekommen und kompaktere UIs zu schaffen. Mit dieser Library bekommt man dann Grundfunktionen zurück wie Fenstergrößenänderungen und andere Standardelemente, die Benutzer erwarten, aber unter voller Kontrolle der Anwendung.</p>
</div>
</div>
</section>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-07</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://velvetshark.com/openclaw-memory-masterclass">OpenClaw Memory Masterclass: The complete guide to agent memory that survives • VelvetShark</a> - interessante Zusammenstellung des Memory-Systems und der Fallstricke mit Compaction bei Openclaw. Der Agent ist ja dazu da, über lange Zeit zu laufen, dadurch ist aber auch immer die Gefahr, dass Compaction gerade in komplexeren Situationen mitten drin zuschlägt. Und openclaw läuft autonom, also will man sicher sein, dass es auch kontinuierlich weitergeht.</p>
</div>
</div>
</section>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-05</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/unum-cloud/USearch">unum-cloud/USearch</a> - drin was drauf steht. Also eine Library, die einen Index für Vektoren bietet, die z.B. aus Embedding stammen können und so semantisch ähnliche Texte finden können. Nicht Text-ähnlich, sondern semantisch, also Inhalt. Spannendes Thema, die dafür nötigen Modelle sind mit den LLMs verwandt, aber eben nicht large, sondern small - sie brauchen nicht voll zu verstehen und zu generieren, weil sie nur Vektoren erstellen, die dann gegeneinander verglichen werden können und je höher die Ähnlichkeit, desto höher die Ähnlichkeit der Texte im Thema. Cooles kleines Feature für bDS.</p>
</div>
</div>
</section>
<div class="archive-day-separator" aria-hidden="true"></div>
<section class="archive-day-group">
<aside class="archive-day-marker"><span>2026-03-02</span></aside>
<div class="archive-day-posts">
<div class="post">
<p><a href="https://github.com/waybarrios/vllm-mlx">waybarrios/vllm-mlx: OpenAI and Anthropic compatible server for Apple Silicon.</a> Den benutze ich, um <a href="/en/2026/03/02/mlx-community-gemma-3-12b-it-4bit-hugging-face/">mlx-community/gemma-3-12b-it-4bit</a> auf meinem MacBook Air zu betreiben. Klappt sehr gut, kleines Shell-Script zum Starten des Servers und dann bin ich autonom. Nicht so komfortabel wie Ollama, aber dafür unterstützt es perfekt Apple's MLX und nutzt damit Silicon gut aus.</p>
</div>
</div>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
#!/bin/sh
set -eu
# Run from the RuDS repository root. Optional arguments override the generated
# bDS2 site and bDS2 checkout paths.
source_dir=${1:-"$HOME/Blogs/rfc1437.de/html"}
bds2_dir=${2:-"$(dirname "$PWD")/bDS2"}
target_dir="fixtures/golden-generated-sites/rfc1437-sample"
for relative_path in \
2005/11/13/esmeralda/index.html \
2026/03/13/cmux-das-terminal-fur-multitasking/index.html \
2026/03/13/ghostty/index.html \
atom.xml \
calendar.json \
category/article/index.html \
en/2005/11/13/esmeralda/index.html \
en/2026/03/13/cmux-das-terminal-fur-multitasking/index.html \
en/2026/03/13/ghostty/index.html \
en/index.html \
index.html \
rss.xml \
sitemap.xml
do
cp "$source_dir/$relative_path" "$target_dir/$relative_path"
perl -pi -e 's/[ \t]+$//' "$target_dir/$relative_path"
done
cp "$bds2_dir/priv/preview_assets/assets/bds.css" "$target_dir/assets/bds.css"
cp "$bds2_dir/priv/preview_assets/assets/search-runtime.js" "$target_dir/assets/search-runtime.js"

View File

@@ -10,7 +10,7 @@ render-tagCloud-empty = Keine Tags gefunden.
render-tagCloud-ariaLabel = Tag-Wolke
render-calendar-open = Kalender öffnen
render-calendar-close = Kalender schließen
render-calendar-title = Archivkalender
render-calendar-title = Archiv
render-calendar-loading = Kalender wird geladen …
render-calendar-error = Kalenderdaten konnten nicht geladen werden.
render-taxonomy-ariaLabel = Taxonomie
@@ -33,3 +33,4 @@ render-month-11 = Nov.
render-month-12 = Dezember
render-search-placeholder = Suchen...
render-search-ariaLabel = Seitensuche
render-search-noResults = Keine Ergebnisse gefunden

View File

@@ -33,3 +33,4 @@ render-month-11 = November
render-month-12 = December
render-search-placeholder = Search...
render-search-ariaLabel = Site search
render-search-noResults = No results found

View File

@@ -10,7 +10,7 @@ render-tagCloud-empty = No se encontraron etiquetas.
render-tagCloud-ariaLabel = Nube de etiquetas
render-calendar-open = Abrir calendario
render-calendar-close = Cerrar calendario
render-calendar-title = Calendario de archivo
render-calendar-title = Archivo
render-calendar-loading = Cargando calendario…
render-calendar-error = No se pudieron cargar los datos del calendario.
render-taxonomy-ariaLabel = Taxonomía
@@ -33,3 +33,4 @@ render-month-11 = noviembre
render-month-12 = diciembre
render-search-placeholder = Buscar...
render-search-ariaLabel = Buscar en el sitio
render-search-noResults = No se encontraron resultados

View File

@@ -10,7 +10,7 @@ render-tagCloud-empty = Aucun tag trouvé.
render-tagCloud-ariaLabel = Nuage de tags
render-calendar-open = Ouvrir le calendrier
render-calendar-close = Fermer le calendrier
render-calendar-title = Calendrier des archives
render-calendar-title = Archives
render-calendar-loading = Chargement du calendrier…
render-calendar-error = Impossible de charger les données du calendrier.
render-taxonomy-ariaLabel = Taxonomie
@@ -33,3 +33,4 @@ render-month-11 = novembre
render-month-12 = décembre
render-search-placeholder = Rechercher...
render-search-ariaLabel = Recherche du site
render-search-noResults = Aucun résultat trouvé

View File

@@ -10,7 +10,7 @@ render-tagCloud-empty = Nessun tag trovato.
render-tagCloud-ariaLabel = Nuvola di tag
render-calendar-open = Apri calendario
render-calendar-close = Chiudi calendario
render-calendar-title = Calendario archivio
render-calendar-title = Archivio
render-calendar-loading = Caricamento calendario…
render-calendar-error = Impossibile caricare i dati del calendario.
render-taxonomy-ariaLabel = Tassonomia
@@ -33,3 +33,4 @@ render-month-11 = novembre
render-month-12 = dicembre
render-search-placeholder = Cerca...
render-search-ariaLabel = Ricerca nel sito
render-search-noResults = Nessun risultato trovato

View File

@@ -108,20 +108,30 @@ invariant IncrementalByContentHash {
}
invariant MultiLanguageRoutes {
-- Main language: flat routes (/{yyyy}/{mm}/{dd}/{slug})
-- Main language: unprefixed routes (/{yyyy}/{mm}/{dd}/{slug})
-- Additional languages: prefixed (/{lang}/{yyyy}/{mm}/{dd}/{slug})
-- Each language subtree gets its own feeds and archives
-- Route dates and archive/calendar membership use created_at in local time,
-- matching bDS2.
-- Posts in the page category additionally render at /{slug} in every tree.
}
invariant LanguageVariantSelection {
-- Every language tree renders each post in its own language when a
-- matching variant exists, falling back to the post's original language.
-- This applies uniformly to single pages, list pages (home/pagination,
-- category/tag/date archives), and feeds:
-- matching variant exists. The main tree prefers the main-language
-- translation and otherwise falls back to the original post. An additional
-- tree uses the original post when its source language matches that tree,
-- otherwise its matching translation, falling back to the original post.
-- This applies uniformly to single pages and list pages (home/pagination,
-- category/tag/date archives):
-- Main tree: a post whose language differs from main_language renders
-- its main_language translation when one exists (canonical variant).
-- Additional-language trees: posts resolve to that language's
-- translation; do_not_translate posts are excluded from those trees.
-- Feeds are deliberately narrower: the main feed contains original
-- published records, while an additional-language feed contains only
-- originals or translations whose language matches that tree. A
-- foreign-language HTML fallback is therefore not added to that feed.
}
invariant CanonicalBaseUrlConfigured {
@@ -146,20 +156,26 @@ rule GenerateCoreSectionPages {
requires: core in generation.sections
ensures: FileGenerated("index.html")
ensures: FileGenerated("sitemap.xml")
-- Multi-language sitemap with hreflang alternates
-- Single root sitemap with hreflang alternates; no per-language sitemap
ensures: FileGenerated("rss.xml")
-- RSS 2.0 feed (same output name as the old bDS app; templates link /rss.xml)
-- bDS2-compatible minimal feed over all published snapshots
ensures: FileGenerated("atom.xml")
-- Atom feed
ensures: FileGenerated("calendar.json")
-- Post dates for calendar widget
-- created_at counts for all published snapshots, including list-hidden posts
ensures: FileGenerated("404.html")
-- Not-found page rendered from the not-found template
for p in Posts where status = published and p.categories.contains("page"):
ensures: FileGenerated(format("{slug}/index.html", slug: p.slug))
for lang in generation.blog_languages - {generation.language}:
ensures: FileGenerated(format("{lang}/index.html", lang: lang))
ensures: FileGenerated(format("{lang}/rss.xml", lang: lang))
ensures: FileGenerated(format("{lang}/atom.xml", lang: lang))
ensures: FileGenerated(format("{lang}/404.html", lang: lang))
for p in Posts where status = published and
p.categories.contains("page") and not p.do_not_translate:
ensures: FileGenerated(format("{lang}/{slug}/index.html",
lang: lang, slug: p.slug))
}
-- Single section: one HTML page per published post
@@ -171,7 +187,7 @@ rule GenerateSinglePostPages {
let url = post_canonical_url(p)
ensures: FileGenerated(format("{url}/index.html", url: url))
for lang in generation.blog_languages - {generation.language}:
if p.translations.any(t => t.language.code = lang):
if not p.do_not_translate:
ensures: FileGenerated(format("{lang}/{url}/index.html",
lang: lang, url: url))
}

View File

@@ -151,6 +151,11 @@ value RenderLabels {
-- and macro fallback labels. Month names resolved 1..12 per language.
}
invariant CompleteStarterNavigation {
-- Starter rendering preserves recursive menu children and the archive
-- calendar entry, and provides localized search empty-result text.
}
invariant LabelsUseContentLanguage {
-- RenderLabels and the i18n filter resolve against the content/render
-- language, consistent with i18n.allium's split-localization rule.