miqro 7.2.7 → 7.2.9

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 (97) 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/common/exit.js +18 -2
  62. package/build/esm/src/common/watch.js +8 -3
  63. package/build/esm/src/services/app.d.ts +1 -1
  64. package/build/esm/src/services/editor.d.ts +1 -1
  65. package/build/esm/src/services/utils/admin-interface.d.ts +1 -1
  66. package/build/esm/src/services/utils/websocketmanager.d.ts +1 -1
  67. package/build/lib.cjs +48 -20
  68. package/editor/auth.ts +52 -0
  69. package/editor/common/constants.server.ts +5 -0
  70. package/editor/common/constants.ts +21 -0
  71. package/editor/common/editor-index.tsx +17 -0
  72. package/editor/common/html-encode.ts +14 -0
  73. package/editor/common/log-socket.tsx +87 -0
  74. package/editor/common/templates.ts +481 -0
  75. package/editor/components/api-preview.tsx +118 -0
  76. package/editor/components/editor.tsx +496 -0
  77. package/editor/components/file-browser.tsx +311 -0
  78. package/editor/components/file-editor-toolbar.tsx +194 -0
  79. package/editor/components/file-editor.tsx +125 -0
  80. package/editor/components/filter-query.tsx +26 -0
  81. package/editor/components/highlight-text-area.tsx +148 -0
  82. package/editor/components/log-viewer.tsx +113 -0
  83. package/editor/components/new-file.tsx +172 -0
  84. package/editor/components/scroll-query.tsx +25 -0
  85. package/editor/components/start-page.tsx +52 -0
  86. package/editor/http/admin/editor/api/fs/delete.api.tsx +32 -0
  87. package/editor/http/admin/editor/api/fs/read.api.tsx +55 -0
  88. package/editor/http/admin/editor/api/fs/rename.api.tsx +41 -0
  89. package/editor/http/admin/editor/api/fs/scan.api.tsx +181 -0
  90. package/editor/http/admin/editor/api/fs/write.api.tsx +41 -0
  91. package/editor/http/admin/editor/api/server/reload.api.ts +53 -0
  92. package/editor/http/admin/editor/api/server/restart.api.tsx +52 -0
  93. package/editor/http/admin/editor/editor.tsx +10 -0
  94. package/editor/http/admin/editor/index.api.tsx +43 -0
  95. package/editor/server.ts +57 -0
  96. package/editor/ws.ts +17 -0
  97. package/package.json +2 -2
@@ -0,0 +1,367 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+ import { FileEditor } from "./file-editor.js";
4
+ import { NewFile } from "./new-file.js";
5
+ import { useScroll } from "./scroll-query.js";
6
+ import { useFilterQuery } from "./filter-query.js";
7
+ import { FileBrowser } from "./file-browser.js";
8
+ import { StartPage } from "./start-page.js";
9
+ import { BASEEDITOR_PATH } from "../common/constants.js";
10
+ import { LogViewer } from "./log-viewer.js";
11
+ import { useLogSocket } from "../common/log-socket.js";
12
+ function InflateError2Map(errors) {
13
+ const errorMap = {};
14
+ for (const e of errors) {
15
+ const filePath = e.filePath;
16
+ const error = e.error;
17
+ errorMap[filePath] = {
18
+ error
19
+ };
20
+ }
21
+ return errorMap;
22
+ }
23
+ export function Editor(props) {
24
+ //console.dir(props);
25
+ const logSocket = useLogSocket({
26
+ disableLog: props.disablelog === "true" || props.disablelog === true ? true : false
27
+ });
28
+ const [services, setservices] = jsx.useState(JSON.parse(props.services));
29
+ const [collapsed, setCollapsed] = jsx.useState((() => {
30
+ const ret = {};
31
+ services.forEach(service => {
32
+ ret[`${service}/`] = false;
33
+ });
34
+ return ret;
35
+ })());
36
+ /*const [panelVisible, setpanelVisible] = jsx.useState<{
37
+ [panel: string]: boolean | undefined;
38
+ }>({ right: false, bottom: false });*/
39
+ const [rightPanelVisible, setRightPanelVisible] = jsx.useQuery("right-panel", "0");
40
+ const [leftPanelVisible, setLeftPanelVisible] = jsx.useQuery("left-panel", "1");
41
+ const [bottomPanelVisible, setBottomPanelVisible] = jsx.useQuery("bottom-panel", "0");
42
+ const [migrations, setmigrations] = jsx.useState(JSON.parse(props.migrations));
43
+ const [files, setfiles] = jsx.useState(JSON.parse(props.files));
44
+ const [reloadString, setreloadString] = jsx.useState(props.reloadstring);
45
+ const [changed, setchanged] = jsx.useState({});
46
+ const [errors, seterrors] = jsx.useState(InflateError2Map(JSON.parse(props.errors)));
47
+ const [newFileDialogShow, setnewFileDialogShow] = jsx.useState(false);
48
+ const [filter, setfilter] = useFilterQuery();
49
+ const refresh = jsx.useRefresh();
50
+ const [opened, setopened] = jsx.useState({});
51
+ const [current, setcurrent] = jsx.useQuery("current", props.initialcurrent ? props.initialcurrent : "");
52
+ const [_, setScroll] = useScroll();
53
+ if (current instanceof Array) {
54
+ setcurrent(current[0]);
55
+ }
56
+ else if (current) {
57
+ if (!opened[current]) {
58
+ const file = files.filter(file => file.filePath === current)[0];
59
+ if (!file) {
60
+ setcurrent("");
61
+ }
62
+ else {
63
+ openFile(file.filePath);
64
+ }
65
+ }
66
+ }
67
+ /*useEffect(() => {
68
+ for (const service of services) {
69
+ if (collapsed[`${service}/`] === undefined) {
70
+ collapsed[`${service}/`] = true;
71
+ refresh();
72
+ }
73
+ }
74
+ }, services);*/
75
+ function openFile(filePath) {
76
+ console.log("open [%s]", filePath);
77
+ const isOpen = opened[filePath];
78
+ setScroll({
79
+ scrollLeft: 0,
80
+ scrollTop: 0
81
+ });
82
+ if (!isOpen) {
83
+ const file = files.filter(file => file.filePath === filePath)[0];
84
+ if (!file) {
85
+ throw new Error("cannot find reference to file [" + filePath + "]");
86
+ }
87
+ opened[file.filePath] = {
88
+ content: null,
89
+ ...file
90
+ };
91
+ changed[file.filePath] = null;
92
+ setopened(opened);
93
+ if (opened[file.filePath].content === null && (globalThis.window) !== undefined) {
94
+ if (opened[file.filePath].language !== "binary") {
95
+ fetch(BASEEDITOR_PATH + "/api/fs/read", {
96
+ method: "POST",
97
+ headers: {
98
+ ["content-type"]: "application/json"
99
+ },
100
+ body: JSON.stringify({
101
+ path: file.filePath
102
+ })
103
+ }).then(response => {
104
+ if (response.ok) {
105
+ response.json().then(json => {
106
+ const contents = json.contents;
107
+ if (opened[file.filePath]) {
108
+ opened[file.filePath].content = contents;
109
+ changed[file.filePath] = contents;
110
+ setopened(opened);
111
+ setchanged(changed);
112
+ refresh();
113
+ }
114
+ });
115
+ }
116
+ });
117
+ }
118
+ else {
119
+ opened[file.filePath].content = "";
120
+ changed[file.filePath] = "";
121
+ setopened(opened);
122
+ refresh();
123
+ }
124
+ }
125
+ }
126
+ setcurrent(filePath);
127
+ }
128
+ function closeFile(filePath) {
129
+ setScroll({
130
+ scrollTop: 0,
131
+ scrollLeft: 0
132
+ });
133
+ console.log("closing [%s]", filePath);
134
+ //console.log("current [%s]", current);
135
+ if (filePath === current) {
136
+ setcurrent("");
137
+ }
138
+ delete opened[filePath];
139
+ delete changed[filePath];
140
+ setchanged(changed);
141
+ setopened(opened);
142
+ refresh();
143
+ }
144
+ async function scanFiles() {
145
+ const r = await fetch(BASEEDITOR_PATH + "/api/fs/scan", {
146
+ method: "GET"
147
+ });
148
+ if (r.ok) {
149
+ const { files, services } = await r.json();
150
+ setfiles(files);
151
+ setservices(services);
152
+ for (const file of files) {
153
+ if (opened[file.filePath]) {
154
+ opened[file.filePath] = {
155
+ content: opened[file.filePath].content,
156
+ ...file
157
+ };
158
+ }
159
+ }
160
+ }
161
+ }
162
+ function isDirCollapsed(dir) {
163
+ console.log("isDirCollapsed [%s]", dir);
164
+ if (dir === "" || filter !== "") {
165
+ return false;
166
+ }
167
+ return collapsed[dir] || collapsed[dir] === undefined ? true : false;
168
+ //return collapsed[dir] ? false : true;
169
+ }
170
+ function toggleCollapseDir(dir) {
171
+ console.log("toggleCollapseDir [%s]", dir);
172
+ collapsed[dir] = isDirCollapsed(dir) ? false : true;
173
+ setCollapsed(collapsed);
174
+ refresh();
175
+ }
176
+ async function deleteFile(file) {
177
+ console.log("deleteFile [%s]", file);
178
+ const r = await fetch(BASEEDITOR_PATH + "/api/fs/delete", {
179
+ method: "POST",
180
+ headers: {
181
+ ["content-type"]: "application/json"
182
+ },
183
+ body: JSON.stringify({
184
+ path: file
185
+ })
186
+ });
187
+ if (r.ok) {
188
+ await scanFiles();
189
+ if (opened[file]) {
190
+ closeFile(file);
191
+ }
192
+ }
193
+ }
194
+ async function renameFile(file, newName) {
195
+ console.log("renameFile [%s] to [%s]", file, newName);
196
+ if (opened[file]) {
197
+ const r = await fetch(BASEEDITOR_PATH + "/api/fs/rename", {
198
+ method: "POST",
199
+ headers: {
200
+ ["content-type"]: "application/json"
201
+ },
202
+ body: JSON.stringify({
203
+ path: file,
204
+ newName
205
+ })
206
+ });
207
+ if (r.ok) {
208
+ const fileO = opened[file];
209
+ const changeO = changed[file];
210
+ fileO.filePath = newName;
211
+ delete changed[file];
212
+ delete opened[file];
213
+ opened[newName] = fileO;
214
+ changed[newName] = changeO;
215
+ await scanFiles();
216
+ setopened(opened);
217
+ setchanged(changed);
218
+ if (current === file) {
219
+ setcurrent(newName);
220
+ }
221
+ refresh();
222
+ }
223
+ }
224
+ }
225
+ async function saveFile(file) {
226
+ console.log("saveFile [%s]", file);
227
+ const contents = changed[file];
228
+ if (opened[file] && contents !== undefined && contents !== null) {
229
+ const r = await fetch(BASEEDITOR_PATH + "/api/fs/write", {
230
+ method: "POST",
231
+ headers: {
232
+ ["content-type"]: "application/json"
233
+ },
234
+ body: JSON.stringify({
235
+ path: file,
236
+ contents,
237
+ override: true
238
+ })
239
+ });
240
+ if (r.ok) {
241
+ opened[file].content = contents;
242
+ setopened(opened);
243
+ setchanged(changed);
244
+ refresh();
245
+ }
246
+ }
247
+ }
248
+ async function reloadServer() {
249
+ const r = await fetch(BASEEDITOR_PATH + "/api/server/reload", {
250
+ method: "POST"
251
+ });
252
+ if (r.ok) {
253
+ const reloadResponse = await r.json();
254
+ if (reloadResponse.errors && reloadResponse.errors.length > 0) {
255
+ seterrors(InflateError2Map(reloadResponse.errors));
256
+ setreloadString(reloadResponse.reloadString);
257
+ refresh();
258
+ }
259
+ else {
260
+ setreloadString(reloadResponse.reloadString);
261
+ seterrors({});
262
+ refresh();
263
+ }
264
+ setmigrations(reloadResponse.migrations);
265
+ await scanFiles();
266
+ }
267
+ }
268
+ function setLanguage(file, newLanguage) {
269
+ if (opened[file]) {
270
+ opened[file].language = newLanguage;
271
+ setopened(opened);
272
+ refresh();
273
+ }
274
+ }
275
+ function hasFileContentChanged(filePath) {
276
+ if (!opened[filePath]) {
277
+ return false;
278
+ }
279
+ return changed[filePath] !== opened[filePath].content;
280
+ }
281
+ function isPanelVisible(panel) {
282
+ switch (panel) {
283
+ case "left":
284
+ return leftPanelVisible === "1";
285
+ case "bottom":
286
+ return bottomPanelVisible === "1";
287
+ case "right":
288
+ return rightPanelVisible === "1";
289
+ default:
290
+ return true;
291
+ }
292
+ //return panelVisible[panel] || panelVisible[panel] === undefined ? true : false;
293
+ }
294
+ function togglePanel(panel) {
295
+ console.log("togglePanel [%s]", panel);
296
+ /*panelVisible[panel] = !isPanelVisible(panel);
297
+ setpanelVisible(panelVisible);
298
+ refresh();*/
299
+ switch (panel) {
300
+ case "left":
301
+ return setLeftPanelVisible(isPanelVisible(panel) ? "0" : "1");
302
+ case "bottom":
303
+ return setBottomPanelVisible(isPanelVisible(panel) ? "0" : "1");
304
+ case "right":
305
+ return setRightPanelVisible(isPanelVisible(panel) ? "0" : "1");
306
+ default:
307
+ return;
308
+ }
309
+ }
310
+ const openedFile = opened[String(current)];
311
+ //console.log("Editor [%s]", openedFile?.filePath);
312
+ return JSX.createElement("div", { class: "editor" },
313
+ JSX.createElement("div", { class: `editor-firstrow${!isPanelVisible("bottom") ? " bottom-panel-hidden" : ""}` },
314
+ JSX.createElement(NewFile, { migrations: migrations, services: services, open: newFileDialogShow, ondone: async (path) => {
315
+ setnewFileDialogShow(false);
316
+ if (path) {
317
+ await scanFiles();
318
+ //await reloadServer();
319
+ }
320
+ } }),
321
+ JSX.createElement(FileBrowser, { disableLog: props.disablelog, disablePreview: props.disablepreview, disableReload: props.disablereload, togglePanel: togglePanel, isPanelVisible: isPanelVisible, opened: Object.keys(opened).sort().map(o => opened[o]), isDirCollapsed: isDirCollapsed, toggleCollapseDir: toggleCollapseDir, migrations: migrations, hasErrors: (file) => errors[file] ? true : false, reloadServer: reloadServer, closeAll: () => {
322
+ const openedList = Object.keys(opened);
323
+ for (const o of openedList) {
324
+ closeFile(o);
325
+ }
326
+ }, closeFile: closeFile, current: current ? String(current) : "", files: files, filter: filter, hasFileContentChanged: hasFileContentChanged, isOpen: (file) => opened[file] !== undefined, openFile: openFile, saveAll: async () => {
327
+ const openedList = Object.keys(opened);
328
+ for (const o of openedList) {
329
+ if (hasFileContentChanged(o)) {
330
+ await saveFile(o);
331
+ }
332
+ }
333
+ //await reloadServer();
334
+ }, scanFiles: scanFiles, setfilter: setfilter, showNewFile: () => {
335
+ setnewFileDialogShow(true);
336
+ } }),
337
+ JSX.createElement("div", { class: `file-editor-list${!isPanelVisible("left") ? " left-panel-hidden" : ""}${!isPanelVisible("bottom") ? " bottom-panel-hidden" : ""}` }, !openedFile ?
338
+ JSX.createElement(StartPage, { disableLog: props.disablelog, disablePreview: props.disablepreview, disableReload: props.disablereload, isPanelVisible: isPanelVisible, togglePanel: togglePanel }) :
339
+ JSX.createElement("div", { class: "file-editor-container" },
340
+ JSX.createElement(FileEditor, { disableLog: props.disablelog, disablePreview: props.disablepreview, disableReload: props.disablereload, isPanelVisible: isPanelVisible, togglePanel: togglePanel, reloadString: reloadString, error: errors[openedFile.filePath]?.error, changed: hasFileContentChanged(openedFile.filePath), setlanguage: (newLanguage) => setLanguage(openedFile.filePath, newLanguage), deleteFile: async () => {
341
+ await deleteFile(openedFile.filePath);
342
+ //await reloadServer();
343
+ }, renameFile: async (newName) => {
344
+ await renameFile(openedFile.filePath, newName);
345
+ //await reloadServer();
346
+ }, saveFile: async (reload = false) => {
347
+ await saveFile(openedFile.filePath);
348
+ if (reload) {
349
+ await reloadServer();
350
+ }
351
+ //
352
+ }, content: changed[openedFile.filePath] !== null ? changed[openedFile.filePath] : opened[openedFile.filePath].content, current: current === openedFile.filePath, revertFile: () => {
353
+ changed[openedFile.filePath] = openedFile.content;
354
+ setchanged(changed);
355
+ refresh();
356
+ }, contentchange: (content) => {
357
+ changed[openedFile.filePath] = content;
358
+ setopened(opened);
359
+ refresh();
360
+ }, closeFile: () => {
361
+ closeFile(openedFile.filePath);
362
+ }, path: openedFile.filePath, previewPath: openedFile.previewPath, apiPreview: openedFile.apiPreview, language: opened[openedFile.filePath].language })))),
363
+ JSX.createElement("div", { class: "editor-bottom-panel", style: `${!isPanelVisible("bottom") ? "display: none;" : ""}` },
364
+ JSX.createElement(LogViewer, { socket: logSocket })));
365
+ }
366
+ Editor.asFragment = true;
367
+ Editor.shadowInit = false;
@@ -0,0 +1,37 @@
1
+ interface FileBrowserFile {
2
+ filePath: string;
3
+ fileName: string;
4
+ language: string;
5
+ content?: string;
6
+ dirs: string[];
7
+ }
8
+ interface FileBrowserProps {
9
+ filter: string;
10
+ migrations: string[];
11
+ current: string;
12
+ isDirCollapsed: (dir: string) => boolean;
13
+ toggleCollapseDir: (dir: string) => void;
14
+ saveAll: () => void;
15
+ isOpen: (file: string) => boolean;
16
+ openFile: (file: string) => void;
17
+ setfilter: (newFilter: any) => void;
18
+ showNewFile: () => void;
19
+ scanFiles: () => void;
20
+ closeAll: () => void;
21
+ closeFile: (file: string) => void;
22
+ reloadServer: () => void;
23
+ hasFileContentChanged: (file: string) => boolean;
24
+ hasErrors: (file: string) => boolean;
25
+ files: FileBrowserFile[];
26
+ opened: {
27
+ fileName: string;
28
+ filePath: string;
29
+ }[];
30
+ isPanelVisible: (panel: string) => boolean;
31
+ togglePanel: (panel: string) => void;
32
+ disableLog?: boolean;
33
+ disablePreview?: boolean;
34
+ disableReload?: boolean;
35
+ }
36
+ export declare function FileBrowser(props: FileBrowserProps): JSX.Element;
37
+ export {};
@@ -0,0 +1,127 @@
1
+ import JSX from "@miqro/jsx";
2
+ export function FileBrowser(props) {
3
+ return JSX.createElement("div", { class: "file-browser", style: `${!props.isPanelVisible("left") ? "display: none;" : ""}` },
4
+ JSX.createElement("div", { class: "file-browser-toolbar" },
5
+ JSX.createElement("div", { class: "row" },
6
+ JSX.createElement("button", { class: "file-editor-button btn", style: `width: 10%;${props.disableReload ? "width: 30%;" : ""}`, onclick: ev => {
7
+ ev.preventDefault();
8
+ props.togglePanel("left");
9
+ } }, "<<"),
10
+ props.disableLog ? JSX.createElement(JSX.Fragment, null) :
11
+ JSX.createElement("button", { id: "log-btn", class: "file-editor-button btn", style: `width: 30%;${props.disableLog ? "display: none;" : ""}`, onclick: ev => {
12
+ ev.preventDefault();
13
+ props.togglePanel("bottom");
14
+ } }, "log"),
15
+ props.disableReload ? JSX.createElement(JSX.Fragment, null) :
16
+ JSX.createElement("button", { id: "scan-btn", class: "file-editor-button btn active", style: `width: 30%;${props.disableReload ? "display: none;" : ""}`, onclick: ev => {
17
+ ev.preventDefault();
18
+ props.scanFiles();
19
+ } }, "scan"),
20
+ props.disableReload ? JSX.createElement(JSX.Fragment, null) :
21
+ JSX.createElement("button", { id: "reload-btn", class: "file-editor-button btn warning", style: `width: 30%;${props.disableReload ? "display: none;" : ""}`, onclick: ev => {
22
+ ev.preventDefault();
23
+ props.reloadServer();
24
+ } }, "reload")),
25
+ JSX.createElement("div", { class: "row" },
26
+ JSX.createElement("input", { type: "text", style: "width: 100%;", value: props.filter, oninput: ev => {
27
+ ev.preventDefault();
28
+ const text = ev.target.value;
29
+ props.setfilter(text);
30
+ }, placeholder: "..filter.." })),
31
+ JSX.createElement("div", { class: "row" },
32
+ props.disablePreview ? JSX.createElement(JSX.Fragment, null) :
33
+ JSX.createElement("button", { id: "preview-btn", class: "file-editor-button btn", style: `width: 30%;${props.disablePreview ? "display: none;" : ""}`, onclick: ev => {
34
+ ev.preventDefault();
35
+ props.togglePanel("right");
36
+ } }, "preview"),
37
+ JSX.createElement("button", { id: "new-btn", class: "file-editor-button btn info", style: "width: 20%;", onclick: ev => {
38
+ ev.preventDefault();
39
+ props.showNewFile();
40
+ } }, "new"),
41
+ props.disableReload ? JSX.createElement(JSX.Fragment, null) :
42
+ JSX.createElement("button", { id: "scan-btn", class: "file-editor-button btn active", style: `width: 30%;${!props.disableReload ? "display: none;" : ""}`, onclick: ev => {
43
+ ev.preventDefault();
44
+ props.scanFiles();
45
+ } }, "scan"),
46
+ JSX.createElement("button", { id: "saveall-btn", class: "file-editor-button btn success", style: "width: 25%;", onclick: ev => {
47
+ ev.preventDefault();
48
+ props.saveAll();
49
+ } }, "save"),
50
+ JSX.createElement("button", { id: "closeall-btn", class: "file-editor-button btn danger", style: "width: 25%;", onclick: ev => {
51
+ ev.preventDefault();
52
+ props.closeAll();
53
+ } }, "close"))),
54
+ JSX.createElement("div", { class: "file-browser-files" },
55
+ props.opened.length > 0 ? JSX.createElement("div", { class: "file-browser-opened-files", style: "padding: var(--file-browser-separation);" },
56
+ JSX.createElement("ul", { style: "padding: 0; margin: 0;" }, props.opened.map(file => JSX.createElement(File, { closeFile: props.closeFile, current: props.current, fileName: file.fileName, filePath: file.filePath, hasErrors: props.hasErrors, hasFileContentChanged: props.hasFileContentChanged, isOpen: props.isOpen, openFile: props.openFile })))) : JSX.createElement(JSX.Fragment, null),
57
+ JSX.createElement(FileTree, { isDirCollapsed: props.isDirCollapsed, toggleCollapseDir: props.toggleCollapseDir, closeFile: props.closeFile, current: props.current, files: props.files, filter: props.filter, hasErrors: props.hasErrors, hasFileContentChanged: props.hasFileContentChanged, isOpen: props.isOpen, openFile: props.openFile })));
58
+ }
59
+ function FileTree(props) {
60
+ const ret = {
61
+ dirs: {},
62
+ files: [],
63
+ name: "",
64
+ fullName: ""
65
+ };
66
+ props.files.forEach((file) => {
67
+ let cDir = ret;
68
+ let fullName = "";
69
+ for (const dir of file.dirs) {
70
+ fullName += dir + "/";
71
+ if (cDir.dirs[dir] === undefined) {
72
+ const newDir = {
73
+ dirs: {},
74
+ files: [],
75
+ name: dir,
76
+ fullName: `${fullName}`
77
+ };
78
+ cDir.dirs[dir] = newDir;
79
+ cDir = newDir;
80
+ }
81
+ else {
82
+ cDir = cDir.dirs[dir];
83
+ }
84
+ }
85
+ cDir.files.push(file);
86
+ });
87
+ //console.dir(ret);
88
+ return JSX.createElement(FileTreeFolder, { ...props, tree: ret, level: 0 });
89
+ }
90
+ function FileTreeFolder(props) {
91
+ //console.dir(props.tree);
92
+ const dirs = Object.keys(props.tree.dirs).map(k => props.tree.dirs[k]);
93
+ const files = props.tree.files.filter(file => props.filter ? file.filePath.includes(props.filter) : true);
94
+ return JSX.createElement("ul", { style: `margin-left: calc(2 * var(--file-browser-separation-file));${props.level === 0 ? " padding: var(--file-browser-separation);" : ""}` },
95
+ JSX.createElement(JSX.Fragment, null,
96
+ props.tree.name !== "" ? JSX.createElement("div", { class: "file-browser-dir", style: "padding: 0;" },
97
+ JSX.createElement("p", { style: "margin: 0;" },
98
+ props.tree.name,
99
+ "/"),
100
+ JSX.createElement("button", { onclick: ev => {
101
+ ev.preventDefault();
102
+ ev.stopPropagation();
103
+ props.toggleCollapseDir(props.tree.fullName);
104
+ }, class: "btn-small", style: "margin: 0; margin-left: calc(2 * var(--file-browser-separation));" }, props.isDirCollapsed(props.tree.fullName) ? "+" : "-")) : JSX.createElement(JSX.Fragment, null),
105
+ props.isDirCollapsed(props.tree.fullName) ? JSX.createElement(JSX.Fragment, null) :
106
+ JSX.createElement(JSX.Fragment, null,
107
+ dirs.map(dir => {
108
+ return JSX.createElement(FileTreeFolder, { ...props, tree: dir, level: props.level + 1 });
109
+ }),
110
+ files.map(file => JSX.createElement(File, { closeFile: props.closeFile, current: props.current, fileName: file.fileName, filePath: file.filePath, hasErrors: props.hasErrors, hasFileContentChanged: props.hasFileContentChanged, isOpen: props.isOpen, openFile: props.openFile })))));
111
+ }
112
+ function File(props) {
113
+ const isOpen = props.isOpen(props.filePath);
114
+ return JSX.createElement("li", { class: `file-browser-file${props.hasErrors(props.filePath) ? " error" : ""}${props.hasFileContentChanged(props.filePath) ? " changed" : ""}${props.current === props.filePath ? " active" : ""}${isOpen ? " open" : ""}`, style: "display: flex; justify-content: flex-start; align-items: center; margin-left: calc(2 * var(--file-browser-separation-file));", onclick: ev => {
115
+ ev.preventDefault();
116
+ ev.stopPropagation();
117
+ props.openFile(props.filePath);
118
+ } },
119
+ JSX.createElement("p", { style: "margin: 0; padding: 0;" },
120
+ props.fileName,
121
+ props.hasFileContentChanged(props.filePath) ? "(*)" : ""),
122
+ JSX.createElement("button", { onclick: ev => {
123
+ ev.preventDefault();
124
+ ev.stopPropagation();
125
+ props.closeFile(props.filePath);
126
+ }, class: "btn-small", style: props.isOpen(props.filePath) ? "margin-left: calc(2 * var(--file-browser-separation-file));" : "display: none;", disabled: !props.isOpen(props.filePath) }, "X"));
127
+ }
@@ -0,0 +1,22 @@
1
+ interface FileEditorToolbar {
2
+ path: string;
3
+ language: string;
4
+ changed: boolean;
5
+ renameFile: (newName: string) => void;
6
+ deleteFile: () => void;
7
+ setlanguage: (newLang: string) => void;
8
+ saveFile: (reload?: boolean) => void;
9
+ revertFile: () => void;
10
+ closeFile: () => void;
11
+ togglePanel: (panel: string) => void;
12
+ isPanelVisible: (panel: string) => boolean;
13
+ disableLog?: boolean;
14
+ disablePreview?: boolean;
15
+ disableReload?: boolean;
16
+ }
17
+ export declare function FileEditorToolbar(props: FileEditorToolbar): JSX.Element;
18
+ export declare namespace FileEditorToolbar {
19
+ var asFragment: boolean;
20
+ var shadowInit: boolean;
21
+ }
22
+ export {};
@@ -0,0 +1,95 @@
1
+ import * as jsx from "@miqro/jsx";
2
+ import JSX from "@miqro/jsx";
3
+ import { SUPPORTED_LANGUAGES } from "../common/constants.js";
4
+ export function FileEditorToolbar(props) {
5
+ const renamingRef = jsx.useRef();
6
+ const refresh = jsx.useRefresh();
7
+ const [renaming, setrenaming] = jsx.useState(false);
8
+ jsx.useEffect(() => {
9
+ if (renamingRef.current) {
10
+ renamingRef.current.value = props.path;
11
+ renamingRef.current.focus();
12
+ refresh();
13
+ }
14
+ }, [renamingRef.current]);
15
+ jsx.useEffect(() => {
16
+ setrenaming(false);
17
+ }, [props.path]);
18
+ return JSX.createElement("div", { class: "file-editor-toolbar" },
19
+ JSX.createElement("div", { class: "file-editor-toolbar-row" },
20
+ JSX.createElement("button", { class: "file-editor-button btn", style: `${props.isPanelVisible("left") ? "display: none;" : "margin-right: var(--file-browser-separation);"}`, onclick: ev => {
21
+ ev.preventDefault();
22
+ props.togglePanel("left");
23
+ } }, ">>"),
24
+ JSX.createElement("button", { id: "save-btn", disabled: !props.changed, class: `btn success`, onclick: ev => {
25
+ ev.preventDefault();
26
+ props.saveFile();
27
+ } }, "save"),
28
+ props.disableReload ? JSX.createElement(JSX.Fragment, null) : JSX.createElement("button", { id: "savereload-btn", disabled: !props.changed, class: `btn info`, style: "margin-left: var(--file-browser-separation);", onclick: ev => {
29
+ ev.preventDefault();
30
+ props.saveFile(true);
31
+ } }, "save/reload"),
32
+ JSX.createElement("button", { id: "revert-btn", disabled: !props.changed, style: "margin-left: var(--file-browser-separation);", class: `btn warning`, onclick: ev => {
33
+ ev.preventDefault();
34
+ props.revertFile();
35
+ } }, "revert"),
36
+ JSX.createElement("button", { id: "close-btn", class: `btn danger`, style: "margin-left: var(--file-browser-separation);", onclick: ev => {
37
+ ev.preventDefault();
38
+ props.closeFile();
39
+ } }, "close"),
40
+ SUPPORTED_LANGUAGES.includes(props.language) ?
41
+ JSX.createElement("select", { value: props.language, oninput: ev => {
42
+ ev.preventDefault();
43
+ props.setlanguage(ev.target.value);
44
+ }, style: "margin: 0; padding: 0; margin-left: calc(2*var(--file-browser-separation-file));" }, SUPPORTED_LANGUAGES.map(language => JSX.createElement("option", { value: language }, language))) : JSX.createElement(JSX.Fragment, null)),
45
+ JSX.createElement("div", { class: "file-editor-toolbar-row" }, !renaming ?
46
+ JSX.createElement(JSX.Fragment, null,
47
+ JSX.createElement("p", { style: "margin-left:20px;", onclick: ev => {
48
+ ev.preventDefault();
49
+ setrenaming(true);
50
+ } }, props.path)) :
51
+ JSX.createElement(JSX.Fragment, null,
52
+ JSX.createElement("form", { style: "width: 100%; height: 100%; margin-right: calc(2 * var(--file-browser-separation));", onsubmit: ev => {
53
+ ev.preventDefault();
54
+ setrenaming(false);
55
+ if (renamingRef.current && renamingRef.current.value !== props.path) {
56
+ setrenaming(false);
57
+ props.renameFile(renamingRef.current.value);
58
+ }
59
+ } },
60
+ JSX.createElement("input", { style: "width: 100%; height: 100%;", onfocusout: ev => {
61
+ ev.preventDefault();
62
+ //setrenaming(false);
63
+ }, ref: renamingRef, onkeydown: (ev) => {
64
+ if (ev.keyCode === 27) {
65
+ // catch esc
66
+ ev.preventDefault();
67
+ setrenaming(false);
68
+ }
69
+ else {
70
+ refresh();
71
+ }
72
+ }, type: "text" })),
73
+ renamingRef.current && renamingRef.current.value !== props.path && renamingRef.current.value !== "" ?
74
+ JSX.createElement("button", { style: "margin-left: var(--file-browser-separation);", class: "btn info", onclick: ev => {
75
+ ev.preventDefault();
76
+ if (renamingRef.current) {
77
+ setrenaming(false);
78
+ props.renameFile(renamingRef.current.value);
79
+ }
80
+ } }, "rename file")
81
+ : JSX.createElement(JSX.Fragment, null),
82
+ JSX.createElement("button", { style: "margin-left: var(--file-browser-separation);", class: "btn danger", onclick: ev => {
83
+ ev.preventDefault();
84
+ if (renamingRef.current) {
85
+ setrenaming(false);
86
+ props.deleteFile();
87
+ }
88
+ } }, "delete file"),
89
+ JSX.createElement("button", { style: "margin-left: var(--file-browser-separation);", class: "btn", onclick: ev => {
90
+ ev.preventDefault();
91
+ setrenaming(false);
92
+ } }, "cancel"))));
93
+ }
94
+ FileEditorToolbar.asFragment = true;
95
+ FileEditorToolbar.shadowInit = false;