diff --git a/db/db copy.sqlite3 b/db/db-sauve.sqlite3 similarity index 79% rename from db/db copy.sqlite3 rename to db/db-sauve.sqlite3 index a531d5d..2280fcd 100644 Binary files a/db/db copy.sqlite3 and b/db/db-sauve.sqlite3 differ diff --git a/db/db.sql b/db/db.sql index 13c5378..b1e54c7 100644 --- a/db/db.sql +++ b/db/db.sql @@ -1,5 +1,7 @@ create table pages ( - page_name text primary key, + domain text, + page_name text, page_text text, - active boolean default true not null + active boolean default true not null, + primary key (domain, page_name) ); \ No newline at end of file diff --git a/db/db.sqlite3 b/db/db.sqlite3 index 9a67e74..5ace3b6 100644 Binary files a/db/db.sqlite3 and b/db/db.sqlite3 differ diff --git a/src/db.rs b/src/db.rs index 1dddb91..11a91a4 100644 --- a/src/db.rs +++ b/src/db.rs @@ -6,11 +6,12 @@ pub type Connection = r2d2::PooledConnection Result, Error> { +pub async fn get_page_by_name(pool: &Pool, domain: String, pagename: String) -> Result, Error> { let pool = pool.clone(); let conn = web::block(move || pool.get()) @@ -19,12 +20,13 @@ pub async fn get_page_by_name(pool: &Pool, pagename: String) -> Result web::block(move || { let mut stmt = conn - .prepare("SELECT page_name, page_text from pages WHERE active=true and page_name=?")?; + .prepare("SELECT domain, page_name, page_text from pages WHERE active=true and domain=? and page_name=?")?; - stmt.query_map([pagename], |row| { + stmt.query_map([domain, pagename], |row| { Ok(Page { - page_name: row.get(0)?, - page_text: row.get(1)?, + domain: row.get(0)?, + page_name: row.get(1)?, + page_text: row.get(2)?, }) }) .and_then(Iterator::collect) @@ -33,7 +35,7 @@ pub async fn get_page_by_name(pool: &Pool, pagename: String) -> Result .map_err(error::ErrorInternalServerError) } -pub async fn update_page(pool: &Pool, page_name: String, page_text: String) -> Result { +pub async fn update_page(pool: &Pool, domain: String, page_name: String, page_text: String) -> Result { let pool = pool.clone(); let conn = web::block(move || pool.get()) @@ -42,10 +44,9 @@ pub async fn update_page(pool: &Pool, page_name: String, page_text: String) -> R web::block(move || { let mut stmt = conn - .prepare("insert or replace into pages (page_name, page_text, active) values (?, ?, true)")?; - stmt.execute([page_name, page_text, ]) + .prepare("insert or replace into pages (domain, page_name, page_text, active) values (?, ?, ?, true)")?; + stmt.execute([domain, page_name, page_text, ]) }) .await? .map_err(error::ErrorInternalServerError) } - diff --git a/src/main.rs b/src/main.rs index 2e13a9a..208237c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,10 +30,10 @@ async fn main() -> std::io::Result<()> { App::new() .app_data(web::Data::new(appdata.to_owned())) .service(index::index) - .service(page::page) - .service(page::save_page) .service(actix_files::Files::new("/static", "./static")) .service(actix_files::Files::new("/static/modules", "./static/modules")) + .service(page::page) + .service(page::save_page) }) .bind((ip, port))? .run() diff --git a/src/page.rs b/src/page.rs index 505199c..9f59d74 100644 --- a/src/page.rs +++ b/src/page.rs @@ -3,8 +3,6 @@ use actix_web::{get, put, Responder}; use actix_web::{web, HttpResponse}; use serde::Deserialize; -use std::fs; - use askama_actix::Template; use askama_actix::TemplateToResponse; @@ -25,37 +23,25 @@ struct QueryParams { pub data: Option, } -async fn get_data(data: & web::Data, pagename: String) -> String { - let fut_page_datas = db::get_page_by_name(&data.pool, pagename.to_owned()); +async fn get_data(data: & web::Data, domain: String, page_name: String) -> String { + let fut_page_datas = db::get_page_by_name(&data.pool, domain.to_owned(), page_name.to_owned()); let page_datas = fut_page_datas.await.unwrap(); - let md; match page_datas.first() { - None => { - let filename = String::from("pages/") + pagename.as_str() + ".md"; - md = match fs::read_to_string(filename) { - Ok(txt) => { - db::update_page(&data.pool, pagename.to_owned(), txt.to_owned()) - .await - .unwrap(); - txt - } - Err(_) => String::from("# ") + pagename.replace("_", " ").as_str(), - }; - } - Some(dat) => md = (*dat.page_text).to_string(), + None => String::from("# ") + page_name.replace("_", " ").as_str(), + Some(dat) => (*dat.page_text).to_string(), } - md } -#[get("/page/{page}")] +#[get("/{domain}/{page}")] async fn page( - path: web::Path<(String,)>, + path: web::Path<(String, String,)>, data: web::Data, params: web::Query, ) -> impl Responder { - let pagename = &path.0; + let domain = &path.0; + let page_name = &path.1; - let md = get_data(&data, pagename.to_owned()).await; + let md = get_data(&data, domain.to_owned(), page_name.to_owned()).await; match ¶ms.data { Some(_) => { @@ -66,7 +52,7 @@ async fn page( None => {} } - let name = data.name.to_owned() + " - " + pagename.as_str(); + let name = data.name.to_owned() + " - " + page_name.as_str(); let root = data.root.to_owned(); //let init = String::from("init();"); let init = format!("init();"); @@ -79,17 +65,18 @@ async fn page( .to_response() } -#[put("/page/{page}")] +#[put("/{domain}/{page}")] async fn save_page( body: String, - path: web::Path<(String,)>, + path: web::Path<(String, String)>, data: web::Data, ) -> impl Responder { - let pagename = &path.0; + let domain = &path.0; + let pagename = &path.1; if pagename == "index" { return HttpResponse::Ok(); } - db::update_page(&data.pool, pagename.to_owned(), body) + db::update_page(&data.pool, domain.to_owned(), pagename.to_owned(), body) .await .unwrap(); HttpResponse::Ok() diff --git a/static/cheezenotes.css b/static/cheezenotes.css index a200c02..0b1d64a 100644 --- a/static/cheezenotes.css +++ b/static/cheezenotes.css @@ -46,7 +46,7 @@ div#content { overflow: auto; } -div #margindiv { +div#margindiv { max-width: 1200px; margin-left: auto; margin-right: auto; @@ -149,7 +149,7 @@ div.ta { textarea#ta { position: relative; - top: 3rem; + top: 0; display: none; width: 100%; height: 15rem; @@ -325,13 +325,13 @@ div#cheezenotes div.cheezenotes_line span.token { overflow: hidden; font-size: 0; vertical-align: baseline; - transition-property: font-size, color; - transition-duration: .2s; + /*transition-property: font-size, color; + transition-duration: .2s;*/ } div#cheezenotes:focus div.cheezenotes_line span.token { color: #1353b3; - font-size: 95%; + font-size: 90%; } div#cheezenotes div.body { diff --git a/static/modules/caret.js b/static/modules/caret.js index 6bab702..2cd6146 100644 --- a/static/modules/caret.js +++ b/static/modules/caret.js @@ -1,3 +1,18 @@ +function insertAtLineStart(txtBefore) { + let ret = saveSelection(); + let line = ret[0]; + let txt = line.innerText; + if (txt == '\n') { + txt = ""; + } + line.innerHTML = txtBefore + txt; + let span = document.createElement('span'); + span.innerHTML = txtBefore; + ret[1] = ret[1] + span.innerText.length;; + ret[3] = ret[3] + span.innerText.length;; + loadSelection(ret); +} + function insertTextAtCaret(txtBefore, txtAfter = '') { let ret = saveSelection(); let line = ret[0]; @@ -190,4 +205,4 @@ function setEndPositionInText(obj, position) { return 0; } -export { saveSelection, loadSelection, insertTextAtCaret }; \ No newline at end of file +export { saveSelection, loadSelection, insertTextAtCaret, insertAtLineStart }; \ No newline at end of file diff --git a/static/modules/cheezenotes.js b/static/modules/cheezenotes.js index 775f0a3..1afb227 100644 --- a/static/modules/cheezenotes.js +++ b/static/modules/cheezenotes.js @@ -1,13 +1,13 @@ -import { saveSelection, loadSelection, insertTextAtCaret } from './caret.js'; +import { saveSelection, loadSelection, insertTextAtCaret, insertAtLineStart } from './caret.js'; import { formatLine, load, save, formatTable, redrawTables, appendData, dpwidth } from './md.js'; const showtokens = [ { fontSize: 0 }, - { fontSize: '95%' } + { fontSize: '90%' } ]; const hidetokens = [ - { fontSize: '95%' }, + { fontSize: '90%' }, { fontSize: 0 } ]; @@ -21,14 +21,14 @@ function timeoutSave() { function showTokens() { let tokens = document.getElementsByClassName('token'); for (let i = 0; i < tokens.length; i++) { - tokens[i].animate(showtokens, { duration: 200, iterations: 1 }); + tokens[i].animate(showtokens, { duration: 50, iterations: 1 }); } } function hideTokens() { let tokens = document.getElementsByClassName('token'); for (let i = 0; i < tokens.length; i++) { - tokens[i].animate(hidetokens, { duration: 200, iterations: 1 }); + tokens[i].animate(hidetokens, { duration: 50, iterations: 1 }); } } @@ -82,7 +82,12 @@ function onh1button(e) { e.preventDefault(); let cheezenotes = document.getElementById('cheezenotes'); cheezenotes.focus(); - insertTextAtCaret('#', ''); + let sel = saveSelection(); + if (sel[0].classList.contains('h')) { + return false; + } else { + insertAtLineStart('# '); + } onedit(e); return false; } @@ -91,7 +96,12 @@ function onh2button(e) { e.preventDefault(); let cheezenotes = document.getElementById('cheezenotes'); cheezenotes.focus(); - insertTextAtCaret('##', ''); + let sel = saveSelection(); + if (sel[0].classList.contains('h')) { + return false; + } else { + insertAtLineStart('## '); + } onedit(e); return false; } @@ -100,7 +110,12 @@ function onh3button(e) { e.preventDefault(); let cheezenotes = document.getElementById('cheezenotes'); cheezenotes.focus(); - insertTextAtCaret('###', ''); + let sel = saveSelection(); + if (sel[0].classList.contains('h')) { + return false; + } else { + insertAtLineStart('### '); + } onedit(e); return false; } @@ -310,21 +325,13 @@ function onblur(e) { let cheezenotes = document.getElementById('cheezenotes'); redrawTables(cheezenotes); onsave(); - // hideTokens(); + hideTokens(); disableFormatButtons(); } function onfocus(e) { - let editModeButton = document.getElementById('editModeButton'); - if (editModeButton.classList.contains('buttonoff')) { - e.preventDefault(); - let cheezenotes = document.getElementById('cheezenotes'); - cheezenotes.contentEditable = false; - cheezenotes.blur(); - return false; - } redrawTables(cheezenotes, dpwidth()); - // showTokens(); + showTokens(); enableFormatButtons(); } diff --git a/static/modules/md.js b/static/modules/md.js index 203d130..dfdb329 100644 --- a/static/modules/md.js +++ b/static/modules/md.js @@ -94,17 +94,23 @@ function onlink(e) { if (link.host !== document.location.host) { open(link.href); } else { - //open(link.href, '_self'); fetch(link.href + "?data=").then((response) => { let ta = document.getElementById('ta'); response.text().then((data) => { let content = document.getElementById('content'); let left = content.scrollLeft; let top = content.scrollTop; - window.history.replaceState({top: top, left: left}, ""); + window.history.replaceState({ top: top, left: left }, ""); window.history.pushState({}, "", link.href); + document.title = 'CheezeNotes - ' + link.getAttribute('data-href'); ta.value = data; load(document.getElementById('ta'), document.getElementById('cheezenotes')); + + let editModeButton = document.getElementById('editModeButton'); + if (editModeButton.classList.contains('buttonoff')) { + let cheezenotes = document.getElementById('cheezenotes'); + cheezenotes.contentEditable = false; + } content.scrollTop = 0; content.scrollLeft = 0; }); @@ -149,21 +155,27 @@ function formatLine(line) { } else if (line.match(/^\s*######(\s|$)/i)) { token = /^(\s*######(\s|$))/i; elem.classList.add('h6'); + elem.classList.add('h'); } else if (line.match(/^\s*#####(\s|$)/i)) { token = /^(\s*#####(\s|$))/i; elem.classList.add('h5'); + elem.classList.add('h'); } else if (line.match(/^\s*####(\s|$)/i)) { token = /^(\s*####(\s|$))/i; elem.classList.add('h4'); + elem.classList.add('h'); } else if (line.match(/^\s*###(\s|$)/i)) { token = /^(\s*###(\s|$))/i; elem.classList.add('h3'); + elem.classList.add('h'); } else if (line.match(/^\s*##(\s|$)/)) { token = /^(\s*##(\s|$))/i; elem.classList.add('h2'); + elem.classList.add('h'); } else if (line.match(/^\s*#(\s|$)/i)) { token = /^(\s*#(\s|$))/i; elem.classList.add('h1'); + elem.classList.add('h'); } else if (line.match(/^\s*>\s*>\s*>(\s|$)/i)) { token = /^(\s*>\s*>\s*>(\s|$))/i; elem.classList.add('bq3');