commit
d9495f6afb
@ -0,0 +1 @@
|
||||
/target
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "mdwiki"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
clap = { version = "4.0.18", features = ["derive"] }
|
||||
|
||||
actix-web = "4"
|
||||
actix-files = "0.6.2"
|
||||
askama = { version = "0.11.1", features = ["with-actix-web"] }
|
||||
askama_actix = "0.13.0"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
git2 = "0.16.0"
|
||||
@ -0,0 +1,9 @@
|
||||
mdwiki
|
||||
======
|
||||
|
||||
Wiki utilisant la syntaxe MarkDown pour être rapide et facile à modifier.
|
||||
Idéal pour la prise de notes.
|
||||
Création dynamique de pages en créant un lien dans une page.
|
||||
|
||||
Ranges :
|
||||
https://stackoverflow.com/questions/6249095/how-to-set-the-caret-cursor-position-in-a-contenteditable-element-div
|
||||
@ -0,0 +1,26 @@
|
||||
use clap::Parser;
|
||||
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Arguments {
|
||||
/// IP address
|
||||
#[arg(short, long, default_value_t = String::from("127.0.0.1"))]
|
||||
pub ip: String,
|
||||
/// Port to listen to
|
||||
#[arg(short, long, default_value_t = 8081)]
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
impl Arguments {
|
||||
|
||||
pub fn parse_args() -> Arguments {
|
||||
let args = Arguments::parse();
|
||||
args
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppData {
|
||||
pub name: String,
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
use actix_web::web;
|
||||
use actix_web::{HttpResponse, Responder, get};
|
||||
|
||||
use askama_actix::Template;
|
||||
use askama_actix::TemplateToResponse;
|
||||
|
||||
use crate::commons::AppData;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
pub struct IndexTemplate {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(data: web::Data<AppData>) -> impl Responder {
|
||||
let name = data.name.to_owned();
|
||||
IndexTemplate { name }.to_response()
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
pub mod index;
|
||||
pub mod commons;
|
||||
@ -0,0 +1,28 @@
|
||||
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
|
||||
use actix_files;
|
||||
|
||||
use mdwiki::commons::{Arguments, AppData};
|
||||
use mdwiki::index;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
|
||||
let args = Arguments::parse_args();
|
||||
|
||||
let ip = args.ip;
|
||||
let port = args.port;
|
||||
|
||||
let appdata = AppData {
|
||||
name: String::from("MdNotes"),
|
||||
};
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(web::Data::new(appdata.to_owned()))
|
||||
.service(index::index)
|
||||
.service(actix_files::Files::new("/static", "./static"))
|
||||
})
|
||||
.bind((ip, port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
html {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
div#mdnotes {
|
||||
font-family: sans-serif;
|
||||
padding-top: 1rem;
|
||||
padding-left: 3rem;
|
||||
padding-right: 3rem;
|
||||
padding-bottom: 1rem;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
div#mdnotes div.h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-bottom: 1px solid #666666;
|
||||
}
|
||||
|
||||
div#mdnotes div.h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
div#mdnotes div.h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 0.3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
div#mdnotes div.h4 {
|
||||
font-style: italic;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
function change(evt) {
|
||||
reId(document.getElementById('mdnotes'));
|
||||
let res = getStartPositionInLine();
|
||||
let line = res[0];
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
let position = res[1];
|
||||
let id = line.id;
|
||||
toMD();
|
||||
toHTML();
|
||||
line = document.getElementById(id);
|
||||
setStartPositionInLine(line, position);
|
||||
return true;
|
||||
}
|
||||
|
||||
function reId(obj) {
|
||||
let prevId = '';
|
||||
let children = obj.childNodes;
|
||||
for (i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
if (child.classList.contains('mdnotes_line')) {
|
||||
if (prevId == child.id) {
|
||||
child.className = 'mdnotes_line';
|
||||
}
|
||||
prevId = child.id;
|
||||
child.id = 'mdnotes_' + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStartPositionInLine() {
|
||||
let selection = window.getSelection();
|
||||
let range = selection.getRangeAt(0).cloneRange();
|
||||
let obj = range.startContainer;
|
||||
|
||||
let position = range.startOffset;
|
||||
let line = null;
|
||||
|
||||
while (obj != null) {
|
||||
if (obj.previousSibling != null) {
|
||||
obj = obj.previousSibling;
|
||||
if (obj.innerText != null) {
|
||||
position += obj.innerText.length;
|
||||
} else if (obj.length != null) {
|
||||
position += obj.length;
|
||||
}
|
||||
} else if (obj.parentNode != null && obj.parentNode.classList != null && !obj.parentNode.classList.contains('mdnotes_line')) {
|
||||
obj = obj.parentNode;
|
||||
} else if (obj.parentNode != null && obj.parentNode.classList != null && obj.parentNode.classList.contains('mdnotes_line')) {
|
||||
line = obj.parentNode;
|
||||
obj = null;
|
||||
} else {
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
return [line, position];
|
||||
}
|
||||
|
||||
function setStartPositionInLine(line, position) {
|
||||
setStartPositionInDiv(line, position)
|
||||
}
|
||||
|
||||
function setStartPositionInDiv(obj, position) {
|
||||
let temppos = position;
|
||||
if (temppos > obj.innerText.length) {
|
||||
return temppos - obj.innerText.length;
|
||||
}
|
||||
let children = obj.childNodes;
|
||||
for (i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
if (child.nodeType == 3) {
|
||||
temppos = setStartPositionInText(child, position);
|
||||
} else {
|
||||
temppos = setStartPositionInDiv(child, temppos);
|
||||
if (temppos == 0) {
|
||||
return temppos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return temppos;
|
||||
}
|
||||
|
||||
function setStartPositionInText(obj, position) {
|
||||
if (position > obj.length) {
|
||||
return position - obj.length;
|
||||
}
|
||||
|
||||
let selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
let range = document.createRange();
|
||||
range.setStart(obj, position);
|
||||
selection.addRange(range);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function toHTML() {
|
||||
let ta = document.getElementById('ta');
|
||||
let mda = document.getElementById('mdnotes');
|
||||
mda.innerHTML = '';
|
||||
let lines = ta.value.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
let obj = document.createElement('div');
|
||||
let line = lines[i];
|
||||
if (line == '') {
|
||||
let br = document.createElement('br');
|
||||
obj.append(br);
|
||||
}
|
||||
let normLine = line.trim();
|
||||
if (normLine.startsWith('#### ')) {
|
||||
obj.classList.add('h4');
|
||||
} else if (normLine.startsWith('### ')) {
|
||||
obj.classList.add('h3');
|
||||
} else if (normLine.startsWith('## ')) {
|
||||
obj.classList.add('h2');
|
||||
} else if (normLine.startsWith('# ')) {
|
||||
obj.classList.add('h1');
|
||||
}
|
||||
obj.classList.add('mdnotes_line');
|
||||
if (line !== '') {
|
||||
obj.innerText = line;
|
||||
}
|
||||
obj.id = 'mdnotes_' + i;
|
||||
mda.append(obj);
|
||||
}
|
||||
}
|
||||
|
||||
function toMD() {
|
||||
let mda = document.getElementById('mdnotes');
|
||||
let content = mda.childNodes;
|
||||
let text = eltToMD(mda)
|
||||
let ta = document.getElementById('ta');
|
||||
ta.value = text;
|
||||
}
|
||||
|
||||
function eltToMD(elt) {
|
||||
let text = '';
|
||||
if (getType(elt) == 'TEXT') {
|
||||
text = elt.nodeValue;
|
||||
} else if (elt.innerText.length == 0) {
|
||||
text = '';
|
||||
} else {
|
||||
let children = elt.childNodes;
|
||||
let prevType = 'DIV';
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let type = getType(children[i]);
|
||||
if (type == 'DIV' && prevType != 'DIV') {
|
||||
text += "\n";
|
||||
}
|
||||
text += eltToMD(children[i]);
|
||||
if (type == 'DIV') {
|
||||
text += "\n";
|
||||
}
|
||||
prevType = type;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function getType(elt) {
|
||||
if (elt.nodeType == 3) {
|
||||
return 'TEXT'
|
||||
} else if (elt.nodeType == 1) {
|
||||
return elt.tagName;
|
||||
} else {
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}
|
||||
|
||||
toHTML();
|
||||
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{name}}</title>
|
||||
<link rel="stylesheet" href="static/mdnotes.css">
|
||||
</head>
|
||||
<body>
|
||||
<textarea id="ta" class="mdnotes" style="display: none;">
|
||||
# Titre 1
|
||||
Un petit texte
|
||||
## Titre 2
|
||||
### Titre 3-1
|
||||
Encore un joli texte
|
||||
### Titre 3-2
|
||||
T'en as marre ???
|
||||
#### Titre 4
|
||||
Moi oui !!!</textarea>
|
||||
<div id="mdnotes" class="mdnotes" oninput="return change(event)" contenteditable="true"></div>
|
||||
<script type="text/javascript" src="static/mdnotes.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in new issue