feat: more on M4, but still broken
This commit is contained in:
18
Cargo.lock
generated
18
Cargo.lock
generated
@@ -520,7 +520,7 @@ dependencies = [
|
|||||||
"keyring",
|
"keyring",
|
||||||
"liquid",
|
"liquid",
|
||||||
"liquid-core",
|
"liquid-core",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark 0.13.3",
|
||||||
"refinery",
|
"refinery",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
@@ -557,6 +557,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"iced",
|
"iced",
|
||||||
|
"iced_widget",
|
||||||
"muda",
|
"muda",
|
||||||
"objc2 0.6.4",
|
"objc2 0.6.4",
|
||||||
"objc2-app-kit 0.3.2",
|
"objc2-app-kit 0.3.2",
|
||||||
@@ -2536,9 +2537,11 @@ dependencies = [
|
|||||||
"iced_runtime",
|
"iced_runtime",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"pulldown-cmark 0.11.3",
|
||||||
"rustc-hash 2.1.2",
|
"rustc-hash 2.1.2",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4417,6 +4420,19 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"getopts",
|
||||||
|
"memchr",
|
||||||
|
"pulldown-cmark-escape",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
version = "0.13.3"
|
version = "0.13.3"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ license.workspace = true
|
|||||||
bds-core = { workspace = true }
|
bds-core = { workspace = true }
|
||||||
bds-editor = { workspace = true }
|
bds-editor = { workspace = true }
|
||||||
iced = { workspace = true }
|
iced = { workspace = true }
|
||||||
|
iced_widget = { version = "0.13.4", features = ["markdown"] }
|
||||||
muda = { workspace = true }
|
muda = { workspace = true }
|
||||||
rfd = { workspace = true }
|
rfd = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -275,6 +275,12 @@ fn load_generation_post_body(data_dir: &Path, post: &Post) -> Result<String, Str
|
|||||||
Ok(body)
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_post_editor_mode(settings_state: Option<&SettingsViewState>) -> &str {
|
||||||
|
settings_state
|
||||||
|
.map(|state| state.default_mode.as_str())
|
||||||
|
.unwrap_or("markdown")
|
||||||
|
}
|
||||||
|
|
||||||
fn save_template_editor_state_impl(
|
fn save_template_editor_state_impl(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
project_id: &str,
|
project_id: &str,
|
||||||
@@ -460,7 +466,7 @@ mod tests {
|
|||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
||||||
let mut editor = PostEditorState::from_post(&editor_post, &["en".to_string(), "de".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
let mut editor = PostEditorState::from_post(&editor_post, "markdown", &["en".to_string(), "de".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
||||||
editor.title = "Updated Post".to_string();
|
editor.title = "Updated Post".to_string();
|
||||||
editor.content = "Updated body".to_string();
|
editor.content = "Updated body".to_string();
|
||||||
editor.tags = vec!["rust".to_string(), "lua".to_string()];
|
editor.tags = vec!["rust".to_string(), "lua".to_string()];
|
||||||
@@ -650,7 +656,7 @@ mod tests {
|
|||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
||||||
let mut editor = PostEditorState::from_post(&editor_post, &["en".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
let mut editor = PostEditorState::from_post(&editor_post, "markdown", &["en".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
||||||
editor.title = "Autosaved".to_string();
|
editor.title = "Autosaved".to_string();
|
||||||
editor.mark_dirty();
|
editor.mark_dirty();
|
||||||
editor.last_edit_at_ms = bds_core::util::now_unix_ms() - POST_AUTO_SAVE_DELAY_MS - 100;
|
editor.last_edit_at_ms = bds_core::util::now_unix_ms() - POST_AUTO_SAVE_DELAY_MS - 100;
|
||||||
@@ -690,7 +696,7 @@ mod tests {
|
|||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
let editor_post = bds_core::db::queries::post::get_post_by_id(db.conn(), &created.id).unwrap();
|
||||||
let mut editor = PostEditorState::from_post(&editor_post, &["en".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
let mut editor = PostEditorState::from_post(&editor_post, "markdown", &["en".to_string()], &[], Vec::new(), Vec::new(), Vec::new());
|
||||||
editor.title = "Switched".to_string();
|
editor.title = "Switched".to_string();
|
||||||
editor.mark_dirty();
|
editor.mark_dirty();
|
||||||
|
|
||||||
@@ -1650,8 +1656,8 @@ impl BdsApp {
|
|||||||
// File open handling deferred to later milestones
|
// File open handling deferred to later milestones
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::UrlOpenRequested(_url) => {
|
Message::UrlOpenRequested(url) => {
|
||||||
// URL open handling deferred to later milestones
|
let _ = open::that(url);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2116,6 +2122,9 @@ impl BdsApp {
|
|||||||
PostEditorMsg::AnalyzeTaxonomy => {
|
PostEditorMsg::AnalyzeTaxonomy => {
|
||||||
deferred = DeferredPostAction::AnalyzeTaxonomy(tab_id.clone());
|
deferred = DeferredPostAction::AnalyzeTaxonomy(tab_id.clone());
|
||||||
}
|
}
|
||||||
|
PostEditorMsg::SwitchEditorMode(mode) => {
|
||||||
|
state.set_editor_mode(&mode);
|
||||||
|
}
|
||||||
PostEditorMsg::DetectLanguage => {
|
PostEditorMsg::DetectLanguage => {
|
||||||
deferred = DeferredPostAction::DetectLanguage(tab_id.clone());
|
deferred = DeferredPostAction::DetectLanguage(tab_id.clone());
|
||||||
}
|
}
|
||||||
@@ -2258,6 +2267,7 @@ impl BdsApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(tab) = self.tabs.iter_mut().find(|t| t.id == state.post_id) {
|
if let Some(tab) = self.tabs.iter_mut().find(|t| t.id == state.post_id) {
|
||||||
|
state.refresh_preview_items();
|
||||||
tab.is_dirty = state.is_dirty;
|
tab.is_dirty = state.is_dirty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2628,6 +2638,7 @@ impl BdsApp {
|
|||||||
let linked_media = self.load_post_media_items(&post.id);
|
let linked_media = self.load_post_media_items(&post.id);
|
||||||
let state = PostEditorState::from_post(
|
let state = PostEditorState::from_post(
|
||||||
&post,
|
&post,
|
||||||
|
default_post_editor_mode(self.settings_state.as_ref()),
|
||||||
&self.blog_languages,
|
&self.blog_languages,
|
||||||
&translations,
|
&translations,
|
||||||
outlinks,
|
outlinks,
|
||||||
@@ -5424,6 +5435,7 @@ impl BdsApp {
|
|||||||
post.id.clone(),
|
post.id.clone(),
|
||||||
PostEditorState::from_post(
|
PostEditorState::from_post(
|
||||||
&post,
|
&post,
|
||||||
|
default_post_editor_mode(self.settings_state.as_ref()),
|
||||||
&self.blog_languages,
|
&self.blog_languages,
|
||||||
&translations,
|
&translations,
|
||||||
outlinks,
|
outlinks,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||||||
use iced::widget::text::{Shaping, Wrapping};
|
use iced::widget::text::{Shaping, Wrapping};
|
||||||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Column, Space};
|
use iced::widget::{button, column, container, row, scrollable, text, text_input, Column, Space};
|
||||||
use iced::{Color, Element, Length, Theme};
|
use iced::{Color, Element, Length, Theme};
|
||||||
|
use iced_widget::markdown;
|
||||||
|
|
||||||
use bds_core::i18n::{self, UiLocale};
|
use bds_core::i18n::{self, UiLocale};
|
||||||
use bds_core::model::{Post, PostStatus, PostTranslation};
|
use bds_core::model::{Post, PostStatus, PostTranslation};
|
||||||
@@ -56,6 +57,7 @@ pub struct PostEditorState {
|
|||||||
pub excerpt: String,
|
pub excerpt: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub editor_buffer: RefCell<EditorBuffer>,
|
pub editor_buffer: RefCell<EditorBuffer>,
|
||||||
|
pub preview_items: Vec<markdown::Item>,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub categories: Vec<String>,
|
pub categories: Vec<String>,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
@@ -70,6 +72,7 @@ pub struct PostEditorState {
|
|||||||
pub last_edit_at_ms: i64,
|
pub last_edit_at_ms: i64,
|
||||||
pub metadata_expanded: bool,
|
pub metadata_expanded: bool,
|
||||||
pub excerpt_expanded: bool,
|
pub excerpt_expanded: bool,
|
||||||
|
pub editor_mode: String,
|
||||||
pub tags_input: String,
|
pub tags_input: String,
|
||||||
pub categories_input: String,
|
pub categories_input: String,
|
||||||
// ── Translation flags ──
|
// ── Translation flags ──
|
||||||
@@ -110,6 +113,7 @@ impl Clone for PostEditorState {
|
|||||||
excerpt: self.excerpt.clone(),
|
excerpt: self.excerpt.clone(),
|
||||||
content: self.content.clone(),
|
content: self.content.clone(),
|
||||||
editor_buffer: RefCell::new(EditorBuffer::new(&self.content)),
|
editor_buffer: RefCell::new(EditorBuffer::new(&self.content)),
|
||||||
|
preview_items: self.preview_items.clone(),
|
||||||
tags: self.tags.clone(),
|
tags: self.tags.clone(),
|
||||||
categories: self.categories.clone(),
|
categories: self.categories.clone(),
|
||||||
author: self.author.clone(),
|
author: self.author.clone(),
|
||||||
@@ -124,6 +128,7 @@ impl Clone for PostEditorState {
|
|||||||
last_edit_at_ms: self.last_edit_at_ms,
|
last_edit_at_ms: self.last_edit_at_ms,
|
||||||
metadata_expanded: self.metadata_expanded,
|
metadata_expanded: self.metadata_expanded,
|
||||||
excerpt_expanded: self.excerpt_expanded,
|
excerpt_expanded: self.excerpt_expanded,
|
||||||
|
editor_mode: self.editor_mode.clone(),
|
||||||
tags_input: self.tags_input.clone(),
|
tags_input: self.tags_input.clone(),
|
||||||
categories_input: self.categories_input.clone(),
|
categories_input: self.categories_input.clone(),
|
||||||
active_language: self.active_language.clone(),
|
active_language: self.active_language.clone(),
|
||||||
@@ -141,6 +146,7 @@ impl Clone for PostEditorState {
|
|||||||
impl PostEditorState {
|
impl PostEditorState {
|
||||||
pub fn from_post(
|
pub fn from_post(
|
||||||
post: &Post,
|
post: &Post,
|
||||||
|
default_mode: &str,
|
||||||
blog_languages: &[String],
|
blog_languages: &[String],
|
||||||
translations: &[PostTranslation],
|
translations: &[PostTranslation],
|
||||||
outlinks: Vec<ResolvedPostLink>,
|
outlinks: Vec<ResolvedPostLink>,
|
||||||
@@ -148,8 +154,11 @@ impl PostEditorState {
|
|||||||
linked_media: Vec<LinkedMediaItem>,
|
linked_media: Vec<LinkedMediaItem>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let title = post.title.clone();
|
let title = post.title.clone();
|
||||||
|
let excerpt = post.excerpt.clone().unwrap_or_default();
|
||||||
let content = post.content.clone().unwrap_or_default();
|
let content = post.content.clone().unwrap_or_default();
|
||||||
let canonical_lang = post.language.clone().unwrap_or_else(|| "en".to_string());
|
let canonical_lang = post.language.clone().unwrap_or_else(|| "en".to_string());
|
||||||
|
let preview_items = markdown::parse(&preview_markdown_document_parts(&title, &excerpt, &content))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut translation_drafts = HashMap::new();
|
let mut translation_drafts = HashMap::new();
|
||||||
for tr in translations {
|
for tr in translations {
|
||||||
@@ -168,9 +177,10 @@ impl PostEditorState {
|
|||||||
Self {
|
Self {
|
||||||
post_id: post.id.clone(),
|
post_id: post.id.clone(),
|
||||||
slug: post.slug.clone(),
|
slug: post.slug.clone(),
|
||||||
excerpt: post.excerpt.clone().unwrap_or_default(),
|
excerpt,
|
||||||
content: content.clone(),
|
content: content.clone(),
|
||||||
editor_buffer: RefCell::new(EditorBuffer::new(&content)),
|
editor_buffer: RefCell::new(EditorBuffer::new(&content)),
|
||||||
|
preview_items,
|
||||||
tags: post.tags.clone(),
|
tags: post.tags.clone(),
|
||||||
categories: post.categories.clone(),
|
categories: post.categories.clone(),
|
||||||
author: post.author.clone().unwrap_or_default(),
|
author: post.author.clone().unwrap_or_default(),
|
||||||
@@ -185,6 +195,7 @@ impl PostEditorState {
|
|||||||
last_edit_at_ms: 0,
|
last_edit_at_ms: 0,
|
||||||
metadata_expanded: title.is_empty(),
|
metadata_expanded: title.is_empty(),
|
||||||
excerpt_expanded: false,
|
excerpt_expanded: false,
|
||||||
|
editor_mode: normalize_editor_mode(default_mode),
|
||||||
tags_input: String::new(),
|
tags_input: String::new(),
|
||||||
categories_input: String::new(),
|
categories_input: String::new(),
|
||||||
active_language: canonical_lang.clone(),
|
active_language: canonical_lang.clone(),
|
||||||
@@ -211,9 +222,18 @@ impl PostEditorState {
|
|||||||
buffer.text()
|
buffer.text()
|
||||||
};
|
};
|
||||||
self.content = new_content;
|
self.content = new_content;
|
||||||
|
self.refresh_preview_items();
|
||||||
self.mark_dirty();
|
self.mark_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_editor_mode(&mut self, mode: &str) {
|
||||||
|
self.editor_mode = normalize_editor_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_preview_items(&mut self) {
|
||||||
|
self.preview_items = markdown::parse(&preview_markdown_document(self)).collect();
|
||||||
|
}
|
||||||
|
|
||||||
/// Switch the editor to display a different language.
|
/// Switch the editor to display a different language.
|
||||||
/// Saves current fields and loads the target language's draft.
|
/// Saves current fields and loads the target language's draft.
|
||||||
pub fn switch_language(&mut self, target_lang: &str) {
|
pub fn switch_language(&mut self, target_lang: &str) {
|
||||||
@@ -273,6 +293,7 @@ impl PostEditorState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.active_language = target_lang.to_string();
|
self.active_language = target_lang.to_string();
|
||||||
|
self.refresh_preview_items();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the translation flags list for the view.
|
/// Build the translation flags list for the view.
|
||||||
@@ -327,6 +348,7 @@ pub enum PostEditorMsg {
|
|||||||
DetectLanguage,
|
DetectLanguage,
|
||||||
Translate,
|
Translate,
|
||||||
TranslateTo(String),
|
TranslateTo(String),
|
||||||
|
SwitchEditorMode(String),
|
||||||
TitleChanged(String),
|
TitleChanged(String),
|
||||||
SlugChanged(String),
|
SlugChanged(String),
|
||||||
ExcerptChanged(String),
|
ExcerptChanged(String),
|
||||||
@@ -753,9 +775,16 @@ pub fn view<'a>(
|
|||||||
|
|
||||||
// ── Content section (fills remaining space) ──
|
// ── Content section (fills remaining space) ──
|
||||||
let content_label = inputs::section_header(&t(locale, "editor.content"));
|
let content_label = inputs::section_header(&t(locale, "editor.content"));
|
||||||
|
let mode_toggle = row![
|
||||||
|
mode_button(locale, &state.editor_mode, "markdown", Message::PostEditor(PostEditorMsg::SwitchEditorMode("markdown".to_string()))),
|
||||||
|
mode_button(locale, &state.editor_mode, "preview", Message::PostEditor(PostEditorMsg::SwitchEditorMode("preview".to_string()))),
|
||||||
|
]
|
||||||
|
.spacing(6)
|
||||||
|
.align_y(iced::Alignment::Center);
|
||||||
let body_toolbar = inputs::toolbar(
|
let body_toolbar = inputs::toolbar(
|
||||||
vec![content_label.into()],
|
vec![row![content_label, Space::with_width(Length::Fixed(12.0)), mode_toggle].align_y(iced::Alignment::Center).into()],
|
||||||
vec![
|
vec![
|
||||||
|
if state.editor_mode == "markdown" {
|
||||||
button(
|
button(
|
||||||
text(t(locale, "editor.insertLink"))
|
text(t(locale, "editor.insertLink"))
|
||||||
.size(13)
|
.size(13)
|
||||||
@@ -763,7 +792,11 @@ pub fn view<'a>(
|
|||||||
)
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::InsertLink))
|
.on_press(Message::PostEditor(PostEditorMsg::InsertLink))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into()
|
||||||
|
} else {
|
||||||
|
Space::new(0, 0).into()
|
||||||
|
},
|
||||||
|
if state.editor_mode == "markdown" {
|
||||||
button(
|
button(
|
||||||
text(t(locale, "editor.insertMedia"))
|
text(t(locale, "editor.insertMedia"))
|
||||||
.size(13)
|
.size(13)
|
||||||
@@ -771,7 +804,10 @@ pub fn view<'a>(
|
|||||||
)
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::InsertMedia))
|
.on_press(Message::PostEditor(PostEditorMsg::InsertMedia))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into()
|
||||||
|
} else {
|
||||||
|
Space::new(0, 0).into()
|
||||||
|
},
|
||||||
button(
|
button(
|
||||||
text(t(locale, "editor.gallery"))
|
text(t(locale, "editor.gallery"))
|
||||||
.size(13)
|
.size(13)
|
||||||
@@ -782,7 +818,21 @@ pub fn view<'a>(
|
|||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let editor_widget: Element<'a, Message> =
|
let editor_widget: Element<'a, Message> = if state.editor_mode == "preview" {
|
||||||
|
scrollable(
|
||||||
|
container(
|
||||||
|
markdown::view(
|
||||||
|
&state.preview_items,
|
||||||
|
markdown::Settings::with_text_size(15),
|
||||||
|
markdown::Style::from_palette(Theme::TokyoNightStorm.palette()),
|
||||||
|
)
|
||||||
|
.map(|url: markdown::Url| Message::UrlOpenRequested(url.to_string())),
|
||||||
|
)
|
||||||
|
.padding(16),
|
||||||
|
)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
CodeEditor::new(&state.editor_buffer, highlighter(), "md")
|
CodeEditor::new(&state.editor_buffer, highlighter(), "md")
|
||||||
.word_wrap(word_wrap)
|
.word_wrap(word_wrap)
|
||||||
.on_change(|msg| match msg {
|
.on_change(|msg| match msg {
|
||||||
@@ -791,7 +841,8 @@ pub fn view<'a>(
|
|||||||
}
|
}
|
||||||
EditorMessage::SaveRequested => Message::PostEditor(PostEditorMsg::Save),
|
EditorMessage::SaveRequested => Message::PostEditor(PostEditorMsg::Save),
|
||||||
})
|
})
|
||||||
.into();
|
.into()
|
||||||
|
};
|
||||||
|
|
||||||
// ── Footer ──
|
// ── Footer ──
|
||||||
let published_gap: Element<'a, Message> = if state.published_at.is_some() {
|
let published_gap: Element<'a, Message> = if state.published_at.is_some() {
|
||||||
@@ -898,6 +949,52 @@ fn chip_button_style(_theme: &Theme, status: button::Status) -> button::Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mode_button<'a>(
|
||||||
|
locale: UiLocale,
|
||||||
|
active_mode: &str,
|
||||||
|
mode: &str,
|
||||||
|
message: Message,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let label = match mode {
|
||||||
|
"preview" => t(locale, "editor.modePreview"),
|
||||||
|
_ => t(locale, "editor.modeMarkdown"),
|
||||||
|
};
|
||||||
|
button(text(label).size(12).shaping(Shaping::Advanced))
|
||||||
|
.on_press(message)
|
||||||
|
.padding([4, 10])
|
||||||
|
.style(if active_mode == mode {
|
||||||
|
flag_active_style
|
||||||
|
} else {
|
||||||
|
flag_inactive_style
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preview_markdown_document(state: &PostEditorState) -> String {
|
||||||
|
preview_markdown_document_parts(&state.title, &state.excerpt, &state.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preview_markdown_document_parts(title: &str, excerpt: &str, content: &str) -> String {
|
||||||
|
let mut sections = Vec::new();
|
||||||
|
if !title.trim().is_empty() {
|
||||||
|
sections.push(format!("# {}", title.trim()));
|
||||||
|
}
|
||||||
|
if !excerpt.trim().is_empty() {
|
||||||
|
sections.push(format!("> {}", excerpt.trim()));
|
||||||
|
}
|
||||||
|
if !content.trim().is_empty() {
|
||||||
|
sections.push(content.to_string());
|
||||||
|
}
|
||||||
|
sections.join("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_editor_mode(mode: &str) -> String {
|
||||||
|
match mode {
|
||||||
|
"preview" => "preview".to_string(),
|
||||||
|
_ => "markdown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn status_badge<'a>(status: &PostStatus) -> Element<'a, Message> {
|
fn status_badge<'a>(status: &PostStatus) -> Element<'a, Message> {
|
||||||
let (label, color) = match status {
|
let (label, color) = match status {
|
||||||
PostStatus::Draft => ("Draft", Color::from_rgb(0.8, 0.7, 0.2)),
|
PostStatus::Draft => ("Draft", Color::from_rgb(0.8, 0.7, 0.2)),
|
||||||
@@ -986,6 +1083,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
PostEditorState::from_post(
|
PostEditorState::from_post(
|
||||||
&post,
|
&post,
|
||||||
|
"markdown",
|
||||||
&["en".to_string()],
|
&["en".to_string()],
|
||||||
&[],
|
&[],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@@ -1015,4 +1113,15 @@ mod tests {
|
|||||||
assert_eq!(state.content, "Hello Rust");
|
assert_eq!(state.content, "Hello Rust");
|
||||||
assert!(state.is_dirty);
|
assert!(state.is_dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsupported_default_mode_falls_back_to_markdown() {
|
||||||
|
let mut state = sample_state();
|
||||||
|
|
||||||
|
state.set_editor_mode("wysiwyg");
|
||||||
|
assert_eq!(state.editor_mode, "markdown");
|
||||||
|
|
||||||
|
state.set_editor_mode("preview");
|
||||||
|
assert_eq!(state.editor_mode, "preview");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
"editor.language": "Sprache",
|
"editor.language": "Sprache",
|
||||||
"editor.aiAnalyze": "Mit KI analysieren",
|
"editor.aiAnalyze": "Mit KI analysieren",
|
||||||
"editor.detectLanguage": "Sprache erkennen",
|
"editor.detectLanguage": "Sprache erkennen",
|
||||||
|
"editor.modeMarkdown": "Markdown",
|
||||||
|
"editor.modePreview": "Vorschau",
|
||||||
"editor.suggestTaxonomy": "Taxonomie vorschlagen",
|
"editor.suggestTaxonomy": "Taxonomie vorschlagen",
|
||||||
"editor.translate": "Übersetzen",
|
"editor.translate": "Übersetzen",
|
||||||
"editor.templateSlug": "Vorlage",
|
"editor.templateSlug": "Vorlage",
|
||||||
|
|||||||
@@ -222,6 +222,8 @@
|
|||||||
"editor.language": "Language",
|
"editor.language": "Language",
|
||||||
"editor.aiAnalyze": "Analyze with AI",
|
"editor.aiAnalyze": "Analyze with AI",
|
||||||
"editor.detectLanguage": "Detect Language",
|
"editor.detectLanguage": "Detect Language",
|
||||||
|
"editor.modeMarkdown": "Markdown",
|
||||||
|
"editor.modePreview": "Preview",
|
||||||
"editor.suggestTaxonomy": "Suggest Taxonomy",
|
"editor.suggestTaxonomy": "Suggest Taxonomy",
|
||||||
"editor.translate": "Translate",
|
"editor.translate": "Translate",
|
||||||
"editor.templateSlug": "Template",
|
"editor.templateSlug": "Template",
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
"editor.language": "Idioma",
|
"editor.language": "Idioma",
|
||||||
"editor.aiAnalyze": "Analizar con IA",
|
"editor.aiAnalyze": "Analizar con IA",
|
||||||
"editor.detectLanguage": "Detectar idioma",
|
"editor.detectLanguage": "Detectar idioma",
|
||||||
|
"editor.modeMarkdown": "Markdown",
|
||||||
|
"editor.modePreview": "Vista previa",
|
||||||
"editor.suggestTaxonomy": "Sugerir taxonomía",
|
"editor.suggestTaxonomy": "Sugerir taxonomía",
|
||||||
"editor.translate": "Traducir",
|
"editor.translate": "Traducir",
|
||||||
"editor.templateSlug": "Plantilla",
|
"editor.templateSlug": "Plantilla",
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
"editor.language": "Langue",
|
"editor.language": "Langue",
|
||||||
"editor.aiAnalyze": "Analyser avec l'IA",
|
"editor.aiAnalyze": "Analyser avec l'IA",
|
||||||
"editor.detectLanguage": "Détecter la langue",
|
"editor.detectLanguage": "Détecter la langue",
|
||||||
|
"editor.modeMarkdown": "Markdown",
|
||||||
|
"editor.modePreview": "Aperçu",
|
||||||
"editor.suggestTaxonomy": "Suggérer la taxonomie",
|
"editor.suggestTaxonomy": "Suggérer la taxonomie",
|
||||||
"editor.translate": "Traduire",
|
"editor.translate": "Traduire",
|
||||||
"editor.templateSlug": "Modèle",
|
"editor.templateSlug": "Modèle",
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
"editor.language": "Lingua",
|
"editor.language": "Lingua",
|
||||||
"editor.aiAnalyze": "Analizza con IA",
|
"editor.aiAnalyze": "Analizza con IA",
|
||||||
"editor.detectLanguage": "Rileva lingua",
|
"editor.detectLanguage": "Rileva lingua",
|
||||||
|
"editor.modeMarkdown": "Markdown",
|
||||||
|
"editor.modePreview": "Anteprima",
|
||||||
"editor.suggestTaxonomy": "Suggerisci tassonomia",
|
"editor.suggestTaxonomy": "Suggerisci tassonomia",
|
||||||
"editor.translate": "Traduci",
|
"editor.translate": "Traduci",
|
||||||
"editor.templateSlug": "Modello",
|
"editor.templateSlug": "Modello",
|
||||||
|
|||||||
Reference in New Issue
Block a user