yo static files tho??

This commit is contained in:
Hamcha 2023-11-23 14:18:00 +01:00
parent 50f2c97d0c
commit 21dd617a4b
15 changed files with 28588 additions and 175 deletions

21
Cargo.lock generated
View file

@ -699,6 +699,25 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "include_dir"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
dependencies = [
"include_dir_macros",
]
[[package]]
name = "include_dir_macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@ -1253,6 +1272,8 @@ dependencies = [
"dotenvy",
"futures-util",
"git2",
"include_dir",
"mime_guess",
"rnix",
"serde",
"serde_json",

View file

@ -13,6 +13,8 @@ clap = { version = "4", features = ["env", "derive"] }
dotenvy = "0.15"
futures-util = "0.3"
git2 = { version = "0.18", default-features = false }
include_dir = "0.7"
mime_guess = "2"
rnix = "0.11"
serde = "1"
serde_json = "1"

29
src/route/assets.rs Normal file
View file

@ -0,0 +1,29 @@
use axum::{
body::{self, Empty, Full},
extract::Path,
http::{header, HeaderValue, Response, StatusCode},
response::IntoResponse,
};
use include_dir::{include_dir, Dir};
static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static");
pub(super) async fn static_path(Path(path): Path<String>) -> impl IntoResponse {
let path = path.trim_start_matches('/');
let mime_type = mime_guess::from_path(path).first_or_text_plain();
match STATIC_DIR.get_file(path) {
None => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(body::boxed(Empty::new()))
.unwrap(),
Some(file) => Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
HeaderValue::from_str(mime_type.as_ref()).unwrap(),
)
.body(body::boxed(Full::from(file.contents())))
.unwrap(),
}
}

View file

@ -13,6 +13,9 @@ use askama::Template;
use axum::{extract::State, response::IntoResponse, routing::get, Json, Router};
use serde_json::json;
use self::assets::static_path;
mod assets;
mod container;
mod stack;
@ -40,6 +43,7 @@ async fn get_sys_info() -> impl IntoResponse {
pub(crate) fn router() -> Router<AppState> {
Router::new()
.route("/static/*path", get(static_path))
.route("/", get(home))
.route("/sysinfo", get(get_sys_info))
.nest("/stack", stack::router())

31
static/css/editor.css Normal file
View file

@ -0,0 +1,31 @@
.editor form {
display: flex;
flex-direction: column;
gap: 1ch;
& .row {
display: flex;
gap: 0.5ch;
& input,
& button {
font-size: 13pt;
padding: 6px 10px;
&.wide {
padding: 6px 20px;
}
}
}
}
#editor {
border-width: 3px;
min-height: 50vh;
}
.ace_editor {
min-height: 50vh;
border: 3px solid #5958B1;
border-radius: 3px;
}

124
static/css/screen.css Normal file
View file

@ -0,0 +1,124 @@
:root {
--background: #13131E;
--bg-raised: #171625;
--text: #E0DFFE;
--link: #FFC53D;
--link-hover: #e28d0e;
--button-color: #B1A9FF;
--button-bg: #202248;
--button-border: #5B5BD6;
--button-bg-hover: #7E451D;
background-color: var(--background);
color: var(--text);
font-family: Inter, sans-serif;
scrollbar-width: thin;
}
code {
font-family: "Iosevka Web", monospace;
font-size: 11pt;
}
a[href],
a[href]:visited {
color: var(--link);
&:hover {
color: var(--link-hover);
}
}
body {
display: flex;
flex-direction: column;
}
nav {
display: flex;
width: 100%;
background-color: var(--bg-raised);
& a {
flex: 1;
display: flex;
text-decoration: none;
padding: 5px 8px;
justify-content: center;
max-width: 100px;
box-shadow: inset 0 -2px var(--button-bg);
font-size: 11pt;
text-transform: uppercase;
&[href],
&[href]:visited {
color: var(--button-color);
}
&:hover {
background-color: #1E160F;
box-shadow: inset 0 -2px var(--button-bg-hover);
}
}
}
main {
padding: 10px;
}
body,
html {
padding: 0;
margin: 0;
}
button {
background-color: var(--button-bg);
color: var(--button-color);
border: 1px solid var(--button-border);
padding: 4px 8px;
border-radius: 3px;
&:hover {
cursor: pointer;
background-color: var(--button-bg-hover);
color: var(--link-hover);
border-color: var(--link-hover);
}
}
table.table {
background-color: var(--bg-raised);
border: 3px solid #5958B1;
border-radius: 3px;
& th,
& td {
padding: 3px 5px;
}
& th {
font-weight: bold;
}
& thead {
background-color: #202248;
}
}
.full-width {
width: 100%;
flex: 1;
}
input,
textarea {
border: 1px solid #5958B1;
border-radius: 3px;
background-color: var(--bg-raised);
color: var(--text);
padding: 4px;
}

50
static/js/ace.mjs Normal file
View file

@ -0,0 +1,50 @@
import "../vendor/ace/ace.js";
import { findNearestParent } from "./node-utils.mjs";
export default class Editor {
/**
* Create a new editor
* @param {string} elementID ID of element to replace with the editor
* @param {boolean} [hookForm=true] Automatically add event to parent form (if any) to set the value on submit
*/
constructor(elementID, hookForm = true) {
/** @type {string} */
let originalElementName = null;
/** @type {HTMLFormElement} */
let parentForm = null;
if (hookForm) {
const el = document.getElementById(elementID);
if (!el) {
throw new Error("there is no element with the specified ID");
}
originalElementName = el.getAttribute("name");
if (originalElementName) {
// If it has a name it might have a form for a parent
parentForm = findNearestParent(el, (parent) => parent.tagName == "FORM");
}
}
// Create editor
ace.config.set('basePath', '/static/vendor/ace')
this.editor = ace.edit(elementID);
// todo make this stuff customizable??
this.editor.setTheme("ace/theme/dracula");
this.editor.setOptions({
fontFamily: "Iosevka Web",
fontSize: "12pt",
});
this.editor.getSession().setUseWrapMode(true);
this.editor.setKeyboardHandler("ace/keyboard/vim");
this.editor.session.setMode("ace/mode/nix");
if (parentForm !== null) {
parentForm.addEventListener("formdata", (ev) => {
ev.formData.set(originalElementName, this.editor.getValue());
});
}
}
}

17
static/js/node-utils.mjs Normal file
View file

@ -0,0 +1,17 @@
/**
* Find nearest parent that satisfies a requirement
* @param {Node|Element} node
* @param {(el: Element) => boolean} findFn
* @returns {HTMLElement | null} Found element, or null if no parent matches
*/
export function findNearestParent(node, findFn) {
let parent = node.parentElement;
while (parent) {
if (findFn(parent)) {
return parent;
}
parent = parent.parentElement;
}
return null;
}

21017
static/vendor/ace/ace.js vendored Normal file

File diff suppressed because one or more lines are too long

6681
static/vendor/ace/keybinding-vim.js vendored Normal file

File diff suppressed because it is too large Load diff

555
static/vendor/ace/mode-nix.js vendored Normal file
View file

@ -0,0 +1,555 @@
ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var DocCommentHighlightRules = function () {
this.$rules = {
"start": [
{
token: "comment.doc.tag",
regex: "@\\w+(?=\\s|$)"
}, DocCommentHighlightRules.getTagRule(), {
defaultToken: "comment.doc",
caseInsensitive: true
}
]
};
};
oop.inherits(DocCommentHighlightRules, TextHighlightRules);
DocCommentHighlightRules.getTagRule = function (start) {
return {
token: "comment.doc.tag.storage.type",
regex: "\\b(?:TODO|FIXME|XXX|HACK)\\b"
};
};
DocCommentHighlightRules.getStartRule = function (start) {
return {
token: "comment.doc",
regex: "\\/\\*(?=\\*)",
next: start
};
};
DocCommentHighlightRules.getEndRule = function (start) {
return {
token: "comment.doc",
regex: "\\*\\/",
next: start
};
};
exports.DocCommentHighlightRules = DocCommentHighlightRules;
});
ace.define("ace/mode/c_cpp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var DocCommentHighlightRules = require("./doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var cFunctions = exports.cFunctions = "hypot|hypotf|hypotl|sscanf|system|snprintf|scanf|scalbn|scalbnf|scalbnl|scalbln|scalblnf|scalblnl|sin|sinh|sinhf|sinhl|sinf|sinl|signal|signbit|strstr|strspn|strncpy|strncat|strncmp|strcspn|strchr|strcoll|strcpy|strcat|strcmp|strtoimax|strtod|strtoul|strtoull|strtoumax|strtok|strtof|strtol|strtold|strtoll|strerror|strpbrk|strftime|strlen|strrchr|strxfrm|sprintf|setjmp|setvbuf|setlocale|setbuf|sqrt|sqrtf|sqrtl|swscanf|swprintf|srand|nearbyint|nearbyintf|nearbyintl|nexttoward|nexttowardf|nexttowardl|nextafter|nextafterf|nextafterl|nan|nanf|nanl|csin|csinh|csinhf|csinhl|csinf|csinl|csqrt|csqrtf|csqrtl|ccos|ccosh|ccoshf|ccosf|ccosl|cimag|cimagf|cimagl|ctime|ctan|ctanh|ctanhf|ctanhl|ctanf|ctanl|cos|cosh|coshf|coshl|cosf|cosl|conj|conjf|conjl|copysign|copysignf|copysignl|cpow|cpowf|cpowl|cproj|cprojf|cprojl|ceil|ceilf|ceill|cexp|cexpf|cexpl|clock|clog|clogf|clogl|clearerr|casin|casinh|casinhf|casinhl|casinf|casinl|cacos|cacosh|cacoshf|cacoshl|cacosf|cacosl|catan|catanh|catanhf|catanhl|catanf|catanl|calloc|carg|cargf|cargl|cabs|cabsf|cabsl|creal|crealf|creall|cbrt|cbrtf|cbrtl|time|toupper|tolower|tan|tanh|tanhf|tanhl|tanf|tanl|trunc|truncf|truncl|tgamma|tgammaf|tgammal|tmpnam|tmpfile|isspace|isnormal|isnan|iscntrl|isinf|isdigit|isunordered|isupper|ispunct|isprint|isfinite|iswspace|iswcntrl|iswctype|iswdigit|iswupper|iswpunct|iswprint|iswlower|iswalnum|iswalpha|iswgraph|iswxdigit|iswblank|islower|isless|islessequal|islessgreater|isalnum|isalpha|isgreater|isgreaterequal|isgraph|isxdigit|isblank|ilogb|ilogbf|ilogbl|imaxdiv|imaxabs|div|difftime|_Exit|ungetc|ungetwc|pow|powf|powl|puts|putc|putchar|putwc|putwchar|perror|printf|erf|erfc|erfcf|erfcl|erff|erfl|exit|exp|exp2|exp2f|exp2l|expf|expl|expm1|expm1f|expm1l|vsscanf|vsnprintf|vscanf|vsprintf|vswscanf|vswprintf|vprintf|vfscanf|vfprintf|vfwscanf|vfwprintf|vwscanf|vwprintf|va_start|va_copy|va_end|va_arg|qsort|fscanf|fsetpos|fseek|fclose|ftell|fopen|fdim|fdimf|fdiml|fpclassify|fputs|fputc|fputws|fputwc|fprintf|feholdexcept|fesetenv|fesetexceptflag|fesetround|feclearexcept|fetestexcept|feof|feupdateenv|feraiseexcept|ferror|fegetenv|fegetexceptflag|fegetround|fflush|fwscanf|fwide|fwprintf|fwrite|floor|floorf|floorl|fabs|fabsf|fabsl|fgets|fgetc|fgetpos|fgetws|fgetwc|freopen|free|fread|frexp|frexpf|frexpl|fmin|fminf|fminl|fmod|fmodf|fmodl|fma|fmaf|fmal|fmax|fmaxf|fmaxl|ldiv|ldexp|ldexpf|ldexpl|longjmp|localtime|localeconv|log|log1p|log1pf|log1pl|log10|log10f|log10l|log2|log2f|log2l|logf|logl|logb|logbf|logbl|labs|lldiv|llabs|llrint|llrintf|llrintl|llround|llroundf|llroundl|lrint|lrintf|lrintl|lround|lroundf|lroundl|lgamma|lgammaf|lgammal|wscanf|wcsstr|wcsspn|wcsncpy|wcsncat|wcsncmp|wcscspn|wcschr|wcscoll|wcscpy|wcscat|wcscmp|wcstoimax|wcstod|wcstoul|wcstoull|wcstoumax|wcstok|wcstof|wcstol|wcstold|wcstoll|wcstombs|wcspbrk|wcsftime|wcslen|wcsrchr|wcsrtombs|wcsxfrm|wctob|wctomb|wcrtomb|wprintf|wmemset|wmemchr|wmemcpy|wmemcmp|wmemmove|assert|asctime|asin|asinh|asinhf|asinhl|asinf|asinl|acos|acosh|acoshf|acoshl|acosf|acosl|atoi|atof|atol|atoll|atexit|atan|atanh|atanhf|atanhl|atan2|atan2f|atan2l|atanf|atanl|abs|abort|gets|getc|getchar|getenv|getwc|getwchar|gmtime|rint|rintf|rintl|round|roundf|roundl|rename|realloc|rewind|remove|remquo|remquof|remquol|remainder|remainderf|remainderl|rand|raise|bsearch|btowc|modf|modff|modfl|memset|memchr|memcpy|memcmp|memmove|mktime|malloc|mbsinit|mbstowcs|mbsrtowcs|mbtowc|mblen|mbrtowc|mbrlen";
var c_cppHighlightRules = function (extraKeywords) {
var keywordControls = ("break|case|continue|default|do|else|for|goto|if|_Pragma|" +
"return|switch|while|catch|operator|try|throw|using");
var storageType = ("asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|" +
"_Imaginary|int|int8_t|int16_t|int32_t|int64_t|long|short|signed|size_t|struct|typedef|uint8_t|uint16_t|uint32_t|uint64_t|union|unsigned|void|" +
"class|wchar_t|template|char16_t|char32_t");
var storageModifiers = ("const|extern|register|restrict|static|volatile|inline|private|" +
"protected|public|friend|explicit|virtual|export|mutable|typename|" +
"constexpr|new|delete|alignas|alignof|decltype|noexcept|thread_local");
var keywordOperators = ("and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|" +
"const_cast|dynamic_cast|reinterpret_cast|static_cast|sizeof|namespace");
var builtinConstants = ("NULL|true|false|TRUE|FALSE|nullptr");
var keywordMapper = this.$keywords = this.createKeywordMapper(Object.assign({
"keyword.control": keywordControls,
"storage.type": storageType,
"storage.modifier": storageModifiers,
"keyword.operator": keywordOperators,
"variable.language": "this",
"constant.language": builtinConstants,
"support.function.C99.c": cFunctions
}, extraKeywords), "identifier");
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b";
var escapeRe = /\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}U[a-fA-F\d]{8}|.)/.source;
var formatRe = "%"
+ /(\d+\$)?/.source // field (argument #)
+ /[#0\- +']*/.source // flags
+ /[,;:_]?/.source // separator character (AltiVec)
+ /((-?\d+)|\*(-?\d+\$)?)?/.source // minimum field width
+ /(\.((-?\d+)|\*(-?\d+\$)?)?)?/.source // precision
+ /(hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)?/.source // length modifier
+ /(\[[^"\]]+\]|[diouxXDOUeEfFgGaACcSspn%])/.source; // conversion type
this.$rules = {
"start": [
{
token: "comment",
regex: "//$",
next: "start"
}, {
token: "comment",
regex: "//",
next: "singleLineComment"
},
DocCommentHighlightRules.getStartRule("doc-start"),
{
token: "comment",
regex: "\\/\\*",
next: "comment"
}, {
token: "string",
regex: "'(?:" + escapeRe + "|.)?'"
}, {
token: "string.start",
regex: '"',
stateName: "qqstring",
next: [
{ token: "string", regex: /\\\s*$/, next: "qqstring" },
{ token: "constant.language.escape", regex: escapeRe },
{ token: "constant.language.escape", regex: formatRe },
{ token: "string.end", regex: '"|$', next: "start" },
{ defaultToken: "string" }
]
}, {
token: "string.start",
regex: 'R"\\(',
stateName: "rawString",
next: [
{ token: "string.end", regex: '\\)"', next: "start" },
{ defaultToken: "string" }
]
}, {
token: "constant.numeric",
regex: "0[xX][0-9a-fA-F]+(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"
}, {
token: "constant.numeric",
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"
}, {
token: "keyword",
regex: "#\\s*(?:include|import|pragma|line|define|undef)\\b",
next: "directive"
}, {
token: "keyword",
regex: "#\\s*(?:endif|if|ifdef|else|elif|ifndef)\\b"
}, {
token: keywordMapper,
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*"
}, {
token: "keyword.operator",
regex: /--|\+\+|<<=|>>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/
}, {
token: "punctuation.operator",
regex: "\\?|\\:|\\,|\\;|\\."
}, {
token: "paren.lparen",
regex: "[[({]"
}, {
token: "paren.rparen",
regex: "[\\])}]"
}, {
token: "text",
regex: "\\s+"
}
],
"comment": [
{
token: "comment",
regex: "\\*\\/",
next: "start"
}, {
defaultToken: "comment"
}
],
"singleLineComment": [
{
token: "comment",
regex: /\\$/,
next: "singleLineComment"
}, {
token: "comment",
regex: /$/,
next: "start"
}, {
defaultToken: "comment"
}
],
"directive": [
{
token: "constant.other.multiline",
regex: /\\/
},
{
token: "constant.other.multiline",
regex: /.*\\/
},
{
token: "constant.other",
regex: "\\s*<.+?>",
next: "start"
},
{
token: "constant.other",
regex: '\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',
next: "start"
},
{
token: "constant.other",
regex: "\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",
next: "start"
},
{
token: "constant.other",
regex: /[^\\\/]+/,
next: "start"
}
]
};
this.embedRules(DocCommentHighlightRules, "doc-", [DocCommentHighlightRules.getEndRule("start")]);
this.normalizeRules();
};
oop.inherits(c_cppHighlightRules, TextHighlightRules);
exports.c_cppHighlightRules = c_cppHighlightRules;
});
ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(require, exports, module){"use strict";
var Range = require("../range").Range;
var MatchingBraceOutdent = function () { };
(function () {
this.checkOutdent = function (line, input) {
if (!/^\s+$/.test(line))
return false;
return /^\s*\}/.test(input);
};
this.autoOutdent = function (doc, row) {
var line = doc.getLine(row);
var match = line.match(/^(\s*\})/);
if (!match)
return 0;
var column = match[1].length;
var openBracePos = doc.findMatchingBracket({ row: row, column: column });
if (!openBracePos || openBracePos.row == row)
return 0;
var indent = this.$getIndent(doc.getLine(openBracePos.row));
doc.replace(new Range(row, 0, row, column - 1), indent);
};
this.$getIndent = function (line) {
return line.match(/^\s*/)[0];
};
}).call(MatchingBraceOutdent.prototype);
exports.MatchingBraceOutdent = MatchingBraceOutdent;
});
ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(require, exports, module){"use strict";
var oop = require("../../lib/oop");
var Range = require("../../range").Range;
var BaseFoldMode = require("./fold_mode").FoldMode;
var FoldMode = exports.FoldMode = function (commentRegex) {
if (commentRegex) {
this.foldingStartMarker = new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start));
this.foldingStopMarker = new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end));
}
};
oop.inherits(FoldMode, BaseFoldMode);
(function () {
this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/;
this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/;
this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/;
this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
this._getFoldWidgetBase = this.getFoldWidget;
this.getFoldWidget = function (session, foldStyle, row) {
var line = session.getLine(row);
if (this.singleLineBlockCommentRe.test(line)) {
if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
return "";
}
var fw = this._getFoldWidgetBase(session, foldStyle, row);
if (!fw && this.startRegionRe.test(line))
return "start"; // lineCommentRegionStart
return fw;
};
this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) {
var line = session.getLine(row);
if (this.startRegionRe.test(line))
return this.getCommentRegionBlock(session, line, row);
var match = line.match(this.foldingStartMarker);
if (match) {
var i = match.index;
if (match[1])
return this.openingBracketBlock(session, match[1], row, i);
var range = session.getCommentFoldRange(row, i + match[0].length, 1);
if (range && !range.isMultiLine()) {
if (forceMultiline) {
range = this.getSectionRange(session, row);
}
else if (foldStyle != "all")
range = null;
}
return range;
}
if (foldStyle === "markbegin")
return;
var match = line.match(this.foldingStopMarker);
if (match) {
var i = match.index + match[0].length;
if (match[1])
return this.closingBracketBlock(session, match[1], row, i);
return session.getCommentFoldRange(row, i, -1);
}
};
this.getSectionRange = function (session, row) {
var line = session.getLine(row);
var startIndent = line.search(/\S/);
var startRow = row;
var startColumn = line.length;
row = row + 1;
var endRow = row;
var maxRow = session.getLength();
while (++row < maxRow) {
line = session.getLine(row);
var indent = line.search(/\S/);
if (indent === -1)
continue;
if (startIndent > indent)
break;
var subRange = this.getFoldWidgetRange(session, "all", row);
if (subRange) {
if (subRange.start.row <= startRow) {
break;
}
else if (subRange.isMultiLine()) {
row = subRange.end.row;
}
else if (startIndent == indent) {
break;
}
}
endRow = row;
}
return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
};
this.getCommentRegionBlock = function (session, line, row) {
var startColumn = line.search(/\s*$/);
var maxRow = session.getLength();
var startRow = row;
var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
var depth = 1;
while (++row < maxRow) {
line = session.getLine(row);
var m = re.exec(line);
if (!m)
continue;
if (m[1])
depth--;
else
depth++;
if (!depth)
break;
}
var endRow = row;
if (endRow > startRow) {
return new Range(startRow, startColumn, endRow, line.length);
}
};
}).call(FoldMode.prototype);
});
ace.define("ace/mode/c_cpp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/c_cpp_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/cstyle"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var c_cppHighlightRules = require("./c_cpp_highlight_rules").c_cppHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
var Mode = function () {
this.HighlightRules = c_cppHighlightRules;
this.$outdent = new MatchingBraceOutdent();
this.$behaviour = this.$defaultBehaviour;
this.foldingRules = new CStyleFoldMode();
};
oop.inherits(Mode, TextMode);
(function () {
this.lineCommentStart = "//";
this.blockComment = { start: "/*", end: "*/" };
this.getNextLineIndent = function (state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length - 1].type == "comment") {
return indent;
}
if (state == "start") {
var match = line.match(/^.*[\{\(\[]\s*$/);
if (match) {
indent += tab;
}
}
else if (state == "doc-start") {
if (endState == "start") {
return "";
}
var match = line.match(/^\s*(\/?)\*/);
if (match) {
if (match[1]) {
indent += " ";
}
indent += "* ";
}
}
return indent;
};
this.checkOutdent = function (state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function (state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
this.$id = "ace/mode/c_cpp";
this.snippetFileId = "ace/snippets/c_cpp";
}).call(Mode.prototype);
exports.Mode = Mode;
});
ace.define("ace/mode/nix_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var NixHighlightRules = function () {
var constantLanguage = "true|false";
var keywordControl = "with|import|if|else|then|inherit";
var keywordDeclaration = "let|in|rec";
var keywordMapper = this.createKeywordMapper({
"constant.language.nix": constantLanguage,
"keyword.control.nix": keywordControl,
"keyword.declaration.nix": keywordDeclaration
}, "identifier");
this.$rules = {
"start": [{
token: "comment",
regex: /#.*$/
}, {
token: "comment",
regex: /\/\*/,
next: "comment"
}, {
token: "constant",
regex: "<[^>]+>"
}, {
regex: "(==|!=|<=?|>=?)",
token: ["keyword.operator.comparison.nix"]
}, {
regex: "((?:[+*/%-]|\\~)=)",
token: ["keyword.operator.assignment.arithmetic.nix"]
}, {
regex: "=",
token: "keyword.operator.assignment.nix"
}, {
token: "string",
regex: "''",
next: "qqdoc"
}, {
token: "string",
regex: "'",
next: "qstring"
}, {
token: "string",
regex: '"',
push: "qqstring"
}, {
token: "constant.numeric",
regex: "0[xX][0-9a-fA-F]+\\b"
}, {
token: "constant.numeric",
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token: keywordMapper,
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
regex: "}",
token: function (val, start, stack) {
return stack[1] && stack[1].charAt(0) == "q" ? "constant.language.escape" : "text";
},
next: "pop"
}],
"comment": [{
token: "comment",
regex: "\\*\\/",
next: "start"
}, {
defaultToken: "comment"
}],
"qqdoc": [
{
token: "constant.language.escape",
regex: /\$\{/,
push: "start"
}, {
token: "string",
regex: "''",
next: "pop"
}, {
defaultToken: "string"
}
],
"qqstring": [
{
token: "constant.language.escape",
regex: /\$\{/,
push: "start"
}, {
token: "string",
regex: '"',
next: "pop"
}, {
defaultToken: "string"
}
],
"qstring": [
{
token: "constant.language.escape",
regex: /\$\{/,
push: "start"
}, {
token: "string",
regex: "'",
next: "pop"
}, {
defaultToken: "string"
}
]
};
this.normalizeRules();
};
oop.inherits(NixHighlightRules, TextHighlightRules);
exports.NixHighlightRules = NixHighlightRules;
});
ace.define("ace/mode/nix",["require","exports","module","ace/lib/oop","ace/mode/c_cpp","ace/mode/nix_highlight_rules","ace/mode/folding/cstyle"], function(require, exports, module){/*
THIS FILE WAS AUTOGENERATED BY mode.tmpl.js
*/
"use strict";
var oop = require("../lib/oop");
var CMode = require("./c_cpp").Mode;
var NixHighlightRules = require("./nix_highlight_rules").NixHighlightRules;
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
var Mode = function () {
CMode.call(this);
this.HighlightRules = NixHighlightRules;
this.foldingRules = new CStyleFoldMode();
this.$behaviour = this.$defaultBehaviour;
};
oop.inherits(Mode, CMode);
(function () {
this.lineCommentStart = "#";
this.blockComment = { start: "/*", end: "*/" };
this.$id = "ace/mode/nix";
}).call(Mode.prototype);
exports.Mode = Mode;
}); (function() {
ace.require(["ace/mode/nix"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

19
static/vendor/ace/theme-dracula.js vendored Normal file
View file

@ -0,0 +1,19 @@
ace.define("ace/theme/dracula-css",["require","exports","module"], function(require, exports, module){module.exports = "/*\n * Copyright \u00A9 2017 Zeno Rocha <hi@zenorocha.com>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \u201CSoftware\u201D), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \u201CAS IS\u201D, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n.ace-dracula .ace_gutter {\n background: #282a36;\n color: rgb(144,145,148)\n}\n\n.ace-dracula .ace_print-margin {\n width: 1px;\n background: #44475a\n}\n\n.ace-dracula {\n background-color: #282a36;\n color: #f8f8f2\n}\n\n.ace-dracula .ace_cursor {\n color: #f8f8f0\n}\n\n.ace-dracula .ace_marker-layer .ace_selection {\n background: #44475a\n}\n\n.ace-dracula.ace_multiselect .ace_selection.ace_start {\n box-shadow: 0 0 3px 0px #282a36;\n border-radius: 2px\n}\n\n.ace-dracula .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174)\n}\n\n.ace-dracula .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #a29709\n}\n\n.ace-dracula .ace_marker-layer .ace_active-line {\n background: #44475a\n}\n\n.ace-dracula .ace_gutter-active-line {\n background-color: #44475a\n}\n\n.ace-dracula .ace_marker-layer .ace_selected-word {\n box-shadow: 0px 0px 0px 1px #a29709;\n border-radius: 3px;\n}\n\n.ace-dracula .ace_fold {\n background-color: #50fa7b;\n border-color: #f8f8f2\n}\n\n.ace-dracula .ace_keyword {\n color: #ff79c6\n}\n\n.ace-dracula .ace_constant.ace_language {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_numeric {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_character {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_character.ace_escape {\n color: #ff79c6\n}\n\n.ace-dracula .ace_constant.ace_other {\n color: #bd93f9\n}\n\n.ace-dracula .ace_support.ace_function {\n color: #8be9fd\n}\n\n.ace-dracula .ace_support.ace_constant {\n color: #6be5fd\n}\n\n.ace-dracula .ace_support.ace_class {\n font-style: italic;\n color: #66d9ef\n}\n\n.ace-dracula .ace_support.ace_type {\n font-style: italic;\n color: #66d9ef\n}\n\n.ace-dracula .ace_storage {\n color: #ff79c6\n}\n\n.ace-dracula .ace_storage.ace_type {\n font-style: italic;\n color: #8be9fd\n}\n\n.ace-dracula .ace_invalid {\n color: #F8F8F0;\n background-color: #ff79c6\n}\n\n.ace-dracula .ace_invalid.ace_deprecated {\n color: #F8F8F0;\n background-color: #bd93f9\n}\n\n.ace-dracula .ace_string {\n color: #f1fa8c\n}\n\n.ace-dracula .ace_comment {\n color: #6272a4\n}\n\n.ace-dracula .ace_variable {\n color: #50fa7b\n}\n\n.ace-dracula .ace_variable.ace_parameter {\n font-style: italic;\n color: #ffb86c\n}\n\n.ace-dracula .ace_entity.ace_other.ace_attribute-name {\n color: #50fa7b\n}\n\n.ace-dracula .ace_entity.ace_name.ace_function {\n color: #50fa7b\n}\n\n.ace-dracula .ace_entity.ace_name.ace_tag {\n color: #ff79c6\n}\n.ace-dracula .ace_invisible {\n color: #626680;\n}\n\n.ace-dracula .ace_indent-guide {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y\n}\n\n.ace-dracula .ace_indent-guide-active {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACAQMAAACjTyRkAAAABlBMVEUAAADCwsK76u2xAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjYGBoAAAAhACBGFbxzQAAAABJRU5ErkJggg==\") right repeat-y;\n}\n";
});
ace.define("ace/theme/dracula",["require","exports","module","ace/theme/dracula-css","ace/lib/dom"], function(require, exports, module){exports.isDark = true;
exports.cssClass = "ace-dracula";
exports.cssText = require("./dracula-css");
exports.$selectionColorConflict = true;
var dom = require("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass, false);
}); (function() {
ace.require(["ace/theme/dracula"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View file

@ -8,123 +8,7 @@
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<link rel="stylesheet" href="https://iosevka-webfonts.github.io/iosevka/iosevka.css">
<style>
:root {
--background: #13131E;
--bg-raised: #171625;
--text: #E0DFFE;
--link: #FFC53D;
--link-hover: #e28d0e;
--button-color: #B1A9FF;
--button-bg: #202248;
--button-border: #5B5BD6;
--button-bg-hover: #7E451D;
background-color: var(--background);
color: var(--text);
font-family: Inter, sans-serif;
scrollbar-width: thin;
}
code {
font-family: "Iosevka Web", monospace;
font-size: 11pt;
}
a[href],
a[href]:visited {
color: var(--link);
&:hover {
color: var(--link-hover);
}
}
body {
display: flex;
flex-direction: column;
}
nav {
display: flex;
width: 100%;
background-color: var(--bg-raised);
& a {
flex: 1;
display: flex;
text-decoration: none;
padding: 5px 8px;
justify-content: center;
max-width: 100px;
box-shadow: inset 0 -2px var(--button-bg);
font-size: 11pt;
text-transform: uppercase;
&[href],
&[href]:visited {
color: var(--button-color);
}
&:hover {
background-color: #1E160F;
box-shadow: inset 0 -2px var(--button-bg-hover);
}
}
}
main {
padding: 10px;
}
body,
html {
padding: 0;
margin: 0;
}
button {
background-color: var(--button-bg);
color: var(--button-color);
border: 1px solid var(--button-border);
padding: 4px 8px;
border-radius: 3px;
&:hover {
cursor: pointer;
background-color: var(--button-bg-hover);
color: var(--link-hover);
border-color: var(--link-hover);
}
}
table.table {
background-color: var(--bg-raised);
border: 3px solid #5958B1;
border-radius: 3px;
& th,
& td {
padding: 3px 5px;
}
& th {
font-weight: bold;
}
& thead {
background-color: #202248;
}
}
.full-width {
width: 100%;
flex: 1;
}
</style>
<link rel="stylesheet" href="/static/css/screen.css">
</head>
<body>

View file

@ -47,6 +47,7 @@
</section>
</main>
<link rel="stylesheet" href="/static/css/editor.css" />
<style scoped>
main {
display: flex;
@ -84,48 +85,6 @@
color: #E796F3;
}
.editor form {
display: flex;
flex-direction: column;
gap: 0.5ch;
& .row {
display: flex;
gap: 0.5ch;
& input,
& button {
font-size: 13pt;
padding: 6px 10px;
&.wide {
padding: 6px 20px;
}
}
}
}
input,
textarea {
border: 1px solid #5958B1;
border-radius: 3px;
background-color: var(--bg-raised);
color: var(--text);
padding: 4px;
}
#editor {
border-width: 3px;
min-height: 50vh;
}
.ace_editor {
min-height: 50vh;
border: 3px solid #5958B1;
border-radius: 3px;
}
.actions {
display: flex;
gap: 0.5rem;
@ -141,23 +100,11 @@
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.31.2/ace.min.js"
integrity="sha512-4qIbBlcJOvbOmEB50FfnviJ9jOCen2yhi4skodkbTLU/uJJdULPxlX2R9Enf1oOBleUK9KD3fGmMcnItFQdNeA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/dracula");
editor.setOptions({
fontFamily: "Iosevka Web",
fontSize: "12pt"
});
editor.getSession().setUseWrapMode(true);
editor.setKeyboardHandler("ace/keyboard/vim");
editor.session.setMode("ace/mode/nix");
document.getElementById("editor-form").addEventListener("formdata", (ev) => {
ev.formData.set("source", editor.getValue());
});
<script type="module">
import Editor from "/static/js/ace.mjs";
new Editor("editor");
</script>
{% endblock %}

View file

@ -3,4 +3,36 @@
{% block title %}New stack{% endblock %}
{% block content %}
<main>
<header>
<h1>New stack</h1>
</header>
<section class="editor">
<form method="POST" action="./edit" id="editor-form">
<div class="row">
<input style="flex:1" name="name" type="text" placeholder="Stack name" />
</div>
<textarea name="source" id="editor">{}</textarea>
<div class="row">
<button type="submit">Create & Deploy</button>
</div>
</form>
</section>
</main>
<link rel="stylesheet" href="/static/css/editor.css" />
<style scoped>
main {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<script type="module">
import Editor from "/static/js/ace.mjs";
new Editor("editor");
</script>
{% endblock %}