miqro 7.2.7 → 7.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/build/esm/editor/auth.d.ts +6 -0
  2. package/build/esm/editor/auth.js +42 -0
  3. package/build/esm/editor/common/constants.d.ts +4 -0
  4. package/build/esm/editor/common/constants.js +20 -0
  5. package/build/esm/editor/common/constants.server.d.ts +2 -0
  6. package/build/esm/editor/common/constants.server.js +4 -0
  7. package/build/esm/editor/common/editor-index.d.ts +2 -0
  8. package/build/esm/editor/common/editor-index.js +14 -0
  9. package/build/esm/editor/common/html-encode.d.ts +1 -0
  10. package/build/esm/editor/common/html-encode.js +14 -0
  11. package/build/esm/editor/common/log-socket.d.ts +15 -0
  12. package/build/esm/editor/common/log-socket.js +71 -0
  13. package/build/esm/editor/common/templates.d.ts +11 -0
  14. package/build/esm/editor/common/templates.js +477 -0
  15. package/build/esm/editor/components/api-preview.d.ts +11 -0
  16. package/build/esm/editor/components/api-preview.js +92 -0
  17. package/build/esm/editor/components/editor.d.ts +16 -0
  18. package/build/esm/editor/components/editor.js +367 -0
  19. package/build/esm/editor/components/file-browser.d.ts +37 -0
  20. package/build/esm/editor/components/file-browser.js +127 -0
  21. package/build/esm/editor/components/file-editor-toolbar.d.ts +22 -0
  22. package/build/esm/editor/components/file-editor-toolbar.js +95 -0
  23. package/build/esm/editor/components/file-editor.d.ts +32 -0
  24. package/build/esm/editor/components/file-editor.js +61 -0
  25. package/build/esm/editor/components/filter-query.d.ts +1 -0
  26. package/build/esm/editor/components/filter-query.js +23 -0
  27. package/build/esm/editor/components/highlight-text-area.d.ts +11 -0
  28. package/build/esm/editor/components/highlight-text-area.js +127 -0
  29. package/build/esm/editor/components/log-viewer.d.ts +6 -0
  30. package/build/esm/editor/components/log-viewer.js +71 -0
  31. package/build/esm/editor/components/new-file.d.ts +10 -0
  32. package/build/esm/editor/components/new-file.js +119 -0
  33. package/build/esm/editor/components/scroll-query.d.ts +7 -0
  34. package/build/esm/editor/components/scroll-query.js +22 -0
  35. package/build/esm/editor/components/start-page.d.ts +13 -0
  36. package/build/esm/editor/components/start-page.js +32 -0
  37. package/build/esm/editor/http/admin/editor/api/fs/delete.api.d.ts +4 -0
  38. package/build/esm/editor/http/admin/editor/api/fs/delete.api.js +30 -0
  39. package/build/esm/editor/http/admin/editor/api/fs/read.api.d.ts +7 -0
  40. package/build/esm/editor/http/admin/editor/api/fs/read.api.js +50 -0
  41. package/build/esm/editor/http/admin/editor/api/fs/rename.api.d.ts +7 -0
  42. package/build/esm/editor/http/admin/editor/api/fs/rename.api.js +40 -0
  43. package/build/esm/editor/http/admin/editor/api/fs/scan.api.d.ts +26 -0
  44. package/build/esm/editor/http/admin/editor/api/fs/scan.api.js +150 -0
  45. package/build/esm/editor/http/admin/editor/api/fs/write.api.d.ts +3 -0
  46. package/build/esm/editor/http/admin/editor/api/fs/write.api.js +39 -0
  47. package/build/esm/editor/http/admin/editor/api/server/reload.api.d.ts +10 -0
  48. package/build/esm/editor/http/admin/editor/api/server/reload.api.js +46 -0
  49. package/build/esm/editor/http/admin/editor/api/server/restart.api.d.ts +10 -0
  50. package/build/esm/editor/http/admin/editor/api/server/restart.api.js +46 -0
  51. package/build/esm/editor/http/admin/editor/editor.d.ts +1 -0
  52. package/build/esm/editor/http/admin/editor/editor.js +8 -0
  53. package/build/esm/editor/http/admin/editor/index.api.d.ts +3 -0
  54. package/build/esm/editor/http/admin/editor/index.api.js +23 -0
  55. package/build/esm/editor/server.d.ts +3 -0
  56. package/build/esm/editor/server.js +49 -0
  57. package/build/esm/editor/ws.d.ts +3 -0
  58. package/build/esm/editor/ws.js +12 -0
  59. package/build/esm/src/common/admin-interface.d.ts +36 -0
  60. package/build/esm/src/common/admin-interface.js +44 -0
  61. package/build/esm/src/services/app.d.ts +1 -1
  62. package/build/esm/src/services/editor.d.ts +1 -1
  63. package/build/esm/src/services/utils/admin-interface.d.ts +1 -1
  64. package/build/esm/src/services/utils/websocketmanager.d.ts +1 -1
  65. package/editor/auth.ts +52 -0
  66. package/editor/common/constants.server.ts +5 -0
  67. package/editor/common/constants.ts +21 -0
  68. package/editor/common/editor-index.tsx +17 -0
  69. package/editor/common/html-encode.ts +14 -0
  70. package/editor/common/log-socket.tsx +87 -0
  71. package/editor/common/templates.ts +481 -0
  72. package/editor/components/api-preview.tsx +118 -0
  73. package/editor/components/editor.tsx +496 -0
  74. package/editor/components/file-browser.tsx +311 -0
  75. package/editor/components/file-editor-toolbar.tsx +194 -0
  76. package/editor/components/file-editor.tsx +125 -0
  77. package/editor/components/filter-query.tsx +26 -0
  78. package/editor/components/highlight-text-area.tsx +148 -0
  79. package/editor/components/log-viewer.tsx +113 -0
  80. package/editor/components/new-file.tsx +172 -0
  81. package/editor/components/scroll-query.tsx +25 -0
  82. package/editor/components/start-page.tsx +52 -0
  83. package/editor/http/admin/editor/api/fs/delete.api.tsx +32 -0
  84. package/editor/http/admin/editor/api/fs/read.api.tsx +55 -0
  85. package/editor/http/admin/editor/api/fs/rename.api.tsx +41 -0
  86. package/editor/http/admin/editor/api/fs/scan.api.tsx +181 -0
  87. package/editor/http/admin/editor/api/fs/write.api.tsx +41 -0
  88. package/editor/http/admin/editor/api/server/reload.api.ts +53 -0
  89. package/editor/http/admin/editor/api/server/restart.api.tsx +52 -0
  90. package/editor/http/admin/editor/editor.tsx +10 -0
  91. package/editor/http/admin/editor/index.api.tsx +43 -0
  92. package/editor/server.ts +57 -0
  93. package/editor/ws.ts +17 -0
  94. package/package.json +1 -1
@@ -0,0 +1,148 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+
4
+ //import hljs from "../lib/highlight/core.min.js"
5
+ import hljs from 'highlight.js/lib/core';
6
+ import javascript from 'highlight.js/lib/languages/javascript';
7
+ import xml from "highlight.js/lib/languages/xml";
8
+ import css from "highlight.js/lib/languages/css";
9
+ import scss from "highlight.js/lib/languages/scss";
10
+ import markdown from "highlight.js/lib/languages/markdown";
11
+ import dockerfile from "highlight.js/lib/languages/dockerfile";
12
+ import yaml from "highlight.js/lib/languages/yaml";
13
+ import typescript from "highlight.js/lib/languages/typescript";
14
+ import c from "highlight.js/lib/languages/c";
15
+ import cpp from "highlight.js/lib/languages/cpp";
16
+ import bash from "highlight.js/lib/languages/bash";
17
+ import python from "highlight.js/lib/languages/python";
18
+ import text from "highlight.js/lib/languages/plaintext";
19
+ import json from "highlight.js/lib/languages/json";
20
+
21
+ // Then register the languages you need
22
+ hljs.registerLanguage('text', text);
23
+ hljs.registerLanguage('dockerfile', dockerfile);
24
+ hljs.registerLanguage('yaml', yaml);
25
+ hljs.registerLanguage('javascript', javascript);
26
+ hljs.registerLanguage('xml', xml);
27
+ hljs.registerLanguage('html', xml);
28
+ hljs.registerLanguage('css', css);
29
+ hljs.registerLanguage('scss', scss);
30
+ hljs.registerLanguage('markdown', markdown);
31
+ hljs.registerLanguage('typescript', typescript);
32
+ hljs.registerLanguage('c', c);
33
+ hljs.registerLanguage('cpp', cpp);
34
+ hljs.registerLanguage('bash', bash);
35
+ hljs.registerLanguage('python', python);
36
+ hljs.registerLanguage('json', json);
37
+
38
+ /*!
39
+ Theme: GitHub Dark
40
+ Description: Dark theme as seen on github.com
41
+ Author: github.com
42
+ Maintainer: @Hirse
43
+ Updated: 2021-05-15
44
+
45
+ Outdated base version: https://github.com/primer/github-syntax-dark
46
+ Current colors taken from GitHub's CSS
47
+ */
48
+ const STYLE = `pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}code{outline: 0px solid rgba(0, 0, 0, 0.263);caret-color: red;}.hljs-name{color: #e8910d;}.hljs-tag .hljs-attr, .hljs-tag .hljs-name{color: #e8910d;}`;
49
+ const POSTTYLE = "pre{padding:0; margin:0;} code { background-color: #000000; color: #ffffff; }";
50
+ const TAG_NAME = "HIGHLIGHT-TEXT-AREA";
51
+
52
+ function runHighlight(content: string | null, language: string) {
53
+ const now = Date.now();
54
+ console.log("runHighlight [%s]", language);
55
+ const value = hljs.highlight(String(content), {
56
+ language,
57
+ ignoreIllegals: true
58
+ }).value;
59
+ console.log("runHighlight [%s] took [%s]ms", language, Date.now() - now);
60
+ return value;
61
+ }
62
+
63
+ function emitEvents(elementRef: any, topMost: any, content: string, oncontentchange?: (ev: CustomEvent) => void) {
64
+ if (elementRef.current && topMost.current) {
65
+ const currentContent = elementRef.current.textContent as any;
66
+ const webComponentElement = (topMost.current as HTMLElement).parentNode as HTMLElement;
67
+ const event = new CustomEvent("contentchange", { detail: currentContent });
68
+ if (webComponentElement && webComponentElement instanceof HTMLElement && webComponentElement.tagName === TAG_NAME) {
69
+ try {
70
+ webComponentElement.dispatchEvent(event);
71
+ } catch (e) {
72
+ console.error(e);
73
+ }
74
+ }
75
+ if (typeof oncontentchange === "function") {
76
+ try {
77
+ oncontentchange(event);
78
+ } catch (e) {
79
+ console.error(e);
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ export function HighlightTextArea({ content, language, oncontentchange, tabChar, disabled }:
86
+ { language: string; content: string; disabled?: string; tabChar?: string; oncontentchange?: (ev: CustomEvent) => void }
87
+ ) {
88
+ const elementRef = jsx.useRef();
89
+ const topMost = jsx.useRef();
90
+ const [lastLanguage, setlastLanguage] = jsx.useState(language);
91
+
92
+ // effect to watch changes to language
93
+ jsx.useEffect(() => {
94
+ if (elementRef.current && (lastLanguage !== language || elementRef.current.textContent !== content)) {
95
+ setlastLanguage(language);
96
+ elementRef.current.innerHTML = runHighlight(content, language);
97
+ }
98
+ }, [lastLanguage, language, elementRef.current, content])
99
+
100
+ return <pre ref={topMost}>
101
+ <style innerHTML={STYLE + POSTTYLE} />
102
+ <code
103
+ autocomplete="off"
104
+ autocorrect="off"
105
+ autocapitalize="off"
106
+ spellcheck="false"
107
+ contenteditable={disabled ? "false" : "true"}
108
+ ref={elementRef}
109
+ tabindex="0"
110
+ innerHTML={!elementRef.current ? runHighlight(content, language) : null}
111
+ onfocusout={async (ev) => {
112
+ ev.preventDefault();
113
+ if (elementRef.current) {
114
+ if (content !== elementRef.current.textContent) {
115
+ //setlastContent(String(elementRef.current.textContent));
116
+ //elementRef.current.innerHTML = runHighlight(elementRef.current.textContent, language);
117
+ }
118
+ elementRef.current.innerHTML = runHighlight(elementRef.current.textContent, language);
119
+ }
120
+ }}
121
+ onkeydown={ev => {
122
+ if (ev.keyCode === 9) {
123
+ ev.preventDefault();
124
+ // now insert four non-breaking spaces for the tab key
125
+ const sel = document.getSelection();
126
+ if (sel && topMost.current) {
127
+ if ((topMost.current as any).contains(sel.anchorNode)) {
128
+ const range = sel.getRangeAt(0);
129
+ const tabNode = document.createTextNode(tabChar ? tabChar : "\t");
130
+ range.insertNode(tabNode);
131
+ range.setStartAfter(tabNode);
132
+ range.setEndAfter(tabNode);
133
+ sel.removeAllRanges();
134
+ sel.addRange(range);
135
+ }
136
+ }
137
+ emitEvents(elementRef, topMost, content, oncontentchange);
138
+ }
139
+ }}
140
+ oninput={ev => {
141
+ ev.preventDefault();
142
+ emitEvents(elementRef, topMost, content, oncontentchange);
143
+ }} />
144
+ </pre>
145
+ }
146
+
147
+ HighlightTextArea.asFragment = true;
148
+ HighlightTextArea.shadowInit = false;
@@ -0,0 +1,113 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+
4
+ import { LogSocket } from "../common/log-socket.js";
5
+
6
+ function getUniqueIdentifiers(lines: {
7
+ out: string;
8
+ identifier: string;
9
+ }[]) {
10
+ const identifiersCache = {};
11
+ return lines.map(l => l.identifier).filter(identifier => {
12
+ const filter = !identifiersCache[identifier];
13
+ identifiersCache[identifier] = true;
14
+ return filter;
15
+ });
16
+ }
17
+
18
+ const LOG_LEVEL_MAP =
19
+ {
20
+ "none": 0,
21
+ "error": 1,
22
+ "warn": 2,
23
+ "info": 3,
24
+ "debug": 4,
25
+ "trace": 5,
26
+ };
27
+
28
+ interface LogViewerProps {
29
+ socket: LogSocket;
30
+ }
31
+
32
+ export function LogViewer(props: LogViewerProps) {
33
+ const { lines, clearLog, getMaxlogsize, setMaxLogSize } = props.socket;
34
+
35
+ const maxLogSize = getMaxlogsize();
36
+
37
+ const refresh = jsx.useRefresh();
38
+
39
+ jsx.useEffect(() => {
40
+ refresh();
41
+ }, [maxLogSize]);
42
+
43
+ const [identifier, setidentifier] = jsx.useState<string>("");
44
+ const [level, setlevel] = jsx.useState<string>("debug");
45
+ const [filter, setfilter] = jsx.useState<string>("");
46
+ const identifiers = getUniqueIdentifiers(lines).sort();
47
+ //console.log(identifiers);
48
+
49
+ return <div class="log-viewer">
50
+ <div class="log-viewer-toolbar">
51
+ <button
52
+ class="btn"
53
+ onclick={e => {
54
+ e.preventDefault();
55
+ clearLog();
56
+ }}>clear log</button>
57
+ <select
58
+ value={maxLogSize}
59
+ oninput={ev => {
60
+ ev.preventDefault();
61
+ if (ev.target.value === "unlimited") {
62
+ setMaxLogSize("unlimited");
63
+ } else {
64
+ setMaxLogSize(parseInt(ev.target.value, 10));
65
+ }
66
+ }}
67
+ style="margin: 0; padding: 0; margin-left:auto; margin-right: 0;">
68
+ {["10", "1000", "5000", "10000", "15000", "20000", "25000", "50000", "100000", "500000", "1000000", "1500000", "5000000"].map(l => <option value={l}>{l}</option>)}
69
+ <option value="unlimited">unlimited</option>
70
+ </select>
71
+ <select
72
+ value={level}
73
+ oninput={ev => {
74
+ ev.preventDefault();
75
+ setlevel(ev.target.value);
76
+ }}
77
+ style="margin: 0; padding: 0; margin-left:var(--file-browser-separation); margin-right: 0;">
78
+ {["error", "warn", "info", "debug", "trace"].map(l => <option value={l}>{l}</option>)}
79
+ <option value="">all</option>
80
+ </select>
81
+ <select
82
+ value={identifier}
83
+ oninput={ev => {
84
+ ev.preventDefault();
85
+ setidentifier(ev.target.value);
86
+ }}
87
+ style="margin: 0; padding: 0; margin-left:var(--file-browser-separation); margin-right: 0;">
88
+ {identifiers.map(identifier => <option value={identifier}>{identifier}</option>)}
89
+ <option value=""></option>
90
+ </select>
91
+ <input
92
+ style="margin: 0; padding: 0; margin-left:var(--file-browser-separation); margin-right: 0;"
93
+ value={filter}
94
+ oninput={ev => {
95
+ ev.preventDefault();
96
+ setfilter(ev.target.value);
97
+ }}
98
+ type="text"
99
+ placeholder="..filter.." />
100
+ </div>
101
+ <div class="log-viewer-log">
102
+ {lines
103
+ .filter(line => identifier === "" || line.identifier === identifier)
104
+ .filter(line => filter === "" || line.out.indexOf(filter) !== -1)
105
+ .filter(line => {
106
+ return (level === "" || LOG_LEVEL_MAP[level] >= LOG_LEVEL_MAP[line.level]);
107
+ })
108
+ .map(line =>
109
+ <p style="margin: 0; padding: 0; border-radius: 0; font-size: 12px;" class={line.level === "error" ? "info-danger" : line.level === "warn" ? "info-warn" : line.level === "trace" ? "info-success" : line.level === "debug" ? "info-info" : ""}>{line.out}</p>
110
+ ).reverse()}
111
+ </div>
112
+ </div>
113
+ }
@@ -0,0 +1,172 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+
4
+ import { BASEEDITOR_PATH } from "../common/constants.js";
5
+ import { TEMPLATES } from "../common/templates.js";
6
+ import { HighlightTextArea } from "./highlight-text-area.js";
7
+
8
+ export function NewFile(props: { migrations: string[]; services: string[]; open: boolean; ondone: (file?: string) => void; }) {
9
+
10
+ const [template, settemplate] = jsx.useState("EMPTY");
11
+
12
+ const refresh = jsx.useRefresh();
13
+
14
+ const [service, setservice] = jsx.useState(props.services[0]);
15
+
16
+ const nameInput = jsx.useRef();
17
+ //const open = dialog ? dialog.open : false;
18
+
19
+ /*jsx.useEffect(() => {
20
+ if (dialog) {
21
+ //console.log((dialogRef.current as any).open);
22
+ if (open !== props.open) {
23
+ if (nameInput.current) {
24
+ nameInput.current.value = "";
25
+ nameInput.current.focus();
26
+ }
27
+ if (props.open) {
28
+ console.log("showModal");
29
+ dialog.showModal();
30
+ //(dialogRef.current as any).show();
31
+ } else {
32
+ console.log("closeModal");
33
+ //dialog.close();
34
+ }
35
+ }
36
+ }
37
+ }, [dialog, open, props.open]);*/
38
+ jsx.useEffect(() => {
39
+
40
+ if (nameInput.current) {
41
+ nameInput.current.value = "";
42
+ nameInput.current.focus();
43
+ }
44
+ }, [nameInput.current]);
45
+
46
+ function close(file?: string) {
47
+ //console.log("closeModal");
48
+ if (props.ondone) {
49
+ props.ondone(file);
50
+ }
51
+ }
52
+
53
+
54
+ let nextMigrationNumber = 1;
55
+ if (props.migrations.length > 0) {
56
+ const lastMigration = props.migrations[props.migrations.length - 1];
57
+ const indexOfDash = lastMigration.indexOf("-");
58
+ if (indexOfDash !== -1) {
59
+ nextMigrationNumber = parseInt(lastMigration.substring(0, indexOfDash), 10) + 1;
60
+ }
61
+ }
62
+
63
+ const httpPath = (TEMPLATES[template].filename ? TEMPLATES[template].filename : nameInput.current?.value) + (TEMPLATES[template].httpSufix ? TEMPLATES[template].httpSufix : "");
64
+
65
+ const filename = (service ? service + "/" : "") + (service && TEMPLATES[template].prefix ? TEMPLATES[template].prefix + "/" : "") + (template === "MIGRATION" ? nextMigrationNumber + "-" : "") + (TEMPLATES[template].filename ? TEMPLATES[template].filename : nameInput.current?.value) + (TEMPLATES[template].sufix ? TEMPLATES[template].sufix : "");
66
+
67
+ async function createNewFile() {
68
+ if ((nameInput.current && nameInput.current.value) || TEMPLATES[template].filename) {
69
+ const t = TEMPLATES[template];
70
+ await fetch(BASEEDITOR_PATH + "/api/fs/write", {
71
+ method: "POST",
72
+ headers: {
73
+ ["content-type"]: "application/json"
74
+ },
75
+ body: JSON.stringify({
76
+ path: filename,
77
+ contents: t.template ? t.template(filename, httpPath) : ""
78
+ })
79
+ });
80
+ return filename;
81
+ }
82
+ }
83
+
84
+ return <>
85
+ {props.open ? <dialog
86
+ class="new-dialog"
87
+ open="">
88
+ <div class="dialog-header">
89
+ <h1>Create new File</h1>
90
+ </div>
91
+ <div class="dialog-body">
92
+ <select
93
+ value={service}
94
+ oninput={ev => {
95
+ ev.preventDefault();
96
+ ev.stopPropagation();
97
+ console.log(ev.target.value);
98
+ setservice(ev.target.value);
99
+ }}
100
+ style="margin: 0; padding: 0; margin-left:auto; margin-right: var(--file-browser-separation); width: 100%; margin-top: var(--file-browser-separation); margin-bottom: var(--file-browser-separation);">
101
+ <option value={""}></option>
102
+ {props.services.map(service => <option value={service}>{service}</option>)}
103
+ </select>
104
+ <p
105
+ style="margin: 0; margin-bottom: var(--file-browser-separation);"
106
+ >{filename}</p>
107
+ {!TEMPLATES[template].filename ? <form
108
+ style="width: 100%;"
109
+ onsubmit={async (ev) => {
110
+ ev.preventDefault();
111
+ ev.stopPropagation();
112
+ const path = await createNewFile();
113
+ close(path);
114
+ }}>
115
+ <input
116
+ style="width: 100%;"
117
+ onkeydown={ev => {
118
+ if (ev.keyCode === 27) {
119
+ // catch esc
120
+ ev.stopPropagation();
121
+ ev.preventDefault();
122
+ close();
123
+ }
124
+ refresh();
125
+ }}
126
+ ref={nameInput}
127
+ type="text"
128
+ placeholder="...filename..." />
129
+ </form> : <></>}
130
+ <select
131
+ value={template}
132
+ oninput={ev => {
133
+ ev.preventDefault();
134
+ ev.stopPropagation();
135
+ console.log(ev.target.value);
136
+ settemplate(ev.target.value);
137
+ }}
138
+ style="margin: 0; padding: 0; margin-left:auto; margin-right: var(--file-browser-separation); width: 100%; margin-top: var(--file-browser-separation);">
139
+ {Object.keys(TEMPLATES).map(templateName => <option value={templateName}>{TEMPLATES[templateName].displayName}</option>)}
140
+ </select>
141
+ {TEMPLATES[template].template ? <div
142
+ class="new-dialog-preview">
143
+ <HighlightTextArea
144
+ content={TEMPLATES[template].template(filename, httpPath)}
145
+ language={TEMPLATES[template].language}
146
+ disabled="true" />
147
+ </div> : <></>}
148
+ </div>
149
+ <div class="dialog-footer">
150
+ <button
151
+ class="btn danger"
152
+ onclick={ev => {
153
+ ev.preventDefault();
154
+ ev.stopPropagation();
155
+ close();
156
+ }}>cancel</button>
157
+ <button
158
+ style="margin-left: auto;"
159
+ class="btn active"
160
+ onclick={async (ev) => {
161
+ ev.preventDefault();
162
+ ev.stopPropagation();
163
+ const path = await createNewFile();
164
+ close(path);
165
+ }}>create</button>
166
+ </div>
167
+ </dialog> : <></>}
168
+ </>
169
+ }
170
+
171
+ NewFile.asFragment = true;
172
+ NewFile.shadowInit = false;
@@ -0,0 +1,25 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+
4
+ let scrollTimeout2: any = null;
5
+
6
+ export function useScroll(): [{ scrollTop: string; scrollLeft: string }, (newScroll: { scrollTop: number; scrollLeft: number; }, inmediate?: boolean) => void] {
7
+ const [scrollTop, setscrollTop] = jsx.useQuery("scrollTop", "0");
8
+ const [scrollLeft, setscrollLeft] = jsx.useQuery("scrollLeft", "0");
9
+
10
+ return [{
11
+ scrollTop: scrollTop as string,
12
+ scrollLeft: scrollLeft as string
13
+ }, (newScroll: { scrollTop: number; scrollLeft: number; }, inmediate?: boolean) => {
14
+ clearTimeout(scrollTimeout2);
15
+ if (inmediate) {
16
+ setscrollTop(String(newScroll.scrollTop));
17
+ setscrollLeft(String(newScroll.scrollLeft));
18
+ } else {
19
+ scrollTimeout2 = setTimeout(() => {
20
+ setscrollTop(String(newScroll.scrollTop));
21
+ setscrollLeft(String(newScroll.scrollLeft));
22
+ }, 1000);
23
+ }
24
+ }]
25
+ }
@@ -0,0 +1,52 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+
4
+ interface StartPageProps {
5
+ togglePanel: (panel: string) => void;
6
+ isPanelVisible: (panel: string) => boolean;
7
+ disableLog?: boolean; disablePreview?: boolean; disableReload?: boolean;
8
+ }
9
+
10
+ export function StartPage(props: StartPageProps) {
11
+
12
+ jsx.useEffect(() => {
13
+ if (!props.isPanelVisible("left")) {
14
+ props.togglePanel("left");
15
+ }
16
+ }, []);
17
+
18
+ return <div class={`start-page`}>
19
+ <div class="row center">
20
+ <h1>Start Page</h1>
21
+ </div>
22
+ <br />
23
+ <div class="row center">
24
+ <div class="row">
25
+ <div class={`toggle-panel-button left-side-panel-button ${props.isPanelVisible("left") ? "active" : ""}`}
26
+ onclick={ev => {
27
+ ev.preventDefault();
28
+ props.togglePanel("left");
29
+ }}
30
+ ></div>
31
+ <br />
32
+ {props.disableLog ? <></> : <div class={`toggle-panel-button bottom-side-panel-button ${props.isPanelVisible("bottom") ? "active" : ""}`}
33
+ onclick={ev => {
34
+ ev.preventDefault();
35
+ props.togglePanel("bottom");
36
+ }}
37
+ ></div>}
38
+ <br />
39
+ {props.disablePreview ? <></> : <div class={`toggle-panel-button right-side-panel-button ${props.isPanelVisible("right") ? "active" : ""}`}
40
+ onclick={ev => {
41
+ ev.preventDefault();
42
+ props.togglePanel("right");
43
+ }}
44
+ ></div>}
45
+ </div>
46
+ </div>
47
+ <br />
48
+ </div>;
49
+ }
50
+
51
+ StartPage.asFragment = true;
52
+ StartPage.shadowInit = false;
@@ -0,0 +1,32 @@
1
+ import { defineRoute, JSONParser } from "@miqro/core";
2
+ import { unlinkSync } from "node:fs";
3
+ import { getPath } from "./read.api.js";
4
+
5
+ export default defineRoute({
6
+ description: "admin editor file deletion endpoint",
7
+ method: "POST",
8
+ path: "/delete",
9
+ middleware: [JSONParser()],
10
+ request: {
11
+ body: {
12
+ path: "string"
13
+ }
14
+ },
15
+ response: {
16
+ status: [200, 400],
17
+ body: {
18
+ message: "string"
19
+ }
20
+ },
21
+ handler: async (req, res) => {
22
+ const { path } = req.body;
23
+ await deleteFile(path);
24
+ return res?.json({
25
+ message: "OK"
26
+ });
27
+ }
28
+ });
29
+
30
+ function deleteFile(path: string) {
31
+ unlinkSync(getPath(path));
32
+ }
@@ -0,0 +1,55 @@
1
+ import { APIRoute, defineRoute, JSONParser } from "@miqro/core";
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import { SUPPORTED_LANGUAGES } from "../../../../../common/constants.js";
4
+ import { relative, resolve } from "node:path";
5
+ import { getLanguage } from "./scan.api.js";
6
+ import { BASE_PATH } from "../../../../../common/constants.server.js";
7
+
8
+ export default defineRoute({
9
+ method: "POST",
10
+ path: "/read",
11
+ description: "admin editor file read endpoint",
12
+ middleware: [JSONParser()],
13
+ request: {
14
+ body: {
15
+ path: "string"
16
+ }
17
+ },
18
+ response: {
19
+ status: [200, 400],
20
+ body: {
21
+ contents: "string",
22
+ path: "string"
23
+ }
24
+ },
25
+ handler: async (req, res) => {
26
+ const { path } = req.body;
27
+ const contents = readFile(path);
28
+ return res?.json({
29
+ contents,
30
+ path
31
+ });
32
+ }
33
+ });
34
+
35
+ export function readFile(path: string) {
36
+ const filePath = getPath(path);
37
+ const language = getLanguage(filePath);
38
+ if (SUPPORTED_LANGUAGES.includes(language)) {
39
+ const contents = readFileSync(filePath).toString();
40
+ return contents;
41
+ } else {
42
+ throw new Error("unsupported file format");
43
+ }
44
+
45
+ }
46
+
47
+ export function getPath(path: string) {
48
+ const realPath = realpathSync(resolve(BASE_PATH, path));
49
+
50
+ if (relative(BASE_PATH, realPath).startsWith("..")) {
51
+ throw new Error("invalid path! [" + path + "]");
52
+ }
53
+
54
+ return realPath;
55
+ }
@@ -0,0 +1,41 @@
1
+ import { APIRoute, defineRoute, JSONParser } from "@miqro/core";
2
+ import { existsSync, mkdirSync, renameSync } from "node:fs";
3
+ import { getPath } from "./read.api.js";
4
+ import { dirname } from "node:path";
5
+
6
+ export default defineRoute({
7
+ middleware: [JSONParser()],
8
+ method: "POST",
9
+ description: "admin editor file rename endpoint",
10
+ path: "/rename",
11
+ request: {
12
+ body: {
13
+ path: "string",
14
+ newName: "string"
15
+ }
16
+ },
17
+ response: {
18
+ status: [200, 400],
19
+ body: {
20
+ message: "string"
21
+ }
22
+ },
23
+ handler: async (req, res) => {
24
+ const { path, newName } = req.body;
25
+ await rename(path, newName);
26
+ return res?.json({
27
+ message: "OK"
28
+ });
29
+ }
30
+ });
31
+
32
+ export async function rename(path: string, newName: string) {
33
+ if (existsSync(path) && !existsSync(newName)) {
34
+ mkdirSync(dirname(getPath(newName)), {
35
+ recursive: true
36
+ });
37
+ renameSync(getPath(path), getPath(newName));
38
+ } else {
39
+ throw new Error("invalid paths");
40
+ }
41
+ }