commit
2598efc9ff
@ -0,0 +1,6 @@
|
||||
[*]
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "pastebucket"
|
||||
version = "0.7.0"
|
||||
authors = ["rasul <rascul3@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
askama = "0.9"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
colored = "1.9"
|
||||
fern = { version = "0.5", features = ["colored"] }
|
||||
futures = "0.1"
|
||||
getopts = "0.2"
|
||||
gotham = "0.4"
|
||||
gotham_derive = "0.4"
|
||||
harsh = "0.1.6"
|
||||
hyper = "0.12"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
mime = "0.3"
|
||||
pulldown-cmark = "0.6"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
syntect = "3.3"
|
||||
toml = "0.5"
|
||||
url = "1.6"
|
@ -0,0 +1,5 @@
|
||||
work in progress
|
||||
|
||||
mostly works though but needs more works
|
||||
|
||||
https://p.rascul.xyz
|
@ -0,0 +1,5 @@
|
||||
max_width = 100
|
||||
hard_tabs = true
|
||||
newline_style = "unix"
|
||||
use_try_shorthand = true
|
||||
edition = "2018"
|
@ -0,0 +1,58 @@
|
||||
pub mod loglevel;
|
||||
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use gotham_derive::StateData;
|
||||
use serde_derive::Deserialize;
|
||||
use toml;
|
||||
|
||||
pub use crate::config::loglevel::LogLevel;
|
||||
use crate::result::Result;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, StateData)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub address: String,
|
||||
#[serde(default)]
|
||||
pub url: String,
|
||||
#[serde(default)]
|
||||
pub log_level: LogLevel,
|
||||
#[serde(default)]
|
||||
pub log_file: Option<String>,
|
||||
#[serde(default)]
|
||||
pub data_directory: PathBuf,
|
||||
#[serde(default)]
|
||||
pub template_directory: PathBuf,
|
||||
#[serde(default)]
|
||||
pub static_directory: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: "127.0.0.1:9214".into(),
|
||||
url: "http://127.0.0.1:9214".into(),
|
||||
log_level: LogLevel::info,
|
||||
log_file: None,
|
||||
data_directory: PathBuf::from("data"),
|
||||
template_directory: PathBuf::from("templates"),
|
||||
static_directory: PathBuf::from("static"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load<T: AsRef<Path>>(path: T) -> Result<Config> {
|
||||
if let Ok(mut file) = File::open(path.as_ref()) {
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)?;
|
||||
let config: Config = toml::from_str(&buf)?;
|
||||
Ok(config)
|
||||
} else {
|
||||
Ok(Config::default())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
use std::default::Default;
|
||||
|
||||
use log::LevelFilter;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub enum LogLevel {
|
||||
#[allow(non_camel_case_types)]
|
||||
error,
|
||||
#[allow(non_camel_case_types)]
|
||||
warn,
|
||||
#[allow(non_camel_case_types)]
|
||||
info,
|
||||
#[allow(non_camel_case_types)]
|
||||
debug,
|
||||
#[allow(non_camel_case_types)]
|
||||
trace,
|
||||
}
|
||||
|
||||
impl Default for LogLevel {
|
||||
fn default() -> LogLevel {
|
||||
LogLevel::info
|
||||
}
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
pub fn level(&self) -> LevelFilter {
|
||||
match &self {
|
||||
LogLevel::error => LevelFilter::Error,
|
||||
LogLevel::warn => LevelFilter::Warn,
|
||||
LogLevel::info => LevelFilter::Info,
|
||||
LogLevel::debug => LevelFilter::Debug,
|
||||
LogLevel::trace => LevelFilter::Trace,
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
use chrono::Local;
|
||||
use colored::Colorize;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use fern::Dispatch;
|
||||
|
||||
use crate::config::LogLevel;
|
||||
use crate::result::Result;
|
||||
|
||||
pub fn setup(level: &LogLevel) -> Result<()> {
|
||||
Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
let colors = ColoredLevelConfig::new()
|
||||
.error(Color::Red)
|
||||
.warn(Color::Magenta)
|
||||
.info(Color::Cyan)
|
||||
.debug(Color::Yellow)
|
||||
.trace(Color::Green);
|
||||
|
||||
out.finish(format_args!(
|
||||
"{} {} {}",
|
||||
Local::now()
|
||||
.format("%Y-%m-%dT%H:%M:%S%.3f%z")
|
||||
.to_string()
|
||||
.white()
|
||||
.bold(),
|
||||
colors.color(record.level()),
|
||||
message
|
||||
))
|
||||
})
|
||||
.level(level.level())
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
mod config;
|
||||
mod logger;
|
||||
mod paste;
|
||||
mod result;
|
||||
mod routes;
|
||||
mod syntax;
|
||||
|
||||
use result::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let config = config::Config::load("config.toml")?;
|
||||
logger::setup(&config.log_level)?;
|
||||
gotham::start(config.address.clone(), routes::build(config));
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter, Error, ErrorKind, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use harsh::HarshBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use toml;
|
||||
|
||||
use crate::result::Result;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Paste {
|
||||
pub id: String,
|
||||
pub dt: DateTime<Utc>,
|
||||
pub lang: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl Paste {
|
||||
pub fn from_file(path: PathBuf) -> Result<Self> {
|
||||
let file = File::open(path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut buf = String::new();
|
||||
reader.read_to_string(&mut buf)?;
|
||||
|
||||
let paste: Paste = toml::from_str(&buf)?;
|
||||
|
||||
Ok(paste)
|
||||
}
|
||||
|
||||
pub fn from_text(text: String) -> Result<Self> {
|
||||
let dt = Utc::now();
|
||||
let harsh = HarshBuilder::new().salt("salt and pepper").init()?;
|
||||
let encoded = harsh.encode(&[dt.timestamp_millis() as u64]);
|
||||
|
||||
if let Some(id) = encoded {
|
||||
Ok(Paste {
|
||||
id,
|
||||
dt,
|
||||
text,
|
||||
lang: "Plain Text".into(),
|
||||
})
|
||||
} else {
|
||||
Err(Box::new(Error::new(ErrorKind::Other, "couldn't generate hash")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn from_form(form: HashMap<String, String>) -> Result<Self> {
|
||||
let dt = Utc::now();
|
||||
let harsh = HarshBuilder::new().salt("salt and pepper").init()?;
|
||||
let encoded = harsh.encode(&[dt.timestamp_millis() as u64]);
|
||||
|
||||
if let Some(id) = encoded {
|
||||
let text: String = if let Some(t) = form.get("paste") {
|
||||
t.into()
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
let lang: String = if let Some(l) = form.get("lang") {
|
||||
l.into()
|
||||
} else {
|
||||
"Plain Text".into()
|
||||
};
|
||||
|
||||
Ok(Paste { id, dt, lang, text })
|
||||
} else {
|
||||
Err(Box::new(Error::new(ErrorKind::Other, "empty hash")))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_file(&self, path: PathBuf) -> Result<()> {
|
||||
let file = File::create(path)?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
writer.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
@ -0,0 +1,45 @@
|
||||
mod edit;
|
||||
mod index;
|
||||
mod raw;
|
||||
mod submit;
|
||||
mod view;
|
||||
|
||||
use gotham::handler::assets::FileOptions;
|
||||
use gotham::middleware::logger::RequestLogger;
|
||||
use gotham::middleware::state::StateMiddleware;
|
||||
use gotham::pipeline::new_pipeline;
|
||||
use gotham::pipeline::single::single_pipeline;
|
||||
use gotham::router::builder::*;
|
||||
use gotham::router::Router;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
pub fn build(config: Config) -> Router {
|
||||
let (chain, pipeline) = single_pipeline(
|
||||
new_pipeline()
|
||||
.add(RequestLogger::new(log::Level::Info))
|
||||
.add(StateMiddleware::new(config.clone()))
|
||||
.build(),
|
||||
);
|
||||
|
||||
build_router(chain, pipeline, |route| {
|
||||
route.get("/").to(index::route);
|
||||
route.post("/").to(submit::post);
|
||||
route.put("/").to(submit::put);
|
||||
route
|
||||
.get("/s/*")
|
||||
.to_dir(FileOptions::new(config.static_directory));
|
||||
route
|
||||
.get("/:id")
|
||||
.with_path_extractor::<view::Params>()
|
||||
.to(view::get);
|
||||
route
|
||||
.get("/:id/edit")
|
||||
.with_path_extractor::<edit::Params>()
|
||||
.to(edit::route);
|
||||
route
|
||||
.get("/:id/raw")
|
||||
.with_path_extractor::<raw::Params>()
|
||||
.to(raw::get);
|
||||
})
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
use askama::Template;
|
||||
use futures::future;
|
||||
|
||||
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||
use gotham::state::{FromState, State};
|
||||
use gotham_derive::{StateData, StaticResponseExtender};
|
||||
|
||||
use hyper::StatusCode;
|
||||
use mime;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::paste::Paste;
|
||||
use crate::syntax::SYNTAXES;
|
||||
|
||||
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||
pub struct Params {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
#[template(path = "edit.html")]
|
||||
pub struct Edit {
|
||||
id: String,
|
||||
dt: String,
|
||||
text: String,
|
||||
site_url: String,
|
||||
syntaxes: Vec<String>,
|
||||
syntax: String,
|
||||
}
|
||||
|
||||
pub fn route(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
let config = Config::take_from(&mut state);
|
||||
let Params { id } = Params::take_from(&mut state);
|
||||
|
||||
let mut path = config.data_directory.clone();
|
||||
path.push(id.clone());
|
||||
|
||||
match Paste::from_file(path) {
|
||||
Ok(paste) => {
|
||||
let template = Edit {
|
||||
id,
|
||||
dt: paste.dt.format("%Y-%m-%dT%H:%MZ").to_string(),
|
||||
text: paste.text,
|
||||
site_url: config.url,
|
||||
syntaxes: SYNTAXES.to_vec(),
|
||||
syntax: paste.lang,
|
||||
};
|
||||
|
||||
let res = match template.render() {
|
||||
Ok(content) => create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_HTML_UTF_8,
|
||||
content.into_bytes(),
|
||||
),
|
||||
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
future::ok((state, res))
|
||||
}
|
||||
Err(e) => {
|
||||
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||
future::err((state, io_error.into_handler_error()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
use askama::Template;
|
||||
use futures::future;
|
||||
|
||||
use gotham::handler::HandlerFuture;
|
||||
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||
use gotham::state::{FromState, State};
|
||||
|
||||
use hyper::StatusCode;
|
||||
use mime;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::syntax::SYNTAXES;
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
#[template(path = "index.html")]
|
||||
pub struct Index {
|
||||
syntaxes: Vec<String>,
|
||||
site_url: String,
|
||||
}
|
||||
|
||||
pub fn route(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
let config = Config::take_from(&mut state);
|
||||
|
||||
let template = Index {
|
||||
syntaxes: SYNTAXES.to_vec(),
|
||||
site_url: config.url,
|
||||
};
|
||||
|
||||
let res = match template.render() {
|
||||
Ok(content) => create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_HTML_UTF_8,
|
||||
content.into_bytes(),
|
||||
),
|
||||
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
future::ok((state, res))
|
||||
})
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
use futures::future;
|
||||
|
||||
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||
use gotham::helpers::http::response::create_response;
|
||||
use gotham::state::{FromState, State};
|
||||
use gotham_derive::{StateData, StaticResponseExtender};
|
||||
|
||||
use hyper::StatusCode;
|
||||
use mime;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::paste::Paste;
|
||||
|
||||
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||
pub struct Params {
|
||||
id: String,
|
||||
}
|
||||
|
||||
pub fn get(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
let config = Config::take_from(&mut state);
|
||||
let Params { id } = Params::take_from(&mut state);
|
||||
|
||||
let mut path = config.data_directory;
|
||||
path.push(id);
|
||||
|
||||
match Paste::from_file(path) {
|
||||
Ok(paste) => {
|
||||
let res = create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_PLAIN,
|
||||
paste.text,
|
||||
);
|
||||
|
||||
future::ok((state, res))
|
||||
},
|
||||
Err(e) => {
|
||||
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||
future::err((state, io_error.into_handler_error()))
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
use futures::{future, Future, Stream};
|
||||
|
||||
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||
use gotham::helpers::http::response::create_response;
|
||||
use gotham::state::{FromState, State};
|
||||
|
||||
use hyper::Response;
|
||||
use hyper::{Body, StatusCode};
|
||||
use mime;
|
||||
use url::form_urlencoded;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::paste::Paste;
|
||||
|
||||
pub fn put(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
Body::take_from(&mut state).concat2().then(|body| {
|
||||
let config = Config::take_from(&mut state);
|
||||
|
||||
match body {
|
||||
Ok(b) => {
|
||||
let body_content = b.into_bytes();
|
||||
let text = String::from_utf8(body_content.as_ref().to_vec()).unwrap();
|
||||
|
||||
match Paste::from_text(text) {
|
||||
Ok(paste) => {
|
||||
let mut path = config.data_directory.clone();
|
||||
path.push(paste.id.clone());
|
||||
|
||||
if let Err(e) = paste.to_file(path) {
|
||||
let err = Error::new(ErrorKind::Other, e.description());
|
||||
future::err((state, err.into_handler_error()))
|
||||
} else {
|
||||
let res = create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_PLAIN,
|
||||
format!("{}/{}\n", config.url, paste.id),
|
||||
);
|
||||
|
||||
future::ok((state, res))
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let err = Error::new(ErrorKind::Other, e.description());
|
||||
future::err((state, err.into_handler_error()))
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => future::err((state, e.into_handler_error())),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn post(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
Body::take_from(&mut state).concat2().then(|body| {
|
||||
let config = Config::take_from(&mut state);
|
||||
match body {
|
||||
Ok(b) => {
|
||||
let body_content = b.into_bytes();
|
||||
let form_map: HashMap<String, String> = form_urlencoded::parse(&body_content)
|
||||
.into_owned()
|
||||
.map(|x| x)
|
||||
.collect();
|
||||
|
||||
if let Ok(paste) = Paste::from_form(form_map) {
|
||||
let mut path = config.data_directory;
|
||||
path.push(paste.id.clone());
|
||||
|
||||
if let Err(e) = paste.to_file(path) {
|
||||
let err = Error::new(ErrorKind::Other, e.description());
|
||||
return future::err((state, err.into_handler_error()));
|
||||
}
|
||||
|
||||
let res = Response::builder()
|
||||
.status(303)
|
||||
.header("Location", format!("/{}", paste.id))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
future::ok((state, res))
|
||||
} else {
|
||||
let res = create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_PLAIN,
|
||||
"ERR"
|
||||
);
|
||||
|
||||
future::ok((state, res))
|
||||
}
|
||||
}
|
||||
Err(e) => future::err((state, e.into_handler_error())),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
use askama::Template;
|
||||
use futures::future;
|
||||
|
||||
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||
use gotham::state::{FromState, State};
|
||||
use gotham_derive::{StateData, StaticResponseExtender};
|
||||
|
||||
use hyper::StatusCode;
|
||||
use mime;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
use syntect::html::ClassedHTMLGenerator;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::paste::Paste;
|
||||
use crate::syntax::SYNTAX_SET;
|
||||
|
||||
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||
pub struct Params {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
#[template(path = "view.html")]
|
||||
pub struct View {
|
||||
id: String,
|
||||
dt: String,
|
||||
text_lines: Vec<String>,
|
||||
index_len: usize,
|
||||
site_url: String,
|
||||
syntax: String,
|
||||
}
|
||||
|
||||
pub fn get(mut state: State) -> Box<HandlerFuture> {
|
||||
Box::new({
|
||||
let config = Config::take_from(&mut state);
|
||||
let Params { id } = Params::take_from(&mut state);
|
||||
|
||||
let mut path = config.data_directory.clone();
|
||||
path.push(id.clone());
|
||||
|
||||
match Paste::from_file(path) {
|
||||
Ok(paste) => {
|
||||
let text_lines: Vec<String> = paste.text.lines().map(|s| s.into()).collect();
|
||||
let index_len = format!("{}", text_lines.len()).len();
|
||||
let syntax = SYNTAX_SET
|
||||
.find_syntax_by_name(&paste.lang)
|
||||
.unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text());
|
||||
let mut high_lines: Vec<String> = Vec::new();
|
||||
|
||||
for line in text_lines {
|
||||
let mut html_generator = ClassedHTMLGenerator::new(&syntax, &SYNTAX_SET);
|
||||
html_generator.parse_html_for_line(&line);
|
||||
high_lines.push(html_generator.finalize());
|
||||
}
|
||||
|
||||
let template = View {
|
||||
id,
|
||||
dt: paste.dt.format("%Y-%m-%dT%H:%MZ").to_string(),
|
||||
text_lines: high_lines,
|
||||
index_len,
|
||||
site_url: config.url,
|
||||
syntax: paste.lang,
|
||||
};
|
||||
|
||||
let res = match template.render() {
|
||||
Ok(content) => create_response(
|
||||
&state,
|
||||
StatusCode::OK,
|
||||
mime::TEXT_HTML_UTF_8,
|
||||
content.into_bytes(),
|
||||
),
|
||||
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
future::ok((state, res))
|
||||
}
|
||||
Err(e) => {
|
||||
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||
future::err((state, io_error.into_handler_error()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
use std::fmt;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SYNTAX_SET: SyntaxSet = SyntaxSet::load_defaults_newlines();
|
||||
pub static ref SYNTAXES: Vec<String> = {
|
||||
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||
let mut syntaxes: Vec<String> = Vec::new();
|
||||
|
||||
for syntax in syntax_set.syntaxes() {
|
||||
if ! syntax.hidden {
|
||||
syntaxes.push(syntax.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
syntaxes.sort();
|
||||
syntaxes
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Syntax {
|
||||
pub name: String,
|
||||
pub ext: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Syntax {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* theme "Solarized (light)" generated by syntect
|
||||
*/
|
||||
|
||||
.code {
|
||||
color: #657b83;
|
||||
}
|
||||
|
||||
.comment {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.meta.documentation {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.string {
|
||||
color: #2aa198;
|
||||
}
|
||||
.string.regexp {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.character.escape {
|
||||
color: #dc322f;
|
||||
}
|
||||
.constant.numeric {
|
||||
color: #6c71c4;
|
||||
}
|
||||
.variable {
|
||||
color: #268bd2;
|
||||
}
|
||||
.variable.function {
|
||||
color: #b58900;
|
||||
}
|
||||
.variable.language {
|
||||
color: #d33682;
|
||||
}
|
||||
.keyword {
|
||||
color: #859900;
|
||||
}
|
||||
.meta.import {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.control.import {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.control.import.from {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.other.import {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.control.at-rule.include {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.control.at-rule.import {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.operator.comparison {
|
||||
color: #657b83;
|
||||
}
|
||||
.keyword.operator.assignment {
|
||||
color: #657b83;
|
||||
}
|
||||
.keyword.operator.arithmetic {
|
||||
color: #657b83;
|
||||
}
|
||||
.storage {
|
||||
color: #859900;
|
||||
}
|
||||
.storage.modifier {
|
||||
color: #586e75;
|
||||
}
|
||||
.keyword.control.class {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name.class {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name.type.class {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.inherited-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
.entity.other.attribute-name {
|
||||
color: #b58900;
|
||||
}
|
||||
.support {
|
||||
color: #859900;
|
||||
}
|
||||
.support.type {
|
||||
color: #859900;
|
||||
}
|
||||
.support.class {
|
||||
color: #859900;
|
||||
}
|
||||
.entity.name.function {
|
||||
color: #b58900;
|
||||
}
|
||||
.punctuation.definition.variable {
|
||||
color: #859900;
|
||||
}
|
||||
.constant {
|
||||
color: #b58900;
|
||||
}
|
||||
.constant.language {
|
||||
color: #b58900;
|
||||
}
|
||||
.meta.preprocessor {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name.section {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.support.function.construct {
|
||||
color: #dc322f;
|
||||
}
|
||||
.keyword.other.new {
|
||||
color: #dc322f;
|
||||
}
|
||||
.constant.character {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.constant.other {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.entity.name.tag {
|
||||
color: #268bd2;
|
||||
}
|
||||
.punctuation.definition.tag.html {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.punctuation.definition.tag.begin {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.punctuation.definition.tag.end {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.support.function {
|
||||
color: #859900;
|
||||
}
|
||||
.punctuation.separator.continuation {
|
||||
color: #dc322f;
|
||||
}
|
||||
.storage.type {
|
||||
color: #268bd2;
|
||||
}
|
||||
.support.type.exception {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.keyword.other.special-method {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.string.quoted.double {
|
||||
color: #2aa198;
|
||||
}
|
||||
.string.quoted.single {
|
||||
color: #2aa198;
|
||||
}
|
||||
.punctuation.definition.string {
|
||||
color: #839496;
|
||||
}
|
||||
.meta.brace.square {
|
||||
color: #268bd2;
|
||||
}
|
||||
.punctuation.section.brackets {
|
||||
color: #268bd2;
|
||||
}
|
||||
.meta.brace.round {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.brace.curly {
|
||||
color: #657b83;
|
||||
}
|
||||
.punctuation.section {
|
||||
color: #657b83;
|
||||
}
|
||||
.punctuation.section.block {
|
||||
color: #657b83;
|
||||
}
|
||||
.punctuation.definition.parameters {
|
||||
color: #657b83;
|
||||
}
|
||||
.punctuation.section.group {
|
||||
color: #657b83;
|
||||
}
|
||||
.support.constant.color {
|
||||
color: #b58900;
|
||||
}
|
||||
.invalid.deprecated.color.w3c-non-standard-color-name.scss {
|
||||
color: #b58900;
|
||||
}
|
||||
.meta.selector.css {
|
||||
color: #657b83;
|
||||
}
|
||||
.entity.name.tag.css {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name.tag.scss {
|
||||
color: #b58900;
|
||||
}
|
||||
.source.less {
|
||||
color: #b58900;
|
||||
}
|
||||
.keyword.control.html.elements {
|
||||
color: #b58900;
|
||||
}
|
||||
.source.sass {
|
||||
color: #b58900;
|
||||
}
|
||||
.keyword.control.untitled {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.attribute-name.class {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.attribute-name.id {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.attribute-name.pseudo-element {
|
||||
color: #268bd2;
|
||||
}
|
||||
.entity.other.attribute-name.tag.pseudo-element {
|
||||
color: #268bd2;
|
||||
}
|
||||
.entity.other.attribute-name.pseudo-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
.entity.other.attribute-name.tag.pseudo-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.tag.other.html {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.tag.any.html {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.tag.block.any {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.tag.inline.any {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.meta.tag.structure.any.html {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #657b83;
|
||||
}
|
||||
.source.js.embedded.html {
|
||||
color: #657b83;
|
||||
}
|
||||
.punctuation.separator.key-value.html {
|
||||
color: #657b83;
|
||||
}
|
||||
.text.html.basic {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.attribute-name.html {
|
||||
color: #b58900;
|
||||
}
|
||||
.meta.tag.xml {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.other.attribute-name {
|
||||
color: #b58900;
|
||||
}
|
||||
.keyword.other.special-method.ruby {
|
||||
color: #859900;
|
||||
}
|
||||
.variable.other.constant.ruby {
|
||||
color: #b58900;
|
||||
}
|
||||
.constant.other.symbol.ruby {
|
||||
color: #2aa198;
|
||||
}
|
||||
.keyword.other.special-method.ruby {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.meta.array {
|
||||
color: #b58900;
|
||||
}
|
||||
.support.function.construct.php {
|
||||
color: #b58900;
|
||||
}
|
||||
.entity.name.function.preprocessor.c {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.meta.preprocessor.c.include {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.meta.preprocessor.macro.c {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.meta.preprocessor.c.include {
|
||||
color: #2aa198;
|
||||
}
|
||||
.string.quoted.other.lt-gt.include.c {
|
||||
color: #2aa198;
|
||||
}
|
||||
.meta.preprocessor.c.include {
|
||||
color: #2aa198;
|
||||
}
|
||||
.punctuation.definition.string.begin.c {
|
||||
color: #2aa198;
|
||||
}
|
||||
.meta.preprocessor.c.include {
|
||||
color: #2aa198;
|
||||
}
|
||||
.punctuation.definition.string.end.c {
|
||||
color: #2aa198;
|
||||
}
|
||||
.other.package.exclude {
|
||||
color: #dc322f;
|
||||
}
|
||||
.other.remove {
|
||||
color: #dc322f;
|
||||
}
|
||||
.other.add {
|
||||
color: #2aa198;
|
||||
}
|
||||
.punctuation.section.group.tex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.punctuation.definition.arguments.begin.latex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.punctuation.definition.arguments.end.latex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.punctuation.definition.arguments.latex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.meta.group.braces.tex {
|
||||
color: #b58900;
|
||||
}
|
||||
.string.other.math.tex {
|
||||
color: #b58900;
|
||||
}
|
||||
.variable.parameter.function.latex {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.punctuation.definition.constant.math.tex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.text.tex.latex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.math.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.general.math.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.general.math.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.character.math.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.string.other.math.tex {
|
||||
color: #b58900;
|
||||
}
|
||||
.punctuation.definition.string.begin.tex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.punctuation.definition.string.end.tex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.keyword.control.label.latex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.text.tex.latex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.general.math.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.variable.parameter.definition.label.latex {
|
||||
color: #dc322f;
|
||||
}
|
||||
.support.function.be.latex {
|
||||
color: #859900;
|
||||
}
|
||||
.support.function.section.latex {
|
||||
color: #cb4b16;
|
||||
}
|
||||
.support.function.general.tex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.keyword.control.ref.latex {
|
||||
color: #2aa198;
|
||||
}
|
||||
.storage.type.class.python {
|
||||
color: #859900;
|
||||
}
|
||||
.storage.type.function.python {
|
||||
color: #859900;
|
||||
}
|
||||
.storage.modifier.global.python {
|
||||
color: #859900;
|
||||
}
|
||||
.support.type.exception.python {
|
||||
color: #b58900;
|
||||
}
|
||||
.meta.scope.for-in-loop.shell {
|
||||
color: #586e75;
|
||||
}
|
||||
.variable.other.loop.shell {
|
||||
color: #586e75;
|
||||
}
|
||||
.meta.scope.case-block.shell {
|
||||
color: #586e75;
|
||||
}
|
||||
.meta.scope.case-body.shell {
|
||||
color: #586e75;
|
||||
}
|
||||
.punctuation.definition.logical-expression.shell {
|
||||
color: #dc322f;
|
||||
}
|
||||
.storage.modifier.c++ {
|
||||
color: #859900;
|
||||
}
|
||||
.support.function.perl {
|
||||
color: #268bd2;
|
||||
}
|
||||
.meta.diff {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.meta.diff.header {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.meta.diff.range {
|
||||
color: #268bd2;
|
||||
}
|
||||
.markup.deleted {
|
||||
color: #dc322f;
|
||||
}
|
||||
.markup.changed {
|
||||
color: #2aa198;
|
||||
}
|
||||
.markup.inserted {
|
||||
color: #859900;
|
||||
}
|
||||
.markup.heading {
|
||||
color: #b58900;
|
||||
}
|
||||
.punctuation.definition.heading.markdown {
|
||||
color: #b58900;
|
||||
}
|
||||
.markup.quote {
|
||||
color: #859900;
|
||||
}
|
||||
.markup.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.markup.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.markup.underline.link.markdown {
|
||||
color: #2aa198;
|
||||
}
|
||||
.meta.link.reference {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.reference.link.markdown {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.other.reference.link.markdown {
|
||||
color: #6c71c4;
|
||||
}
|
||||
.sublimelinter.notes {
|
||||
color: #eee8d5;
|
||||
}
|
||||
.sublimelinter.outline.illegal {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.sublimelinter.outline.warning {
|
||||
color: #839496;
|
||||
}
|
||||
.sublimelinter.outline.violation {
|
||||
color: #657b83;
|
||||
}
|
||||
.sublimelinter.mark.warning {
|
||||
color: #b58900;
|
||||
}
|
||||
.sublimelinter.mark.error {
|
||||
color: #dc322f;
|
||||
}
|
||||
.sublimelinter.gutter-mark {
|
||||
color: #657b83;
|
||||
}
|
||||
.brackethighlighter.all {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.entity.name.filename.find-in-files {
|
||||
color: #2aa198;
|
||||
}
|
||||
.constant.numeric.line-number.find-in-files {
|
||||
color: #93a1a1;
|
||||
}
|
||||
.markup.deleted.git_gutter {
|
||||
color: #dc322f;
|
||||
}
|
||||
.markup.inserted.git_gutter {
|
||||
color: #859900;
|
||||
}
|
||||
.markup.changed.git_gutter {
|
||||
color: #b58900;
|
||||
}
|
||||
.variable.other.readwrite.js {
|
||||
color: #657b83;
|
||||
}
|
||||
.variable.other.object.js {
|
||||
color: #657b83;
|
||||
}
|
||||
.variable.other.constant.js {
|
||||
color: #657b83;
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-select {
|
||||
/*-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;*/
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: royalblue;
|
||||
}
|
||||
|
||||
div.error_description {
|
||||
margin: 0 auto;
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
div.error_description > h2 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
header > hr {
|
||||
border: 0;
|
||||
border-top: 1px solid lightgray;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
header > ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header > ul > li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
header > ul > li > code {
|
||||
background-color: floralwhite;
|
||||
color: firebrick;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
header > ul > li > code > span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
nav > ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav > ul > li {
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
padding: 0 2ch;
|
||||
}
|
||||
|
||||
|
||||
form > textarea {
|
||||
background-color: white;
|
||||
border: 1px solid lightgray;
|
||||
border-radius: .5ch;
|
||||
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||
height: 400px;
|
||||
padding: .5ch;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
width: 120ch;
|
||||
}
|
||||
|
||||
form > p > input {
|
||||
background-color: white;
|
||||
border: 1px lightgray solid;
|
||||
border-radius: .5ch;
|
||||
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||
color: blue;
|
||||
padding: .5ch;
|
||||
}
|
||||
|
||||
form > p > input:hover {
|
||||
background-color: #fcfcfc;
|
||||
box-shadow: .1ch .1ch .25ch rgba(0, 0, 128, .45);
|
||||
color: royalblue;
|
||||
}
|
||||
|
||||
main {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main > div {
|
||||
overflow-wrap: break-word;
|
||||
background-color: white;
|
||||
border: 1px solid lightgray;
|
||||
border-radius: .5ch;
|
||||
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||
counter-reset: line;
|
||||
display: inline-block;
|
||||
padding: .5rem;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
main > div > div {
|
||||
border-bottom: 1px dashed #eee;
|
||||
}
|
||||
|
||||
main > div > div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
main > div > div:hover {
|
||||
background-color: floralwhite;
|
||||
}
|
||||
|
||||
main > div > div > div {
|
||||
display: inline-block;
|
||||
line-height: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
main > div > div > div.index {
|
||||
text-align: right;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
main > div > div > div.index > a {
|
||||
color: lightcoral;
|
||||
}
|
||||
|
||||
main > div > div > div.index > a:hover {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
main > div > div > div.line {
|
||||
white-space: pre-wrap;
|
||||
max-width: 80vw;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
padding: 0 !important;
|
||||
padding-left: 1ch !important;
|
||||
border-left: 1px solid lightgray;
|
||||
margin-left: 1ch;
|
||||
min-height: 1rem;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
span.hljs {
|
||||
padding: 0 !important;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>pastebucket :: {{id}}</title>
|
||||
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><a href="{{site_url|safe}}">pastebucket</a> :: <a href="/{{id}}">{{id}}</a></h1>
|
||||
<hr>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/{{id}}/raw">raw</a></li>
|
||||
<li><a href="/{{id}}/edit">edit</a></li>
|
||||
<li>{{syntax}}</li>
|
||||
<li>{{dt}}</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<form action="/" method="post">
|
||||
<textarea id="paste_area" autofocus name="paste" onkeydown="ctrl_enter(event);">{{text|safe}}</textarea>
|
||||
<p>
|
||||
<select name="lang">
|
||||
<option value="{{syntax}}" selected>{{syntax}}
|
||||
<option value="Plain Text">Plain Text
|
||||
{% for s in syntaxes %}
|
||||
<option value="{{s}}">{{s}}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
<p><input id="paste_button" type="submit" value="submit"></p>
|
||||
</form>
|
||||
<script>
|
||||
function ctrl_enter(e) {
|
||||
if (e.keyCode == 13 && e.ctrlKey) {
|
||||
document.getElementById("paste_button").click();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,50 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>pastebucket</title>
|
||||
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><a href="{{site_url|safe}}">pastebucket</a></h1>
|
||||
<hr>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
<span class="no-select"><command></span><span> | curl -T- https://p.rascul.xyz</span>
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
<span class="no-select">-- or --</span>
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
<span>curl -T- https://p.rascul.xyz < </span><span class="no-select"><file></span>
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
<form action="/" method="post">
|
||||
<textarea id="paste_area" autofocus name="paste" onkeydown="ctrl_enter(event);"></textarea>
|
||||
<p>
|
||||
<select name="lang">
|
||||
<option value="Plain Text">Plain Text
|
||||
{% for syntax in syntaxes %}
|
||||
<option value="{{syntax}}">{{syntax}}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
<p><input id="paste_button" type="submit" value="submit"></p>
|
||||
</form>
|
||||
<script>
|
||||
function ctrl_enter(e) {
|
||||
if (e.keyCode == 13 && e.ctrlKey) {
|
||||
document.getElementById("paste_button").click();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>pastebucket :: {{id}}</title>
|
||||
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||
<link rel="stylesheet" href="/s/code.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><a href="{{site_url|safe}}">pastebucket</a> :: <a href="/{{id}}">{{id}}</a></h1>
|
||||
<hr>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/{{id}}/raw">raw</a></li>
|
||||
<li><a href="/{{id}}/edit">edit</a></li>
|
||||
<li>{{syntax}}</li>
|
||||
<li>{{dt}}</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<div>
|
||||
{% for line in text_lines %}
|
||||
<div id="{{loop.index}}" onmouseover="document.getElementById('{{loop.index}}').firstElementChild.firstElementChild.style = 'color: red'" onmouseout="document.getElementById('{{loop.index}}').firstElementChild.firstElementChild.style = 'color: lightcoral'">
|
||||
<div class="index no-select" style="width: {{index_len}}ch"><a href="#{{loop.index}}">{{loop.index}}</a></div><div class="line">{{line|safe}}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in new issue