asljs-part-srv 0.1.1 → 0.1.2
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.
- package/README.md +2 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +33 -10
- package/dist/browser/plugin.d.ts +3 -1
- package/dist/browser/plugin.js +146 -4
- package/dist/project.js +1 -1
- package/dist/view.d.ts +9 -3
- package/dist/view.js +15 -4
- package/docs/part-srv.md +26 -9
- package/package.json +25 -7
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
editor in the browser, as a tree of folders, each opened as a tab of its
|
|
10
10
|
details - path, size, modification time - and the artefact definitions that
|
|
11
11
|
match it. Each `part.json` of the working directory, else of its folders, is a
|
|
12
|
-
project, chosen in a
|
|
13
|
-
serves the content of a file.
|
|
12
|
+
project, chosen in a selector that finds it by its title. It reads a project on
|
|
13
|
+
every request and never serves the content of a file.
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
package/dist/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type EditorApp, type EditorAppContext } from 'asljs-appbase';
|
|
2
2
|
/**
|
|
3
3
|
* The part application as an editor plugin: its projects, by `part.json`.
|
|
4
|
-
* Each `part.json` `
|
|
4
|
+
* Each `part.json` `findAppProjects` finds from the working directory is a
|
|
5
5
|
* project. A project's root, definitions and state folder come from its own
|
|
6
6
|
* `part.json` only, else the default, when it is first requested; no option
|
|
7
7
|
* or environment variable sets them, since one value cannot suit every
|
package/dist/app.js
CHANGED
|
@@ -1,15 +1,34 @@
|
|
|
1
|
-
import { configPath, configStrings, createEditorApp,
|
|
1
|
+
import { configPath, configStrings, createEditorApp, findAppProjects, findBrowserModules, stateFolder } from 'asljs-appbase';
|
|
2
2
|
import { CONFIG_FILE, resolveDefinitionSource } from 'asljs-part';
|
|
3
3
|
import { ICONS } from 'asljs-ui-editor';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { createProjectHandler } from './view.js';
|
|
7
7
|
/**
|
|
8
|
-
* The style of an artefact's details:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The style of an artefact's details: the tab, the details with the preview
|
|
9
|
+
* of a file on their right and an action bar along its bottom, as the graph
|
|
10
|
+
* of `rq-srv` has them; a scrolling view of the details, their table of the
|
|
11
|
+
* location, path, size and modification time, and their list of
|
|
12
|
+
* definitions; and the file's content in the preview.
|
|
11
13
|
*/
|
|
12
|
-
const STYLE = `.part-
|
|
14
|
+
const STYLE = `.part-document { height: 100%; display: flex; flex-direction: column; background: #fff; }
|
|
15
|
+
.part-stage { flex: 1; min-height: 0; display: flex; }
|
|
16
|
+
.part-view { flex: 1; min-width: 0; box-sizing: border-box; overflow: auto; padding: 0.75rem 1rem; line-height: 1.5; }
|
|
17
|
+
asljs-diagram-preview { width: min(40rem, 45vw); border-left: 1px solid #ddd; background: #fff; }
|
|
18
|
+
.asljs-diagram-preview-splitter { background: transparent; transition: background 0.15s; }
|
|
19
|
+
.asljs-diagram-preview-splitter:hover, .asljs-diagram-preview-splitter:focus-visible { background: #1a73e8; outline: none; }
|
|
20
|
+
.asljs-diagram-preview-placeholder, .asljs-diagram-preview-body > .asljs-plugin-note { margin: 1rem; color: #757575; }
|
|
21
|
+
.part-actionbar { display: flex; align-items: center; justify-content: flex-end; padding: 2px 8px; background: #f3f4f6; border-top: 1px solid #ddd; }
|
|
22
|
+
.asljs-diagram-preview-tabs { gap: 2px; }
|
|
23
|
+
.asljs-diagram-preview-tab { border: 0; border-radius: 4px; background: none; padding: 3px 10px; font: inherit; color: #555; cursor: pointer; }
|
|
24
|
+
.asljs-diagram-preview-tab:hover:not(:disabled) { background: #e5e7eb; }
|
|
25
|
+
.asljs-diagram-preview-tab[aria-selected="true"] { background: #dbe7fd; color: #1a73e8; }
|
|
26
|
+
.asljs-diagram-preview-toggle { width: 28px; height: 28px; display: grid; place-items: center; border: 0; border-radius: 4px; background: none; color: #555; cursor: pointer; }
|
|
27
|
+
.asljs-diagram-preview-toggle:hover:not(:disabled) { background: #e5e7eb; }
|
|
28
|
+
.asljs-diagram-preview-toggle[aria-expanded="true"] { color: #1a73e8; }
|
|
29
|
+
.part-content { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: 0.75rem 1rem; box-sizing: border-box; }
|
|
30
|
+
.part-text { font-family: ui-monospace, monospace; font-size: 0.85rem; line-height: 1.45; white-space: pre; }
|
|
31
|
+
.part-image { max-width: 100%; }
|
|
13
32
|
.part-details { border-collapse: collapse; margin: 0 0 1rem; }
|
|
14
33
|
.part-details th { text-align: left; color: #555; font-weight: normal; padding: 2px 1rem 2px 0; vertical-align: top; }
|
|
15
34
|
.part-details td { padding: 2px 0; word-break: break-all; }
|
|
@@ -18,15 +37,16 @@ const STYLE = `.part-view { height: 100%; box-sizing: border-box; overflow: auto
|
|
|
18
37
|
.part-note { color: #757575; }`;
|
|
19
38
|
/**
|
|
20
39
|
* The part application as an editor plugin: its projects, by `part.json`.
|
|
21
|
-
* Each `part.json` `
|
|
40
|
+
* Each `part.json` `findAppProjects` finds from the working directory is a
|
|
22
41
|
* project. A project's root, definitions and state folder come from its own
|
|
23
42
|
* `part.json` only, else the default, when it is first requested; no option
|
|
24
43
|
* or environment variable sets them, since one value cannot suit every
|
|
25
44
|
* project.
|
|
26
45
|
*/
|
|
27
46
|
export async function createApp(context) {
|
|
28
|
-
const projects = await
|
|
29
|
-
return createEditorApp({
|
|
47
|
+
const projects = await findAppProjects(context, CONFIG_FILE, { required: true });
|
|
48
|
+
return createEditorApp({
|
|
49
|
+
id: 'part',
|
|
30
50
|
title: 'Part',
|
|
31
51
|
icon: ICONS.document,
|
|
32
52
|
base: context.base,
|
|
@@ -35,8 +55,11 @@ export async function createApp(context) {
|
|
|
35
55
|
plugin: fileURLToPath(new URL('./browser/plugin.js', import.meta.url)),
|
|
36
56
|
// The page loads the plugin's packages, and what they import, from
|
|
37
57
|
// the server.
|
|
38
|
-
modules: findBrowserModules(['asljs-ui-
|
|
39
|
-
|
|
58
|
+
modules: findBrowserModules(['asljs-ui-diagram',
|
|
59
|
+
'asljs-ui-editor',
|
|
60
|
+
'asljs-ui-markdown'], path.dirname(fileURLToPath(import.meta.url))),
|
|
61
|
+
style: STYLE
|
|
62
|
+
});
|
|
40
63
|
}
|
|
41
64
|
/**
|
|
42
65
|
* The project part reads in an application project: `project`,
|
package/dist/browser/plugin.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { type EditorPluginFactory } from 'asljs-ui-editor';
|
|
|
5
5
|
* `_/data.json` once, when it is made. Its Artefacts operation is a
|
|
6
6
|
* `pathTree` of their locations, grouped by `locationSegments`, its groups
|
|
7
7
|
* folded. Choosing an entry opens its details as a document whose id is
|
|
8
|
-
* `part:<location>`, rendered here from `_/artefact.json
|
|
8
|
+
* `part:<location>`, rendered here from `_/artefact.json`, with the preview
|
|
9
|
+
* of a file on their right, `previewOf`, in an `asljs-diagram-preview`
|
|
10
|
+
* whose state, shared by the tabs, this browser keeps. Its search finds
|
|
9
11
|
* artefacts by location (`searchMatcher`), at most `MAX_RESULTS` of them: a
|
|
10
12
|
* file by its path, with or without `file:`, and another artefact by its
|
|
11
13
|
* location. Every URL is relative to the project's `base`.
|
package/dist/browser/plugin.js
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { observable } from 'asljs-observable';
|
|
2
|
+
import { createPreviewModel } from 'asljs-ui-diagram';
|
|
3
|
+
import { element, fetchJson, ICONS, note, pathHref, pathTree, projectPath, searchMatcher, tree } from 'asljs-ui-editor';
|
|
4
|
+
import { markdownDocument } from 'asljs-ui-markdown';
|
|
2
5
|
import { locationSegments } from './locations.js';
|
|
3
6
|
/**
|
|
4
7
|
* How many matches a search shows.
|
|
5
8
|
*/
|
|
6
9
|
const MAX_RESULTS = 200;
|
|
10
|
+
/**
|
|
11
|
+
* Where the browser keeps the state of the details' preview.
|
|
12
|
+
*/
|
|
13
|
+
const LAYOUT_KEY = 'part-view-layout';
|
|
14
|
+
/**
|
|
15
|
+
* The largest file the preview shows, in bytes.
|
|
16
|
+
*/
|
|
17
|
+
const PREVIEW_LIMIT = 1024 * 1024;
|
|
7
18
|
/**
|
|
8
19
|
* The plugin of a project, the browser module of `asljs-ui-editor` the
|
|
9
20
|
* server serves. It loads the project's files and other artefacts from
|
|
10
21
|
* `_/data.json` once, when it is made. Its Artefacts operation is a
|
|
11
22
|
* `pathTree` of their locations, grouped by `locationSegments`, its groups
|
|
12
23
|
* folded. Choosing an entry opens its details as a document whose id is
|
|
13
|
-
* `part:<location>`, rendered here from `_/artefact.json
|
|
24
|
+
* `part:<location>`, rendered here from `_/artefact.json`, with the preview
|
|
25
|
+
* of a file on their right, `previewOf`, in an `asljs-diagram-preview`
|
|
26
|
+
* whose state, shared by the tabs, this browser keeps. Its search finds
|
|
14
27
|
* artefacts by location (`searchMatcher`), at most `MAX_RESULTS` of them: a
|
|
15
28
|
* file by its path, with or without `file:`, and another artefact by its
|
|
16
29
|
* location. Every URL is relative to the project's `base`.
|
|
@@ -30,11 +43,82 @@ const createPlugin = async ({ id, base, model }) => {
|
|
|
30
43
|
document: location => documentOf(location, locationSegments(location).at(-1)),
|
|
31
44
|
open: false }));
|
|
32
45
|
};
|
|
46
|
+
// The preview of the details: whether it is shown, its mode and its
|
|
47
|
+
// width, kept in this browser.
|
|
48
|
+
let stored = {};
|
|
49
|
+
try {
|
|
50
|
+
stored =
|
|
51
|
+
JSON.parse(localStorage.getItem(LAYOUT_KEY) ?? '{}');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Without storage, the preview starts open.
|
|
55
|
+
}
|
|
56
|
+
const preview = createPreviewModel(stored.preview
|
|
57
|
+
?? { open: true,
|
|
58
|
+
mode: 'view' });
|
|
59
|
+
preview.on('change', () => {
|
|
60
|
+
try {
|
|
61
|
+
localStorage.setItem(LAYOUT_KEY, JSON.stringify({ preview: { open: preview.open,
|
|
62
|
+
mode: preview.mode,
|
|
63
|
+
width: preview.width } }));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Without storage, the preview lasts until the page is reloaded.
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// A link of a previewed markdown file to another file of the project
|
|
70
|
+
// opens that file's details.
|
|
71
|
+
const openLink = (url) => {
|
|
72
|
+
const relative = projectPath(base, url);
|
|
73
|
+
const file = relative === null
|
|
74
|
+
? undefined
|
|
75
|
+
: files.find(candidate => candidate.replaceAll('\\', '/')
|
|
76
|
+
=== relative);
|
|
77
|
+
if (file === undefined) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
model.open(documentOf(`file:${file}`, locationSegments(`file:${file}`).at(-1)));
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
33
83
|
const renderDocument = (opened) => {
|
|
34
84
|
const { location } = opened.data;
|
|
35
|
-
const view = element('div', 'part-
|
|
85
|
+
const view = element('div', 'part-document');
|
|
86
|
+
const details = element('div', 'part-view');
|
|
87
|
+
// The panel shows the file of the details, once they are read: its
|
|
88
|
+
// selection is the file's path, and none for another artefact.
|
|
89
|
+
const shown = observable({ selection: [] });
|
|
90
|
+
let file = null;
|
|
91
|
+
const modes = [{ id: 'view',
|
|
92
|
+
title: 'View',
|
|
93
|
+
isEnabled: selection => selection.length === 1
|
|
94
|
+
&& file !== null,
|
|
95
|
+
render: async () => await previewOf(base, file, openLink) }];
|
|
96
|
+
const panel = document.createElement('asljs-diagram-preview');
|
|
97
|
+
panel.placeholder = '';
|
|
98
|
+
panel.modes = modes;
|
|
99
|
+
panel.model = shown;
|
|
100
|
+
panel.preview = preview;
|
|
101
|
+
const stage = element('div', 'part-stage');
|
|
102
|
+
stage.append(details, panel);
|
|
103
|
+
const tabs = document.createElement('asljs-diagram-preview-tabs');
|
|
104
|
+
tabs.modes = modes;
|
|
105
|
+
tabs.preview = preview;
|
|
106
|
+
const bar = element('div', 'part-actionbar');
|
|
107
|
+
bar.append(tabs);
|
|
108
|
+
view.append(stage, bar);
|
|
36
109
|
void fetchJson(base, `_/artefact.json?location=${encodeURIComponent(location)}`)
|
|
37
|
-
.then(
|
|
110
|
+
.then((read) => {
|
|
111
|
+
details.append(...detailsView(read));
|
|
112
|
+
if (read.path === null) {
|
|
113
|
+
panel.placeholder =
|
|
114
|
+
'Only a file has a preview.';
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
file = read;
|
|
118
|
+
}
|
|
119
|
+
shown.selection =
|
|
120
|
+
[location];
|
|
121
|
+
}, (error) => details.append(note(error instanceof Error
|
|
38
122
|
? error.message
|
|
39
123
|
: String(error))));
|
|
40
124
|
return view;
|
|
@@ -47,6 +131,9 @@ const createPlugin = async ({ id, base, model }) => {
|
|
|
47
131
|
icon: ICONS.document,
|
|
48
132
|
render: artefactsPanel }],
|
|
49
133
|
renderDocument,
|
|
134
|
+
documentOf: location => documentOf(location, location
|
|
135
|
+
.split(/[\\/]/)
|
|
136
|
+
.at(-1)),
|
|
50
137
|
search: async (query) => {
|
|
51
138
|
const matches = searchMatcher(query);
|
|
52
139
|
return [...files
|
|
@@ -86,6 +173,61 @@ function detailsView(details) {
|
|
|
86
173
|
element('h2', undefined, 'Definitions'),
|
|
87
174
|
definitionsView(details.definitions)];
|
|
88
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* The preview of a file, read from the server as it is: an image as an image,
|
|
178
|
+
* a markdown file rendered, `openLink` opening its links to other files, and
|
|
179
|
+
* other text as it is; a note for a binary file, one over `PREVIEW_LIMIT`,
|
|
180
|
+
* and one the server does not serve.
|
|
181
|
+
*/
|
|
182
|
+
async function previewOf(base, file, openLink) {
|
|
183
|
+
if ((file.size
|
|
184
|
+
?? 0)
|
|
185
|
+
> PREVIEW_LIMIT) {
|
|
186
|
+
return note(`The file is too large to preview, ${file.size} bytes.`);
|
|
187
|
+
}
|
|
188
|
+
const url = new URL(pathHref(base, file.path), location.href);
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(url);
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
return note(`The file has no preview: ${response.status} ${response.statusText}`
|
|
193
|
+
.trim());
|
|
194
|
+
}
|
|
195
|
+
if ((response.headers.get('content-type') ?? '')
|
|
196
|
+
.startsWith('image/')) {
|
|
197
|
+
const image = element('img', 'part-image');
|
|
198
|
+
image.src = url.href;
|
|
199
|
+
image.alt = file.name;
|
|
200
|
+
const frame = element('div', 'part-content');
|
|
201
|
+
frame.append(image);
|
|
202
|
+
return frame;
|
|
203
|
+
}
|
|
204
|
+
let text;
|
|
205
|
+
try {
|
|
206
|
+
text =
|
|
207
|
+
new TextDecoder('utf-8', { fatal: true })
|
|
208
|
+
.decode(await response.arrayBuffer());
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
text = '\0';
|
|
212
|
+
}
|
|
213
|
+
if (text.includes('\0')) {
|
|
214
|
+
return note('A binary file has no preview.');
|
|
215
|
+
}
|
|
216
|
+
if (/\.md$/i.test(file.path)) {
|
|
217
|
+
return markdownDocument({ url,
|
|
218
|
+
load: async () => ({ body: text }),
|
|
219
|
+
openLink });
|
|
220
|
+
}
|
|
221
|
+
const code = element('pre', 'part-content part-text');
|
|
222
|
+
code.append(element('code', undefined, text));
|
|
223
|
+
return code;
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return note(error instanceof Error
|
|
227
|
+
? error.message
|
|
228
|
+
: String(error));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
89
231
|
function row(label, value) {
|
|
90
232
|
const made = element('tr');
|
|
91
233
|
const cell = element('td');
|
package/dist/project.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isInside } from 'asljs-locator';
|
|
2
1
|
import { toDisplayPath } from 'asljs-appbase';
|
|
2
|
+
import { isInside } from 'asljs-locator';
|
|
3
3
|
import { FILE_SCHEME, providersFactory } from 'asljs-part';
|
|
4
4
|
import { stat } from 'node:fs/promises';
|
|
5
5
|
import path from 'node:path';
|
package/dist/view.d.ts
CHANGED
|
@@ -13,8 +13,14 @@ export declare const DATA_PATH = "/_/data.json";
|
|
|
13
13
|
/**
|
|
14
14
|
* The handler of a project, relative to where it is mounted. It answers
|
|
15
15
|
* data, which the plugin renders: `DATA_PATH` the files and the other
|
|
16
|
-
* artefacts of the project,
|
|
17
|
-
*
|
|
18
|
-
* is
|
|
16
|
+
* artefacts of the project, `ARTEFACT_PATH` the details of an artefact, or
|
|
17
|
+
* 404 with `{ error }` saying why there is none, and a file of the project
|
|
18
|
+
* root by its path, as it is, for the preview, unless `isServed` refuses it.
|
|
19
|
+
* The project is read on every request.
|
|
19
20
|
*/
|
|
20
21
|
export declare function createProjectHandler(project: Project): RequestHandler;
|
|
22
|
+
/**
|
|
23
|
+
* Whether a file of the project is served, by its path relative to the
|
|
24
|
+
* root: not one in a dot folder, a dot file or one under `node_modules`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isServed(relative: string): boolean;
|
package/dist/view.js
CHANGED
|
@@ -13,9 +13,10 @@ export const DATA_PATH = '/_/data.json';
|
|
|
13
13
|
/**
|
|
14
14
|
* The handler of a project, relative to where it is mounted. It answers
|
|
15
15
|
* data, which the plugin renders: `DATA_PATH` the files and the other
|
|
16
|
-
* artefacts of the project,
|
|
17
|
-
*
|
|
18
|
-
* is
|
|
16
|
+
* artefacts of the project, `ARTEFACT_PATH` the details of an artefact, or
|
|
17
|
+
* 404 with `{ error }` saying why there is none, and a file of the project
|
|
18
|
+
* root by its path, as it is, for the preview, unless `isServed` refuses it.
|
|
19
|
+
* The project is read on every request.
|
|
19
20
|
*/
|
|
20
21
|
export function createProjectHandler(project) {
|
|
21
22
|
return createHandler({ folder: project.root,
|
|
@@ -28,5 +29,15 @@ export function createProjectHandler(project) {
|
|
|
28
29
|
return jsonReply(404, { error: `${messageOf(error)}.` });
|
|
29
30
|
}
|
|
30
31
|
} },
|
|
31
|
-
allow:
|
|
32
|
+
allow: isServed });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether a file of the project is served, by its path relative to the
|
|
36
|
+
* root: not one in a dot folder, a dot file or one under `node_modules`.
|
|
37
|
+
*/
|
|
38
|
+
export function isServed(relative) {
|
|
39
|
+
return !relative
|
|
40
|
+
.split('/')
|
|
41
|
+
.some(segment => segment.startsWith('.')
|
|
42
|
+
|| segment === 'node_modules');
|
|
32
43
|
}
|
package/docs/part-srv.md
CHANGED
|
@@ -26,7 +26,10 @@ folder, `--working-dir` or else the working directory:
|
|
|
26
26
|
path from the working folder, leaving out what `.gitignore` ignores, `.git`
|
|
27
27
|
and `node_modules`.
|
|
28
28
|
- With none, the working folder is the one project, with the nearest `part.json`
|
|
29
|
-
of a parent
|
|
29
|
+
of a parent.
|
|
30
|
+
- With no `part.json` there either, `part-srv` stops with an error, since a
|
|
31
|
+
project's `part.json` says which artefacts it supports; `factory-srv` serves
|
|
32
|
+
its other applications.
|
|
30
33
|
|
|
31
34
|
The root, the definitions and the state folder of each project come from its own
|
|
32
35
|
`part.json` only, `project`, `definitions` and `stateDir`, its paths resolved
|
|
@@ -49,8 +52,9 @@ server that shows several applications serves it at `/part/`.
|
|
|
49
52
|
icon opens its panel next to the column; the button at the bottom of the
|
|
50
53
|
column hides and shows the panel. Dragging the panel's inner edge resizes it,
|
|
51
54
|
and the browser remembers the width, and the project shown, for the next
|
|
52
|
-
visit. With more than one project, a
|
|
53
|
-
|
|
55
|
+
visit. With more than one project, a selector heads the panel, its filter box
|
|
56
|
+
finding a project by its title; choosing another project shows its artefacts,
|
|
57
|
+
and the details of the one before stay open.
|
|
54
58
|
- **Artefacts** - the artefacts of the project as one tree of their locations,
|
|
55
59
|
folded at first: grouped by scheme, such as `file:`, `npm:` or `git:`, then
|
|
56
60
|
by each segment of the path, anything between `/` or `\`, as `npm:` >
|
|
@@ -75,6 +79,16 @@ server that shows several applications serves it at `/part/`.
|
|
|
75
79
|
first paragraph of its description; a file no definition matches says so. A
|
|
76
80
|
file that does not exist, a path outside the project and an unknown location
|
|
77
81
|
give a note that says so.
|
|
82
|
+
|
|
83
|
+
The details have a preview of their file on their right, as the graph of
|
|
84
|
+
`rq-srv` has of its node: an `asljs-diagram-preview` of `asljs-ui-diagram`,
|
|
85
|
+
read from the server as the file is. An image is shown, a markdown file is
|
|
86
|
+
rendered - a link to another file of the project opens that file's details -
|
|
87
|
+
and other text is shown as it is. A binary file, one over 1 MiB, one the
|
|
88
|
+
server does not serve, and an artefact that is not a file have a note instead.
|
|
89
|
+
The View tab at the right of the action bar under the details shows it, and
|
|
90
|
+
the button after it shows and hides it; dragging its left edge resizes it, and
|
|
91
|
+
the browser remembers its width and whether it is shown, for every tab.
|
|
78
92
|
- `/_/app.json` - the application and its projects, each with the path it is
|
|
79
93
|
served at; `/_/plugin/plugin.js` - the plugin the editor loads, a browser
|
|
80
94
|
module, with the modules beside it under `/_/plugin/`.
|
|
@@ -90,12 +104,15 @@ server that shows several applications serves it at `/part/`.
|
|
|
90
104
|
a `file:` location, a path relative to the project root, or the location of
|
|
91
105
|
another artefact. When there is none, the answer is 404 with `{ error }`,
|
|
92
106
|
the message the details tab shows.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
- `/p/<project>/<path>` - a file of the project root, by its path, as it is,
|
|
108
|
+
for the preview: a `.md` file as `text/markdown`. A file in a dot folder, a
|
|
109
|
+
dot file and a file under `node_modules` are not found.
|
|
110
|
+
|
|
111
|
+
The server renders nothing: it answers the details and the files as they are,
|
|
112
|
+
and the preview is made in the browser. The project is read on every request, so
|
|
113
|
+
a reload shows the files as they are. The page loads `asljs-ui-editor`,
|
|
114
|
+
`asljs-ui-diagram`, `asljs-ui-markdown` and what they import from the server,
|
|
115
|
+
under `/modules/`, so it needs no network.
|
|
99
116
|
|
|
100
117
|
## Options
|
|
101
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asljs-part-srv",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The part-srv web server: a project's files in an editor, each with its details and the artefact definitions that match it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"asljs": {
|
|
@@ -22,22 +22,37 @@
|
|
|
22
22
|
"dist/**",
|
|
23
23
|
"docs/**",
|
|
24
24
|
"README.md",
|
|
25
|
-
"LICENSE.md"
|
|
25
|
+
"LICENSE.md",
|
|
26
|
+
"!dist/*.tsbuildinfo"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
28
29
|
"flint": "toolkit flint",
|
|
29
|
-
"clean": "
|
|
30
|
-
"
|
|
31
|
-
"build
|
|
30
|
+
"clean:build": "node ../../../scripts/cleanBuildOutDir.js",
|
|
31
|
+
"clean:dist": "node ../../../scripts/cleanDistOutDir.js",
|
|
32
|
+
"compile:build": "npm run codegen --if-present && tsc -p tsconfig.build.json",
|
|
33
|
+
"compile:dist": "npm run codegen --if-present && tsc -p tsconfig.dist.json",
|
|
34
|
+
"build:dist": "node ../../../scripts/buildDistDeps.js && npm run compile:dist",
|
|
32
35
|
"typecheck": "npm run codegen --if-present && tsc -p tsconfig.build.json --noEmit",
|
|
33
36
|
"guard:clean-git": "toolkit ensure-clean-working-directory",
|
|
34
|
-
"prepack": "npm run clean && npm run build:dist && npm run build && npm run test && npm run guard:clean-git",
|
|
37
|
+
"prepack": "npm run clean:build && npm run clean:dist && npm run build:dist && npm run compile:build && npm run test && npm run guard:clean-git",
|
|
35
38
|
"postpublish": "toolkit tag-release-revision",
|
|
36
39
|
"test": "node --test --enable-source-maps \"build/**/*.test.js\"",
|
|
37
40
|
"test:watch": "node --watch --test \"build/**/*.test.js\"",
|
|
38
41
|
"release:patch": "toolkit release-patch",
|
|
39
|
-
"
|
|
42
|
+
"build": "npm run clean:build && npm run flint && npm run typecheck && npm run compile:build && npm run test"
|
|
40
43
|
},
|
|
44
|
+
"distDependencies": [
|
|
45
|
+
"asljs-appbase",
|
|
46
|
+
"asljs-locator",
|
|
47
|
+
"asljs-logging",
|
|
48
|
+
"asljs-observable",
|
|
49
|
+
"asljs-part",
|
|
50
|
+
"asljs-testing",
|
|
51
|
+
"asljs-tmpdir",
|
|
52
|
+
"asljs-ui-diagram",
|
|
53
|
+
"asljs-ui-editor",
|
|
54
|
+
"asljs-ui-markdown"
|
|
55
|
+
],
|
|
41
56
|
"keywords": [
|
|
42
57
|
"artefacts",
|
|
43
58
|
"project",
|
|
@@ -57,8 +72,11 @@
|
|
|
57
72
|
"asljs-locator": "^0.1.1",
|
|
58
73
|
"asljs-logging": "^0.1.1",
|
|
59
74
|
"asljs-appbase": "^0.1.3",
|
|
75
|
+
"asljs-observable": "^0.5.6",
|
|
60
76
|
"asljs-part": "^0.3.2",
|
|
77
|
+
"asljs-ui-diagram": "^0.1.2",
|
|
61
78
|
"asljs-ui-editor": "^0.1.1",
|
|
79
|
+
"asljs-ui-markdown": "^0.1.1",
|
|
62
80
|
"commander": "^15.0.0"
|
|
63
81
|
},
|
|
64
82
|
"devDependencies": {
|