hidden navbar
This commit is contained in:
Generated
+1
-1
@@ -2581,7 +2581,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.94"
|
rust-version = "1.94"
|
||||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE user_editor_preferences ADD COLUMN navbar_collapsed BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE user_editor_preferences ADD COLUMN navbar_collapsed BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE user_editor_preferences ADD COLUMN navbar_collapsed INTEGER NOT NULL DEFAULT 0;
|
||||||
@@ -454,6 +454,7 @@ pub struct NoteInfo {
|
|||||||
preview_line_numbers: bool,
|
preview_line_numbers: bool,
|
||||||
line_links: bool,
|
line_links: bool,
|
||||||
toolbar_collapsed: bool,
|
toolbar_collapsed: bool,
|
||||||
|
navbar_collapsed: bool,
|
||||||
font_family: String,
|
font_family: String,
|
||||||
font_size: i64,
|
font_size: i64,
|
||||||
personal_editor_settings: bool,
|
personal_editor_settings: bool,
|
||||||
@@ -485,6 +486,8 @@ pub struct EditorSettingsRequest {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
toolbar_collapsed: Option<bool>,
|
toolbar_collapsed: Option<bool>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
navbar_collapsed: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
font_family: Option<String>,
|
font_family: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
font_size: Option<i64>,
|
font_size: Option<i64>,
|
||||||
@@ -527,6 +530,7 @@ async fn save_editor_settings(
|
|||||||
|| payload.preview_line_numbers.is_some()
|
|| payload.preview_line_numbers.is_some()
|
||||||
|| payload.line_links.is_some()
|
|| payload.line_links.is_some()
|
||||||
|| payload.toolbar_collapsed.is_some()
|
|| payload.toolbar_collapsed.is_some()
|
||||||
|
|| payload.navbar_collapsed.is_some()
|
||||||
|| payload.font_family.is_some()
|
|| payload.font_family.is_some()
|
||||||
|| payload.font_size.is_some();
|
|| payload.font_size.is_some();
|
||||||
let wants_global_update = payload.authorship_mode.is_some() || payload.colors_enabled.is_some();
|
let wants_global_update = payload.authorship_mode.is_some() || payload.colors_enabled.is_some();
|
||||||
@@ -581,6 +585,9 @@ async fn save_editor_settings(
|
|||||||
if let Some(value) = payload.toolbar_collapsed {
|
if let Some(value) = payload.toolbar_collapsed {
|
||||||
preferences.toolbar_collapsed = value;
|
preferences.toolbar_collapsed = value;
|
||||||
}
|
}
|
||||||
|
if let Some(value) = payload.navbar_collapsed {
|
||||||
|
preferences.navbar_collapsed = value;
|
||||||
|
}
|
||||||
if let Some(value) = payload.font_family {
|
if let Some(value) = payload.font_family {
|
||||||
preferences.font_family = match value.as_str() {
|
preferences.font_family = match value.as_str() {
|
||||||
"mono" | "system" | "serif" | "arial" | "georgia" => value,
|
"mono" | "system" | "serif" | "arial" | "georgia" => value,
|
||||||
@@ -1148,6 +1155,7 @@ pub async fn note_info(
|
|||||||
preview_line_numbers: editor_preferences.preview_line_numbers,
|
preview_line_numbers: editor_preferences.preview_line_numbers,
|
||||||
line_links: editor_preferences.line_links,
|
line_links: editor_preferences.line_links,
|
||||||
toolbar_collapsed: editor_preferences.toolbar_collapsed,
|
toolbar_collapsed: editor_preferences.toolbar_collapsed,
|
||||||
|
navbar_collapsed: editor_preferences.navbar_collapsed,
|
||||||
font_family: editor_preferences.font_family,
|
font_family: editor_preferences.font_family,
|
||||||
font_size: editor_preferences.font_size,
|
font_size: editor_preferences.font_size,
|
||||||
personal_editor_settings,
|
personal_editor_settings,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ pub struct PadInfo {
|
|||||||
preview_line_numbers: bool,
|
preview_line_numbers: bool,
|
||||||
line_links: bool,
|
line_links: bool,
|
||||||
toolbar_collapsed: bool,
|
toolbar_collapsed: bool,
|
||||||
|
navbar_collapsed: bool,
|
||||||
font_family: String,
|
font_family: String,
|
||||||
font_size: i64,
|
font_size: i64,
|
||||||
personal_editor_settings: bool,
|
personal_editor_settings: bool,
|
||||||
@@ -174,6 +175,7 @@ pub async fn pad_info(
|
|||||||
preview_line_numbers: editor_preferences.preview_line_numbers,
|
preview_line_numbers: editor_preferences.preview_line_numbers,
|
||||||
line_links: editor_preferences.line_links,
|
line_links: editor_preferences.line_links,
|
||||||
toolbar_collapsed: editor_preferences.toolbar_collapsed,
|
toolbar_collapsed: editor_preferences.toolbar_collapsed,
|
||||||
|
navbar_collapsed: editor_preferences.navbar_collapsed,
|
||||||
font_family: editor_preferences.font_family,
|
font_family: editor_preferences.font_family,
|
||||||
font_size: editor_preferences.font_size,
|
font_size: editor_preferences.font_size,
|
||||||
personal_editor_settings,
|
personal_editor_settings,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub struct EditorPreferences {
|
|||||||
pub preview_line_numbers: bool,
|
pub preview_line_numbers: bool,
|
||||||
pub line_links: bool,
|
pub line_links: bool,
|
||||||
pub toolbar_collapsed: bool,
|
pub toolbar_collapsed: bool,
|
||||||
|
pub navbar_collapsed: bool,
|
||||||
pub font_family: String,
|
pub font_family: String,
|
||||||
pub font_size: i64,
|
pub font_size: i64,
|
||||||
}
|
}
|
||||||
@@ -28,6 +29,7 @@ impl Default for EditorPreferences {
|
|||||||
preview_line_numbers: false,
|
preview_line_numbers: false,
|
||||||
line_links: false,
|
line_links: false,
|
||||||
toolbar_collapsed: false,
|
toolbar_collapsed: false,
|
||||||
|
navbar_collapsed: false,
|
||||||
font_family: "mono".into(),
|
font_family: "mono".into(),
|
||||||
font_size: 14,
|
font_size: 14,
|
||||||
}
|
}
|
||||||
@@ -95,8 +97,9 @@ pub async fn load_editor_preferences(
|
|||||||
preview_line_numbers: row.try_get::<i64, _>(2)? != 0,
|
preview_line_numbers: row.try_get::<i64, _>(2)? != 0,
|
||||||
line_links: row.try_get::<i64, _>(3)? != 0,
|
line_links: row.try_get::<i64, _>(3)? != 0,
|
||||||
toolbar_collapsed: row.try_get::<i64, _>(4)? != 0,
|
toolbar_collapsed: row.try_get::<i64, _>(4)? != 0,
|
||||||
font_family: crate::row_decode::text(&row, 5)?,
|
navbar_collapsed: row.try_get::<i64, _>(5)? != 0,
|
||||||
font_size: row.try_get(6)?,
|
font_family: crate::row_decode::text(&row, 6)?,
|
||||||
|
font_size: row.try_get(7)?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +124,7 @@ pub async fn save_editor_configuration(
|
|||||||
.bind(preferences.preview_line_numbers)
|
.bind(preferences.preview_line_numbers)
|
||||||
.bind(preferences.line_links)
|
.bind(preferences.line_links)
|
||||||
.bind(preferences.toolbar_collapsed)
|
.bind(preferences.toolbar_collapsed)
|
||||||
|
.bind(preferences.navbar_collapsed)
|
||||||
.bind(&preferences.font_family)
|
.bind(&preferences.font_family)
|
||||||
.bind(preferences.font_size)
|
.bind(preferences.font_size)
|
||||||
.execute(&mut *tx)
|
.execute(&mut *tx)
|
||||||
|
|||||||
@@ -59,16 +59,16 @@ pub fn get(query: Query) -> &'static str {
|
|||||||
r#"DELETE FROM resource_editor_settings WHERE resource_kind = ? AND resource_slug = ?"#
|
r#"DELETE FROM resource_editor_settings WHERE resource_kind = ? AND resource_slug = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
||||||
r#"SELECT CAST(CASE WHEN compact_view THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN editor_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN preview_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN line_links THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(font_family AS CHAR CHARACTER SET utf8mb4), font_size FROM user_editor_preferences WHERE user_id = ? AND pad_id = ?"#
|
r#"SELECT CAST(CASE WHEN compact_view THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN editor_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN preview_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN line_links THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN navbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(font_family AS CHAR CHARACTER SET utf8mb4), font_size FROM user_editor_preferences WHERE user_id = ? AND pad_id = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
||||||
r#"SELECT CAST(CASE WHEN compact_view THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN editor_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN preview_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN line_links THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(font_family AS CHAR CHARACTER SET utf8mb4), font_size FROM user_editor_preferences WHERE user_id = ? AND note_id = ?"#
|
r#"SELECT CAST(CASE WHEN compact_view THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN editor_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN preview_line_numbers THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN line_links THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(CASE WHEN navbar_collapsed THEN 1 ELSE 0 END AS SIGNED), CAST(font_family AS CHAR CHARACTER SET utf8mb4), font_size FROM user_editor_preferences WHERE user_id = ? AND note_id = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE compact_view = VALUES(compact_view), editor_line_numbers = VALUES(editor_line_numbers), preview_line_numbers = VALUES(preview_line_numbers), line_links = VALUES(line_links), toolbar_collapsed = VALUES(toolbar_collapsed), font_family = VALUES(font_family), font_size = VALUES(font_size), updated_at = CURRENT_TIMESTAMP"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE compact_view = VALUES(compact_view), editor_line_numbers = VALUES(editor_line_numbers), preview_line_numbers = VALUES(preview_line_numbers), line_links = VALUES(line_links), toolbar_collapsed = VALUES(toolbar_collapsed), navbar_collapsed = VALUES(navbar_collapsed), font_family = VALUES(font_family), font_size = VALUES(font_size), updated_at = CURRENT_TIMESTAMP"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES (?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE compact_view = VALUES(compact_view), editor_line_numbers = VALUES(editor_line_numbers), preview_line_numbers = VALUES(preview_line_numbers), line_links = VALUES(line_links), toolbar_collapsed = VALUES(toolbar_collapsed), font_family = VALUES(font_family), font_size = VALUES(font_size), updated_at = CURRENT_TIMESTAMP"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES (?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE compact_view = VALUES(compact_view), editor_line_numbers = VALUES(editor_line_numbers), preview_line_numbers = VALUES(preview_line_numbers), line_links = VALUES(line_links), toolbar_collapsed = VALUES(toolbar_collapsed), navbar_collapsed = VALUES(navbar_collapsed), font_family = VALUES(font_family), font_size = VALUES(font_size), updated_at = CURRENT_TIMESTAMP"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
||||||
r#"DELETE FROM user_editor_preferences WHERE user_id = ?"#
|
r#"DELETE FROM user_editor_preferences WHERE user_id = ?"#
|
||||||
|
|||||||
@@ -59,16 +59,16 @@ pub fn get(query: Query) -> &'static str {
|
|||||||
r#"DELETE FROM resource_editor_settings WHERE resource_kind = $1 AND resource_slug = $2"#
|
r#"DELETE FROM resource_editor_settings WHERE resource_kind = $1 AND resource_slug = $2"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
||||||
r#"SELECT (CASE WHEN compact_view THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN editor_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN preview_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN line_links THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END)::BIGINT, font_family, font_size FROM user_editor_preferences WHERE user_id = $1 AND pad_id = $2"#
|
r#"SELECT (CASE WHEN compact_view THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN editor_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN preview_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN line_links THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN navbar_collapsed THEN 1 ELSE 0 END)::BIGINT, font_family, font_size FROM user_editor_preferences WHERE user_id = $1 AND pad_id = $2"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
||||||
r#"SELECT (CASE WHEN compact_view THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN editor_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN preview_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN line_links THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END)::BIGINT, font_family, font_size FROM user_editor_preferences WHERE user_id = $1 AND note_id = $2"#
|
r#"SELECT (CASE WHEN compact_view THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN editor_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN preview_line_numbers THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN line_links THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END)::BIGINT, (CASE WHEN navbar_collapsed THEN 1 ELSE 0 END)::BIGINT, font_family, font_size FROM user_editor_preferences WHERE user_id = $1 AND note_id = $2"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES ($1, $2, NULL, $3, $4, $5, $6, $7, $8, $9, CURRENT_TIMESTAMP::text) ON CONFLICT (user_id, pad_id) DO UPDATE SET compact_view = EXCLUDED.compact_view, editor_line_numbers = EXCLUDED.editor_line_numbers, preview_line_numbers = EXCLUDED.preview_line_numbers, line_links = EXCLUDED.line_links, toolbar_collapsed = EXCLUDED.toolbar_collapsed, font_family = EXCLUDED.font_family, font_size = EXCLUDED.font_size, updated_at = CURRENT_TIMESTAMP::text"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES ($1, $2, NULL, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP::text) ON CONFLICT (user_id, pad_id) DO UPDATE SET compact_view = EXCLUDED.compact_view, editor_line_numbers = EXCLUDED.editor_line_numbers, preview_line_numbers = EXCLUDED.preview_line_numbers, line_links = EXCLUDED.line_links, toolbar_collapsed = EXCLUDED.toolbar_collapsed, navbar_collapsed = EXCLUDED.navbar_collapsed, font_family = EXCLUDED.font_family, font_size = EXCLUDED.font_size, updated_at = CURRENT_TIMESTAMP::text"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES ($1, NULL, $2, $3, $4, $5, $6, $7, $8, $9, CURRENT_TIMESTAMP::text) ON CONFLICT (user_id, note_id) DO UPDATE SET compact_view = EXCLUDED.compact_view, editor_line_numbers = EXCLUDED.editor_line_numbers, preview_line_numbers = EXCLUDED.preview_line_numbers, line_links = EXCLUDED.line_links, toolbar_collapsed = EXCLUDED.toolbar_collapsed, font_family = EXCLUDED.font_family, font_size = EXCLUDED.font_size, updated_at = CURRENT_TIMESTAMP::text"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES ($1, NULL, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP::text) ON CONFLICT (user_id, note_id) DO UPDATE SET compact_view = EXCLUDED.compact_view, editor_line_numbers = EXCLUDED.editor_line_numbers, preview_line_numbers = EXCLUDED.preview_line_numbers, line_links = EXCLUDED.line_links, toolbar_collapsed = EXCLUDED.toolbar_collapsed, navbar_collapsed = EXCLUDED.navbar_collapsed, font_family = EXCLUDED.font_family, font_size = EXCLUDED.font_size, updated_at = CURRENT_TIMESTAMP::text"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
||||||
r#"DELETE FROM user_editor_preferences WHERE user_id = $1"#
|
r#"DELETE FROM user_editor_preferences WHERE user_id = $1"#
|
||||||
|
|||||||
@@ -59,16 +59,16 @@ pub fn get(query: Query) -> &'static str {
|
|||||||
r#"DELETE FROM resource_editor_settings WHERE resource_kind = ? AND resource_slug = ?"#
|
r#"DELETE FROM resource_editor_settings WHERE resource_kind = ? AND resource_slug = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
Query::EDITOR_PREFERENCES_SELECT_PAD => {
|
||||||
r#"SELECT CASE WHEN compact_view THEN 1 ELSE 0 END, CASE WHEN editor_line_numbers THEN 1 ELSE 0 END, CASE WHEN preview_line_numbers THEN 1 ELSE 0 END, CASE WHEN line_links THEN 1 ELSE 0 END, CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END, font_family, font_size FROM user_editor_preferences WHERE user_id = ? AND pad_id = ?"#
|
r#"SELECT CASE WHEN compact_view THEN 1 ELSE 0 END, CASE WHEN editor_line_numbers THEN 1 ELSE 0 END, CASE WHEN preview_line_numbers THEN 1 ELSE 0 END, CASE WHEN line_links THEN 1 ELSE 0 END, CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END, CASE WHEN navbar_collapsed THEN 1 ELSE 0 END, font_family, font_size FROM user_editor_preferences WHERE user_id = ? AND pad_id = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
Query::EDITOR_PREFERENCES_SELECT_NOTE => {
|
||||||
r#"SELECT CASE WHEN compact_view THEN 1 ELSE 0 END, CASE WHEN editor_line_numbers THEN 1 ELSE 0 END, CASE WHEN preview_line_numbers THEN 1 ELSE 0 END, CASE WHEN line_links THEN 1 ELSE 0 END, CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END, font_family, font_size FROM user_editor_preferences WHERE user_id = ? AND note_id = ?"#
|
r#"SELECT CASE WHEN compact_view THEN 1 ELSE 0 END, CASE WHEN editor_line_numbers THEN 1 ELSE 0 END, CASE WHEN preview_line_numbers THEN 1 ELSE 0 END, CASE WHEN line_links THEN 1 ELSE 0 END, CASE WHEN toolbar_collapsed THEN 1 ELSE 0 END, CASE WHEN navbar_collapsed THEN 1 ELSE 0 END, font_family, font_size FROM user_editor_preferences WHERE user_id = ? AND note_id = ?"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
Query::EDITOR_PREFERENCES_UPSERT_PAD => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, pad_id) DO UPDATE SET compact_view = excluded.compact_view, editor_line_numbers = excluded.editor_line_numbers, preview_line_numbers = excluded.preview_line_numbers, line_links = excluded.line_links, toolbar_collapsed = excluded.toolbar_collapsed, font_family = excluded.font_family, font_size = excluded.font_size, updated_at = CURRENT_TIMESTAMP"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, pad_id) DO UPDATE SET compact_view = excluded.compact_view, editor_line_numbers = excluded.editor_line_numbers, preview_line_numbers = excluded.preview_line_numbers, line_links = excluded.line_links, toolbar_collapsed = excluded.toolbar_collapsed, navbar_collapsed = excluded.navbar_collapsed, font_family = excluded.font_family, font_size = excluded.font_size, updated_at = CURRENT_TIMESTAMP"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
Query::EDITOR_PREFERENCES_UPSERT_NOTE => {
|
||||||
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, font_family, font_size, updated_at) VALUES (?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, note_id) DO UPDATE SET compact_view = excluded.compact_view, editor_line_numbers = excluded.editor_line_numbers, preview_line_numbers = excluded.preview_line_numbers, line_links = excluded.line_links, toolbar_collapsed = excluded.toolbar_collapsed, font_family = excluded.font_family, font_size = excluded.font_size, updated_at = CURRENT_TIMESTAMP"#
|
r#"INSERT INTO user_editor_preferences (user_id, pad_id, note_id, compact_view, editor_line_numbers, preview_line_numbers, line_links, toolbar_collapsed, navbar_collapsed, font_family, font_size, updated_at) VALUES (?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, note_id) DO UPDATE SET compact_view = excluded.compact_view, editor_line_numbers = excluded.editor_line_numbers, preview_line_numbers = excluded.preview_line_numbers, line_links = excluded.line_links, toolbar_collapsed = excluded.toolbar_collapsed, navbar_collapsed = excluded.navbar_collapsed, font_family = excluded.font_family, font_size = excluded.font_size, updated_at = CURRENT_TIMESTAMP"#
|
||||||
}
|
}
|
||||||
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
Query::EDITOR_PREFERENCES_DELETE_BY_USER => {
|
||||||
r#"DELETE FROM user_editor_preferences WHERE user_id = ?"#
|
r#"DELETE FROM user_editor_preferences WHERE user_id = ?"#
|
||||||
|
|||||||
@@ -7344,3 +7344,158 @@ dialog::backdrop {
|
|||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Published-note permalinks ------------------------------------------------ */
|
||||||
|
.public-content .preview-source-line {
|
||||||
|
scroll-margin-top: 88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .public-permalink {
|
||||||
|
position: absolute;
|
||||||
|
top: .08em;
|
||||||
|
left: -1.55rem;
|
||||||
|
z-index: 3;
|
||||||
|
display: inline-grid;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: var(--muted-2);
|
||||||
|
font: 700 .86rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||||
|
text-decoration: none;
|
||||||
|
user-select: none;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(3px);
|
||||||
|
transition: opacity .14s ease, transform .14s ease, background-color .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .list-source-line > .public-permalink {
|
||||||
|
left: -2.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .preview-source-line:hover > .public-permalink,
|
||||||
|
.public-content .preview-source-line:focus-within > .public-permalink,
|
||||||
|
.public-content .public-permalink:focus-visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .public-permalink:hover,
|
||||||
|
.public-content .public-permalink:focus-visible {
|
||||||
|
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
||||||
|
color: var(--text);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .public-permalink.is-copied {
|
||||||
|
background: color-mix(in srgb, var(--success) 18%, transparent);
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-content .is-public-link-target {
|
||||||
|
border-radius: 5px;
|
||||||
|
background: color-mix(in srgb, var(--accent) 11%, transparent);
|
||||||
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 24%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: none), (pointer: coarse) {
|
||||||
|
.public-content .public-permalink {
|
||||||
|
opacity: .62;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Per-user, per-note navigation-bar focus mode ---------------------------- */
|
||||||
|
.pad-page .navbar-collapse-toggle {
|
||||||
|
position: fixed;
|
||||||
|
top: max(3px, env(safe-area-inset-top));
|
||||||
|
right: 0;
|
||||||
|
z-index: 40;
|
||||||
|
display: grid;
|
||||||
|
width: 24px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
place-items: center;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--border-strong) 76%, transparent);
|
||||||
|
border-top: 0;
|
||||||
|
border-right: 0;
|
||||||
|
border-radius: 0 0 0 9px;
|
||||||
|
background: color-mix(in srgb, var(--surface-toolbar) 88%, transparent);
|
||||||
|
color: var(--muted);
|
||||||
|
box-shadow: 0 6px 18px var(--shadow-28);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-5px);
|
||||||
|
transition: width .14s ease, opacity .14s ease, transform .14s ease, color .14s ease, background-color .14s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page .navbar-collapse-toggle > span {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 14px;
|
||||||
|
height: 9px;
|
||||||
|
transition: transform .18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page .navbar-collapse-toggle > span::before,
|
||||||
|
.pad-page .navbar-collapse-toggle > span::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
width: 8px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page .navbar-collapse-toggle > span::before {
|
||||||
|
left: 0;
|
||||||
|
transform: rotate(-36deg);
|
||||||
|
transform-origin: right center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page .navbar-collapse-toggle > span::after {
|
||||||
|
right: 0;
|
||||||
|
transform: rotate(36deg);
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page .app-header:hover + .navbar-collapse-toggle,
|
||||||
|
.pad-page .navbar-collapse-toggle:hover,
|
||||||
|
.pad-page .navbar-collapse-toggle:focus-visible,
|
||||||
|
.pad-page.navbar-collapsed .navbar-collapse-toggle:hover,
|
||||||
|
.pad-page.navbar-collapsed .navbar-collapse-toggle:focus-visible {
|
||||||
|
width: 32px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page.navbar-collapsed .navbar-collapse-toggle > span {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page.navbar-collapsed {
|
||||||
|
grid-template-rows: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page.navbar-collapsed .app-header {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-page.navbar-collapsed .editor-layout {
|
||||||
|
height: 100%;
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: none), (pointer: coarse) {
|
||||||
|
.pad-page .navbar-collapse-toggle {
|
||||||
|
width: 32px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: .78;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<button id="navbar-collapse-toggle" class="navbar-collapse-toggle" type="button" aria-pressed="false"
|
||||||
|
aria-label="Hide navigation bar" title="Hide navigation bar"><span aria-hidden="true"></span></button>
|
||||||
<main class="editor-layout">
|
<main class="editor-layout">
|
||||||
<section class="editor-panel">
|
<section class="editor-panel">
|
||||||
<div class="editor-toolbar">
|
<div class="editor-toolbar">
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import { loadHighlight, loadMediaPlayer, loadMermaid } from "@rustpad/vendor-lib
|
|||||||
|
|
||||||
export function startNoteEditor(adapter) {
|
export function startNoteEditor(adapter) {
|
||||||
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
|
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
|
||||||
const modeToggle = document.querySelector("#mode-toggle"), toolbarCollapseToggle = document.querySelector("#toolbar-collapse-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog");
|
const modeToggle = document.querySelector("#mode-toggle"), toolbarCollapseToggle = document.querySelector("#toolbar-collapse-toggle"), navbarCollapseToggle = document.querySelector("#navbar-collapse-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog");
|
||||||
const accessLevel = document.querySelector("#access-level"), roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), mobileConnectionDetails = document.querySelector("#mobile-connection-details"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread"), mobileChatUnread = document.querySelector("#mobile-chat-unread"), connectionNotice = document.querySelector("#connection-notice"), connectionNoticeTitle = document.querySelector("#connection-notice-title"), connectionNoticeMessage = document.querySelector("#connection-notice-message");
|
const accessLevel = document.querySelector("#access-level"), roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), mobileConnectionDetails = document.querySelector("#mobile-connection-details"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread"), mobileChatUnread = document.querySelector("#mobile-chat-unread"), connectionNotice = document.querySelector("#connection-notice"), connectionNoticeTitle = document.querySelector("#connection-notice-title"), connectionNoticeMessage = document.querySelector("#connection-notice-message");
|
||||||
const saveState = document.querySelector("#save-state");
|
const saveState = document.querySelector("#save-state");
|
||||||
editor.readOnly = true;
|
editor.readOnly = true;
|
||||||
@@ -42,6 +42,7 @@ export function startNoteEditor(adapter) {
|
|||||||
const shareToken = new URLSearchParams(location.search).get("share");
|
const shareToken = new URLSearchParams(location.search).get("share");
|
||||||
const notePreferenceKey = name => `rustpad:${name}:${location.pathname}`;
|
const notePreferenceKey = name => `rustpad:${name}:${location.pathname}`;
|
||||||
let toolbarCollapsed = localStorage.getItem(notePreferenceKey("toolbar-collapsed")) === "on";
|
let toolbarCollapsed = localStorage.getItem(notePreferenceKey("toolbar-collapsed")) === "on";
|
||||||
|
let navbarCollapsed = localStorage.getItem(notePreferenceKey("navbar-collapsed")) === "on";
|
||||||
const collaborationClientId = typeof crypto.randomUUID === "function"
|
const collaborationClientId = typeof crypto.randomUUID === "function"
|
||||||
? crypto.randomUUID().replaceAll("-", "")
|
? crypto.randomUUID().replaceAll("-", "")
|
||||||
: `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
: `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
||||||
@@ -234,6 +235,7 @@ export function startNoteEditor(adapter) {
|
|||||||
previewLineToggle.checked = info.preview_line_numbers === true;
|
previewLineToggle.checked = info.preview_line_numbers === true;
|
||||||
lineLinksToggle.checked = info.line_links === true;
|
lineLinksToggle.checked = info.line_links === true;
|
||||||
toolbarCollapsed = info.toolbar_collapsed === true;
|
toolbarCollapsed = info.toolbar_collapsed === true;
|
||||||
|
navbarCollapsed = info.navbar_collapsed === true;
|
||||||
if (["mono", "system", "serif", "arial", "georgia"].includes(info.font_family)) fontFamily.value = info.font_family;
|
if (["mono", "system", "serif", "arial", "georgia"].includes(info.font_family)) fontFamily.value = info.font_family;
|
||||||
if (["14", "16", "18", "20", "22"].includes(String(info.font_size))) fontSize.value = String(info.font_size);
|
if (["14", "16", "18", "20", "22"].includes(String(info.font_size))) fontSize.value = String(info.font_size);
|
||||||
}
|
}
|
||||||
@@ -1172,6 +1174,15 @@ export function startNoteEditor(adapter) {
|
|||||||
toolbarCollapseToggle.querySelector(".toolbar-collapse-toggle__icon").textContent = toolbarCollapsed ? "⌄" : "⌃";
|
toolbarCollapseToggle.querySelector(".toolbar-collapse-toggle__icon").textContent = toolbarCollapsed ? "⌄" : "⌃";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyNavbarCollapsed() {
|
||||||
|
document.body.classList.toggle("navbar-collapsed", navbarCollapsed);
|
||||||
|
if (!navbarCollapseToggle) return;
|
||||||
|
const label = navbarCollapsed ? "Show navigation bar" : "Hide navigation bar";
|
||||||
|
navbarCollapseToggle.setAttribute("aria-pressed", String(navbarCollapsed));
|
||||||
|
navbarCollapseToggle.setAttribute("aria-label", label);
|
||||||
|
navbarCollapseToggle.title = label;
|
||||||
|
}
|
||||||
|
|
||||||
function applyUi({ write = false, replace = false } = {}) {
|
function applyUi({ write = false, replace = false } = {}) {
|
||||||
const view = activeView();
|
const view = activeView();
|
||||||
renderedView = view;
|
renderedView = view;
|
||||||
@@ -1180,6 +1191,7 @@ export function startNoteEditor(adapter) {
|
|||||||
document.body.classList.toggle("compact-editor", compactToggle.checked);
|
document.body.classList.toggle("compact-editor", compactToggle.checked);
|
||||||
document.body.classList.toggle("compact-note-layout", compactLayoutQuery.matches);
|
document.body.classList.toggle("compact-note-layout", compactLayoutQuery.matches);
|
||||||
applyToolbarCollapsed();
|
applyToolbarCollapsed();
|
||||||
|
applyNavbarCollapsed();
|
||||||
document.querySelectorAll("[data-view]").forEach(button => {
|
document.querySelectorAll("[data-view]").forEach(button => {
|
||||||
const active = button.dataset.view === view;
|
const active = button.dataset.view === view;
|
||||||
button.classList.toggle("active", active);
|
button.classList.toggle("active", active);
|
||||||
@@ -1602,6 +1614,7 @@ export function startNoteEditor(adapter) {
|
|||||||
});
|
});
|
||||||
modeToggle.addEventListener("click", () => { uiState = { ...uiState, mode: uiState.mode === "markdown" ? "text" : "markdown" }; applyUiPreservingScroll({ write: true }); });
|
modeToggle.addEventListener("click", () => { uiState = { ...uiState, mode: uiState.mode === "markdown" ? "text" : "markdown" }; applyUiPreservingScroll({ write: true }); });
|
||||||
toolbarCollapseToggle?.addEventListener("click", () => { toolbarCollapsed = !toolbarCollapsed; localStorage.setItem(notePreferenceKey("toolbar-collapsed"), toolbarCollapsed ? "on" : "off"); applyUiPreservingScroll(); scheduleEditorSettingsSave({ personal: true }); });
|
toolbarCollapseToggle?.addEventListener("click", () => { toolbarCollapsed = !toolbarCollapsed; localStorage.setItem(notePreferenceKey("toolbar-collapsed"), toolbarCollapsed ? "on" : "off"); applyUiPreservingScroll(); scheduleEditorSettingsSave({ personal: true }); });
|
||||||
|
navbarCollapseToggle?.addEventListener("click", () => { navbarCollapsed = !navbarCollapsed; localStorage.setItem(notePreferenceKey("navbar-collapsed"), navbarCollapsed ? "on" : "off"); if (navbarCollapsed) setHeaderMenuOpen(false); applyUiPreservingScroll(); scheduleEditorSettingsSave({ personal: true }); });
|
||||||
lineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("line-numbers"), lineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); scheduleEditorSettingsSave({ personal: true }); });
|
lineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("line-numbers"), lineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); scheduleEditorSettingsSave({ personal: true }); });
|
||||||
previewLineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("preview-line-numbers"), previewLineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); alignPreviewLineNumbers(preview); scheduleEditorSettingsSave({ personal: true }); });
|
previewLineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("preview-line-numbers"), previewLineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); alignPreviewLineNumbers(preview); scheduleEditorSettingsSave({ personal: true }); });
|
||||||
compactToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("compact"), compactToggle.checked ? "on" : "off"); syncMobileEditorControls(); applyUiPreservingScroll(); scheduleEditorSettingsSave({ personal: true }); });
|
compactToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("compact"), compactToggle.checked ? "on" : "off"); syncMobileEditorControls(); applyUiPreservingScroll(); scheduleEditorSettingsSave({ personal: true }); });
|
||||||
@@ -1800,6 +1813,7 @@ export function startNoteEditor(adapter) {
|
|||||||
preview_line_numbers: previewLineToggle.checked,
|
preview_line_numbers: previewLineToggle.checked,
|
||||||
line_links: lineLinksToggle.checked,
|
line_links: lineLinksToggle.checked,
|
||||||
toolbar_collapsed: toolbarCollapsed,
|
toolbar_collapsed: toolbarCollapsed,
|
||||||
|
navbar_collapsed: navbarCollapsed,
|
||||||
font_family: fontFamily.value,
|
font_family: fontFamily.value,
|
||||||
font_size: Number(fontSize.value),
|
font_size: Number(fontSize.value),
|
||||||
};
|
};
|
||||||
|
|||||||
+42
-5
@@ -12,6 +12,7 @@ installGlobalDiagnostics();
|
|||||||
|
|
||||||
import { api } from "@rustpad/api";
|
import { api } from "@rustpad/api";
|
||||||
import { copyText } from "@rustpad/clipboard";
|
import { copyText } from "@rustpad/clipboard";
|
||||||
|
import { lineFromHash, lineLink } from "@rustpad/line-links";
|
||||||
import { alignPreviewLineNumbers, renderMarkdown, setMarkdownFiles } from "@rustpad/markdown";
|
import { alignPreviewLineNumbers, renderMarkdown, setMarkdownFiles } from "@rustpad/markdown";
|
||||||
import { toast } from "@rustpad/toast";
|
import { toast } from "@rustpad/toast";
|
||||||
import { getTheme } from "@rustpad/theme";
|
import { getTheme } from "@rustpad/theme";
|
||||||
@@ -52,17 +53,45 @@ function lockPublicContent(allowTaskUpdates) {
|
|||||||
content.querySelectorAll('.preview-editable').forEach(node => node.classList.remove('preview-editable'));
|
content.querySelectorAll('.preview-editable').forEach(node => node.classList.remove('preview-editable'));
|
||||||
content.querySelectorAll('.task-checkbox').forEach(box => { box.disabled = !allowTaskUpdates; box.title = allowTaskUpdates ? 'Update this task' : 'Task updates are disabled by the owner'; });
|
content.querySelectorAll('.task-checkbox').forEach(box => { box.disabled = !allowTaskUpdates; box.title = allowTaskUpdates ? 'Update this task' : 'Task updates are disabled by the owner'; });
|
||||||
}
|
}
|
||||||
function scrollToPublicAnchor(hash, behavior = "auto") {
|
function publicAnchorTarget(hash) {
|
||||||
const rawId = String(hash || "").replace(/^#/, "");
|
const rawId = String(hash || "").replace(/^#/, "");
|
||||||
if (!rawId) return false;
|
if (!rawId) return null;
|
||||||
let id;
|
let id;
|
||||||
try { id = decodeURIComponent(rawId); } catch { id = rawId; }
|
try { id = decodeURIComponent(rawId); } catch { id = rawId; }
|
||||||
const target = document.getElementById(id);
|
const target = document.getElementById(id);
|
||||||
if (!target || !content.contains(target)) return false;
|
if (target && content.contains(target)) return target;
|
||||||
|
const line = lineFromHash(hash);
|
||||||
|
return line ? content.querySelector(`.preview-source-line[data-source-line="${line}"]`) : null;
|
||||||
|
}
|
||||||
|
function markPublicAnchor(target) {
|
||||||
|
content.querySelectorAll(".is-public-link-target").forEach(node => node.classList.remove("is-public-link-target"));
|
||||||
|
target?.classList.add("is-public-link-target");
|
||||||
|
}
|
||||||
|
function scrollToPublicAnchor(hash, behavior = "auto") {
|
||||||
|
const target = publicAnchorTarget(hash);
|
||||||
|
if (!target) return false;
|
||||||
for (let details = target.closest("details"); details; details = details.parentElement?.closest("details")) details.open = true;
|
for (let details = target.closest("details"); details; details = details.parentElement?.closest("details")) details.open = true;
|
||||||
|
markPublicAnchor(target);
|
||||||
target.scrollIntoView({ behavior, block: "start" });
|
target.scrollIntoView({ behavior, block: "start" });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
function installPublicPermalinks() {
|
||||||
|
content.querySelectorAll(".preview-source-line[data-source-line]").forEach(target => {
|
||||||
|
if (target.matches("hr") || target.querySelector(":scope > .public-permalink")) return;
|
||||||
|
const line = Number(target.dataset.sourceLine);
|
||||||
|
if (!Number.isSafeInteger(line) || line < 1) return;
|
||||||
|
const heading = target.matches("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]");
|
||||||
|
const anchor = document.createElement("a");
|
||||||
|
anchor.className = "public-permalink";
|
||||||
|
anchor.href = heading ? `#${encodeURIComponent(target.id)}` : `#L${line}`;
|
||||||
|
anchor.dataset.line = String(line);
|
||||||
|
anchor.dataset.linkKind = heading ? "heading" : "line";
|
||||||
|
anchor.textContent = heading ? "¶" : "#";
|
||||||
|
anchor.setAttribute("aria-label", heading ? `Copy link to heading on line ${line}` : `Copy link to line ${line}`);
|
||||||
|
anchor.title = heading ? "Copy link to this heading" : `Copy link to line ${line}`;
|
||||||
|
target.append(anchor);
|
||||||
|
});
|
||||||
|
}
|
||||||
function setFullWidth(enabled, { persist = true } = {}) {
|
function setFullWidth(enabled, { persist = true } = {}) {
|
||||||
const active = Boolean(enabled);
|
const active = Boolean(enabled);
|
||||||
document.body.classList.toggle("public-full-width", active);
|
document.body.classList.toggle("public-full-width", active);
|
||||||
@@ -77,14 +106,22 @@ function restoreFullWidth() {
|
|||||||
try { enabled = localStorage.getItem(fullWidthStorageKey) === "1"; } catch { }
|
try { enabled = localStorage.getItem(fullWidthStorageKey) === "1"; } catch { }
|
||||||
setFullWidth(enabled, { persist: false });
|
setFullWidth(enabled, { persist: false });
|
||||||
}
|
}
|
||||||
async function initialize() { try { const page = await api(`/api/public/${encodeURIComponent(token)}`, { headers: pageHeaders() }); if (passwordDialog.open) passwordDialog.close(); passwordError.textContent = ""; document.querySelector("#public-title").textContent = page.title; document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")}${page.allow_task_updates ? " · tasks can be updated" : ""}`; document.title = `${page.title} · RustPad`; setMarkdownFiles(page.files || []); content.innerHTML = renderMarkdown(page.content); alignPreviewLineNumbers(content); lockPublicContent(page.allow_task_updates); await Promise.all([renderMermaid(), renderCodeHighlight(), renderMediaPlayers()]); requestAnimationFrame(() => scrollToPublicAnchor(location.hash)); } catch (error) { if (error.status === 401 || error.status === 403) { passwordError.textContent = error.status === 403 ? "Sign in with an authorized account or enter the resource password." : "Enter the correct password."; if (!passwordDialog.open) passwordDialog.showModal(); passwordInput.focus(); return; } content.replaceChildren(); const message = document.createElement("p"); message.className = "error"; message.textContent = String(error.message); content.append(message); } }
|
async function initialize() { try { const page = await api(`/api/public/${encodeURIComponent(token)}`, { headers: pageHeaders() }); if (passwordDialog.open) passwordDialog.close(); passwordError.textContent = ""; document.querySelector("#public-title").textContent = page.title; document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")}${page.allow_task_updates ? " · tasks can be updated" : ""}`; document.title = `${page.title} · RustPad`; setMarkdownFiles(page.files || []); content.innerHTML = renderMarkdown(page.content); lockPublicContent(page.allow_task_updates); await Promise.all([renderMermaid(), renderCodeHighlight(), renderMediaPlayers()]); installPublicPermalinks(); alignPreviewLineNumbers(content); requestAnimationFrame(() => scrollToPublicAnchor(location.hash)); } catch (error) { if (error.status === 401 || error.status === 403) { passwordError.textContent = error.status === 403 ? "Sign in with an authorized account or enter the resource password." : "Enter the correct password."; if (!passwordDialog.open) passwordDialog.showModal(); passwordInput.focus(); return; } content.replaceChildren(); const message = document.createElement("p"); message.className = "error"; message.textContent = String(error.message); content.append(message); } }
|
||||||
content.addEventListener("click", event => {
|
content.addEventListener("click", async event => {
|
||||||
const link = event.target.closest('a[href^="#"]');
|
const link = event.target.closest('a[href^="#"]');
|
||||||
if (!link || !content.contains(link)) return;
|
if (!link || !content.contains(link)) return;
|
||||||
const hash = link.getAttribute("href");
|
const hash = link.getAttribute("href");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!hash || hash === "#" || !scrollToPublicAnchor(hash, "smooth")) return;
|
if (!hash || hash === "#" || !scrollToPublicAnchor(hash, "smooth")) return;
|
||||||
history.replaceState(null, "", `${location.pathname}${location.search}${hash}`);
|
history.replaceState(null, "", `${location.pathname}${location.search}${hash}`);
|
||||||
|
if (!link.classList.contains("public-permalink")) return;
|
||||||
|
try {
|
||||||
|
const href = link.dataset.linkKind === "heading" ? new URL(hash, location.href).href : lineLink(location.href, Number(link.dataset.line));
|
||||||
|
await copyText(href);
|
||||||
|
link.classList.add("is-copied");
|
||||||
|
setTimeout(() => link.classList.remove("is-copied"), 900);
|
||||||
|
toast(link.dataset.linkKind === "heading" ? "Heading link copied" : `Link to line ${link.dataset.line} copied`);
|
||||||
|
} catch (error) { toast(error.message); }
|
||||||
});
|
});
|
||||||
window.addEventListener("hashchange", () => scrollToPublicAnchor(location.hash, "smooth"));
|
window.addEventListener("hashchange", () => scrollToPublicAnchor(location.hash, "smooth"));
|
||||||
content.addEventListener("change", async event => { const box = event.target.closest(".task-checkbox"); if (!box || box.disabled) return; const previous = !box.checked; box.disabled = true; try { const page = await api(`/api/public/${encodeURIComponent(token)}/tasks`, { method: "POST", headers: pageHeaders(), body: JSON.stringify({ source_line: Number(box.dataset.sourceLine), checked: box.checked }) }); document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")} · tasks can be updated`; toast("Task saved"); } catch (error) { box.checked = previous; toast(error.message); } finally { box.disabled = false; } });
|
content.addEventListener("change", async event => { const box = event.target.closest(".task-checkbox"); if (!box || box.disabled) return; const previous = !box.checked; box.disabled = true; try { const page = await api(`/api/public/${encodeURIComponent(token)}/tasks`, { method: "POST", headers: pageHeaders(), body: JSON.stringify({ source_line: Number(box.dataset.sourceLine), checked: box.checked }) }); document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")} · tasks can be updated`; toast("Task saved"); } catch (error) { box.checked = previous; toast(error.message); } finally { box.disabled = false; } });
|
||||||
|
|||||||
Reference in New Issue
Block a user