tsondb 0.7.7 → 0.7.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.
- package/dist/src/node/server/api/git.js +156 -24
- package/dist/src/node/server/index.js +4 -0
- package/dist/src/node/utils/instanceOperations.d.ts +1 -1
- package/dist/src/node/utils/instanceOperations.js +2 -2
- package/dist/src/shared/api.d.ts +17 -1
- package/dist/src/shared/utils/git.d.ts +4 -0
- package/dist/src/shared/utils/git.js +6 -1
- package/dist/src/shared/utils/markdown.d.ts +36 -17
- package/dist/src/shared/utils/markdown.js +79 -23
- package/dist/src/shared/utils/object.d.ts +1 -0
- package/dist/src/shared/utils/object.js +1 -0
- package/dist/src/web/api/git.d.ts +5 -1
- package/dist/src/web/api/git.js +6 -2
- package/dist/src/web/components/ContextProviderWrapper.d.ts +7 -0
- package/dist/src/web/components/ContextProviderWrapper.js +5 -0
- package/dist/src/web/components/InstanceRouteSkeleton.d.ts +1 -0
- package/dist/src/web/components/InstanceRouteSkeleton.js +8 -4
- package/dist/src/web/components/LoadingOverlay.d.ts +1 -0
- package/dist/src/web/components/LoadingOverlay.js +3 -0
- package/dist/src/web/components/ModalDialog.js +4 -3
- package/dist/src/web/components/git/Git.js +47 -0
- package/dist/src/web/components/git/GitBranchManager.d.ts +7 -0
- package/dist/src/web/components/git/GitBranchManager.js +17 -0
- package/dist/src/web/components/git/GitFileList.d.ts +17 -0
- package/dist/src/web/components/git/GitFileList.js +11 -0
- package/dist/src/web/components/git/GitFileManager.d.ts +8 -0
- package/dist/src/web/components/git/GitFileManager.js +34 -0
- package/dist/src/web/components/git/GitStatusIndicator.d.ts +7 -0
- package/dist/src/web/components/git/GitStatusIndicator.js +6 -0
- package/dist/src/web/components/typeInputs/StringTypeInput.js +1 -1
- package/dist/src/web/context/entities.d.ts +6 -5
- package/dist/src/web/context/git.d.ts +2 -1
- package/dist/src/web/context/gitClient.d.ts +2 -0
- package/dist/src/web/context/gitClient.js +2 -0
- package/dist/src/web/hooks/useGitClient.d.ts +37 -0
- package/dist/src/web/hooks/useGitClient.js +313 -0
- package/dist/src/web/index.js +6 -2
- package/dist/src/web/routes/CreateInstance.js +2 -1
- package/dist/src/web/routes/Entity.js +10 -4
- package/dist/src/web/routes/Instance.js +2 -1
- package/dist/src/web/signals/loading.d.ts +2 -0
- package/dist/src/web/signals/loading.js +11 -0
- package/dist/src/web/utils/BlockMarkdown.js +1 -1
- package/dist/src/web/utils/debug.d.ts +1 -0
- package/dist/src/web/utils/debug.js +4 -0
- package/package.json +3 -3
- package/public/css/styles.css +319 -229
- package/dist/src/web/components/Git.js +0 -164
- /package/dist/src/web/components/{Git.d.ts → git/Git.d.ts} +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { omitUndefinedKeys } from "./object.js";
|
|
2
|
+
import { assertExhaustive } from "./typeSafety.js";
|
|
1
3
|
const codeRule = {
|
|
2
4
|
pattern: /`(.*?)`/,
|
|
3
5
|
map: result => ({
|
|
@@ -122,13 +124,12 @@ const parsedAttributesLength = (rawAttributes) => rawAttributes.reduce((sum, att
|
|
|
122
124
|
: attr.name.length + attr.separator.length + attr.rawValue.length), 0);
|
|
123
125
|
const attributedRule = {
|
|
124
126
|
pattern: /(?<!\\)\^\[(.*?[^\\])\]\(((?:\w+: *(?:true|false|\d+(?:\.\d+)?|"(.*?)(?<!\\)"|'(.*?)(?<!\\)'))(?:, *\w+: *(?:true|false|\d+(?:\.\d+)?|"(.*?)(?<!\\)"|'(.*?)(?<!\\)'))*)\)/,
|
|
125
|
-
map: (
|
|
127
|
+
map: ([_res, content = "", attributesText = ""], parseInside) => ({
|
|
126
128
|
kind: "attributed",
|
|
127
|
-
attributes: mapAttributesToObject(parseAttributes(
|
|
128
|
-
content: parseInside(
|
|
129
|
+
attributes: mapAttributesToObject(parseAttributes(attributesText)),
|
|
130
|
+
content: parseInside(content),
|
|
129
131
|
}),
|
|
130
|
-
mapHighlighting: (
|
|
131
|
-
const attributesText = result[2] ?? "";
|
|
132
|
+
mapHighlighting: ([_res, content = "", attributesText = ""], parseInside) => {
|
|
132
133
|
const attributes = parseAttributes(attributesText);
|
|
133
134
|
const length = parsedAttributesLength(attributes);
|
|
134
135
|
const unparsedText = attributesText.length > length
|
|
@@ -139,7 +140,7 @@ const attributedRule = {
|
|
|
139
140
|
attributes: mapAttributesToObject(attributes),
|
|
140
141
|
content: [
|
|
141
142
|
textNode("^["),
|
|
142
|
-
...parseInside(
|
|
143
|
+
...parseInside(content),
|
|
143
144
|
textNode("]("),
|
|
144
145
|
...mapAttributesToNodes(attributes),
|
|
145
146
|
...unparsedText,
|
|
@@ -154,7 +155,7 @@ const textNode = (content) => ({
|
|
|
154
155
|
});
|
|
155
156
|
const parseEscapedCharacters = (text) => text.replace(/\\([*_`[\]()\\])/g, "$1");
|
|
156
157
|
const textRule = {
|
|
157
|
-
pattern:
|
|
158
|
+
pattern: /.+/s,
|
|
158
159
|
map: result => ({
|
|
159
160
|
kind: "text",
|
|
160
161
|
content: parseEscapedCharacters(result[0]),
|
|
@@ -225,13 +226,13 @@ const listRule = {
|
|
|
225
226
|
};
|
|
226
227
|
const paragraphRule = {
|
|
227
228
|
pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(\n{2,}|\s*$)/,
|
|
228
|
-
map:
|
|
229
|
+
map: ([_res, content = "", _trailingWhitespace]) => ({
|
|
229
230
|
kind: "paragraph",
|
|
230
|
-
content: parseInlineMarkdown(
|
|
231
|
+
content: parseInlineMarkdown(content, false),
|
|
231
232
|
}),
|
|
232
|
-
mapHighlighting:
|
|
233
|
-
...parseInlineMarkdown(
|
|
234
|
-
...nodesForTrailingWhitespace(
|
|
233
|
+
mapHighlighting: ([_res, content = "", trailingWhitespace]) => [
|
|
234
|
+
...parseInlineMarkdown(content, true),
|
|
235
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
235
236
|
],
|
|
236
237
|
};
|
|
237
238
|
const headingRule = {
|
|
@@ -253,26 +254,35 @@ const tableMarker = (text) => ({
|
|
|
253
254
|
content: text,
|
|
254
255
|
});
|
|
255
256
|
const tableRule = {
|
|
256
|
-
pattern: /^(\|
|
|
257
|
-
map:
|
|
257
|
+
pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)\|.+?)*(?<!\\)\|?)+)(\n{2,}|$)/,
|
|
258
|
+
map: ([_res, _captionMarkerStart, caption, _captionMarkerEnd, _headerMarkerStart, headers, _headerMarkerEnd, _bodySeparators, body, _trailingWhitespace,]) => omitUndefinedKeys({
|
|
258
259
|
kind: "table",
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
caption: caption !== undefined ? parseInlineMarkdown(caption.trim(), false) : undefined,
|
|
261
|
+
header: headers?.split("|").map(th => parseInlineMarkdown(th.trim(), false)) ?? [],
|
|
262
|
+
rows: body
|
|
261
263
|
?.split("\n")
|
|
262
264
|
.slice(1)
|
|
263
265
|
.map(tr => removeSurroundingPipes(tr)
|
|
264
266
|
.split("|")
|
|
265
267
|
.map(tc => parseInlineMarkdown(tc.trim(), false))) ?? [],
|
|
266
268
|
}),
|
|
267
|
-
mapHighlighting:
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
mapHighlighting: ([_res, captionMarkerStart, caption, captionMarkerEnd, headerMarkerStart, headers, headerMarkerEnd, bodySeparators, body, trailingWhitespace,]) => [
|
|
270
|
+
...(caption !== undefined
|
|
271
|
+
? [
|
|
272
|
+
tableMarker(captionMarkerStart ?? ""),
|
|
273
|
+
...parseInlineMarkdown(caption, true),
|
|
274
|
+
tableMarker(captionMarkerEnd ?? ""),
|
|
275
|
+
textNode("\n"),
|
|
276
|
+
]
|
|
277
|
+
: []),
|
|
278
|
+
tableMarker(headerMarkerStart ?? ""),
|
|
279
|
+
...(headers
|
|
270
280
|
?.split("|")
|
|
271
281
|
.flatMap((th, i) => i === 0
|
|
272
282
|
? parseInlineMarkdown(th, true)
|
|
273
283
|
: [tableMarker("|"), ...parseInlineMarkdown(th, true)]) ?? []),
|
|
274
|
-
tableMarker((
|
|
275
|
-
...(
|
|
284
|
+
tableMarker((headerMarkerEnd ?? "") + "\n" + (bodySeparators ?? "")),
|
|
285
|
+
...(body
|
|
276
286
|
?.split("\n")
|
|
277
287
|
.slice(1)
|
|
278
288
|
.flatMap((tr) => [
|
|
@@ -283,7 +293,7 @@ const tableRule = {
|
|
|
283
293
|
? parseInlineMarkdown(tc, true)
|
|
284
294
|
: [tableMarker("|"), ...parseInlineMarkdown(tc, true)]),
|
|
285
295
|
]) ?? []),
|
|
286
|
-
...nodesForTrailingWhitespace(
|
|
296
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
287
297
|
],
|
|
288
298
|
};
|
|
289
299
|
const blockRules = [headingRule, tableRule, listRule, paragraphRule];
|
|
@@ -310,4 +320,50 @@ const parseForBlockRules = (rules, text, ruleParser, remainingRules = rules) =>
|
|
|
310
320
|
}
|
|
311
321
|
};
|
|
312
322
|
export const parseBlockMarkdown = (text) => parseForBlockRules(blockRules, text, parseActiveBlockRule);
|
|
313
|
-
export const parseBlockMarkdownForSyntaxHighlighting = (text) => parseForBlockRules(blockRules, text, parseActiveBlockSyntaxRule);
|
|
323
|
+
export const parseBlockMarkdownForSyntaxHighlighting = (text) => reduceSyntaxNodes(parseForBlockRules(blockRules, text, parseActiveBlockSyntaxRule));
|
|
324
|
+
export const reduceSyntaxNodes = (nodes) => nodes.reduce((reducedNodes, node, index) => {
|
|
325
|
+
const lastNode = index > 0 ? reducedNodes[reducedNodes.length - 1] : undefined;
|
|
326
|
+
const newLastNode = lastNode ? mergeSyntaxNodes(lastNode, node) : null;
|
|
327
|
+
if (newLastNode) {
|
|
328
|
+
reducedNodes[reducedNodes.length - 1] = reduceSyntaxNode(newLastNode);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
reducedNodes.push(reduceSyntaxNode(node));
|
|
332
|
+
}
|
|
333
|
+
return reducedNodes;
|
|
334
|
+
}, []);
|
|
335
|
+
const reduceSyntaxNode = (node) => {
|
|
336
|
+
switch (node.kind) {
|
|
337
|
+
case "bold":
|
|
338
|
+
case "italic":
|
|
339
|
+
return { ...node, content: reduceSyntaxNodes(node.content) };
|
|
340
|
+
case "code":
|
|
341
|
+
case "link":
|
|
342
|
+
case "attributed":
|
|
343
|
+
case "text":
|
|
344
|
+
case "listitemmarker":
|
|
345
|
+
case "tablemarker":
|
|
346
|
+
case "headingmarker":
|
|
347
|
+
return node;
|
|
348
|
+
default:
|
|
349
|
+
return assertExhaustive(node);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const syntaxNodeMergeRules = {
|
|
353
|
+
bold: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
|
|
354
|
+
italic: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
|
|
355
|
+
code: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
356
|
+
text: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
357
|
+
listitemmarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
358
|
+
tablemarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
359
|
+
headingmarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
360
|
+
link: null,
|
|
361
|
+
attributed: null,
|
|
362
|
+
};
|
|
363
|
+
const mergeSyntaxNodes = (lastNode, node) => {
|
|
364
|
+
if (lastNode.kind !== node.kind) {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
const mergeFn = syntaxNodeMergeRules[lastNode.kind];
|
|
368
|
+
return mergeFn?.(lastNode, node) ?? null;
|
|
369
|
+
};
|
|
@@ -6,3 +6,4 @@ export type Leaves<T> = T extends object ? {
|
|
|
6
6
|
}[keyof T] : never;
|
|
7
7
|
export declare const onlyKeys: <T extends object, K extends keyof T>(obj: T, ...keys: K[]) => Pick<T, K>;
|
|
8
8
|
export declare const hasKey: <T extends object, K extends PropertyKey>(obj: T, key: K) => obj is T & { [k in K]: unknown; };
|
|
9
|
+
export declare const omitUndefinedKeys: <T extends object>(obj: T) => T;
|
|
@@ -10,3 +10,4 @@ export const mergeObjects = (obj1, obj2, solveConflict) => Object.entries(obj2).
|
|
|
10
10
|
}), obj1);
|
|
11
11
|
export const onlyKeys = (obj, ...keys) => Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key)));
|
|
12
12
|
export const hasKey = (obj, key) => Object.hasOwn(obj, key);
|
|
13
|
+
export const omitUndefinedKeys = (obj) => Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import type { GetAllGitBranchesResponseBody, GitStatusResponseBody } from "../../shared/api.ts";
|
|
1
|
+
import type { GetAllGitBranchesResponseBody, GitStatusResponseBody, IsRepoResponseBody } from "../../shared/api.ts";
|
|
2
|
+
export declare const isRepo: (locales: string[]) => Promise<IsRepoResponseBody>;
|
|
2
3
|
export declare const getStatus: (locales: string[]) => Promise<GitStatusResponseBody>;
|
|
4
|
+
export declare const fetch: (locales: string[]) => Promise<void>;
|
|
3
5
|
export declare const stageAllFiles: (locales: string[]) => Promise<void>;
|
|
4
6
|
export declare const stageAllFilesOfEntity: (locales: string[], entityName: string) => Promise<void>;
|
|
5
7
|
export declare const stageFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
|
|
6
8
|
export declare const unstageAllFiles: (locales: string[]) => Promise<void>;
|
|
7
9
|
export declare const unstageAllFilesOfEntity: (locales: string[], entityName: string) => Promise<void>;
|
|
8
10
|
export declare const unstageFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
|
|
11
|
+
export declare const resetFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
|
|
9
12
|
export declare const commitStagedFiles: (locales: string[], message: string) => Promise<void>;
|
|
10
13
|
export declare const pushCommits: (locales: string[]) => Promise<void>;
|
|
11
14
|
export declare const pullCommits: (locales: string[]) => Promise<void>;
|
|
12
15
|
export declare const getBranches: (locales: string[]) => Promise<GetAllGitBranchesResponseBody>;
|
|
13
16
|
export declare const createBranch: (locales: string[], branchName: string) => Promise<void>;
|
|
14
17
|
export declare const switchBranch: (locales: string[], branchName: string) => Promise<void>;
|
|
18
|
+
export declare const deleteBranch: (locales: string[], branchName: string) => Promise<void>;
|
package/dist/src/web/api/git.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { getResource, postResource } from "../utils/api.js";
|
|
1
|
+
import { deleteResource, getResource, postResource } from "../utils/api.js";
|
|
2
|
+
export const isRepo = async (locales) => getResource("/api/git", { locales });
|
|
2
3
|
export const getStatus = async (locales) => getResource("/api/git/status", { locales });
|
|
4
|
+
export const fetch = async (locales) => postResource("/api/git/fetch", { locales });
|
|
3
5
|
export const stageAllFiles = async (locales) => postResource("/api/git/stage", { locales });
|
|
4
6
|
export const stageAllFilesOfEntity = async (locales, entityName) => postResource(`/api/git/stage/${entityName}`, { locales });
|
|
5
7
|
export const stageFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/stage/${entityName}/${id}`, { locales });
|
|
6
8
|
export const unstageAllFiles = async (locales) => postResource(`/api/git/unstage`, { locales });
|
|
7
9
|
export const unstageAllFilesOfEntity = async (locales, entityName) => postResource(`/api/git/unstage/${entityName}`, { locales });
|
|
8
10
|
export const unstageFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/unstage/${entityName}/${id}`, { locales });
|
|
11
|
+
export const resetFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/reset/${entityName}/${id}`, { locales });
|
|
9
12
|
export const commitStagedFiles = async (locales, message) => {
|
|
10
13
|
const body = { message };
|
|
11
14
|
return postResource(`/api/git/commit`, { locales, body });
|
|
@@ -17,4 +20,5 @@ export const createBranch = async (locales, branchName) => {
|
|
|
17
20
|
const body = { branchName };
|
|
18
21
|
return postResource(`/api/git/branch`, { locales, body });
|
|
19
22
|
};
|
|
20
|
-
export const switchBranch = async (locales, branchName) => postResource(`/api/git/branch/${branchName}`, { locales });
|
|
23
|
+
export const switchBranch = async (locales, branchName) => postResource(`/api/git/branch/${encodeURIComponent(branchName)}`, { locales });
|
|
24
|
+
export const deleteBranch = async (locales, branchName) => deleteResource(`/api/git/branch/${encodeURIComponent(branchName)}`, { locales });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComponentChildren, Context } from "preact";
|
|
2
|
+
export type ContextProviderWrapper = <T>(props: {
|
|
3
|
+
children?: ComponentChildren;
|
|
4
|
+
context: Context<T>;
|
|
5
|
+
useValue: () => T;
|
|
6
|
+
}) => ComponentChildren;
|
|
7
|
+
export declare const ContextProviderWrapper: ContextProviderWrapper;
|
|
@@ -25,6 +25,7 @@ export type InstanceRouteSkeletonOnSubmitHandler = (values: {
|
|
|
25
25
|
setInstanceContent: Dispatch<SetStateAction<unknown>>;
|
|
26
26
|
setCustomId: Dispatch<SetStateAction<string>>;
|
|
27
27
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
28
|
+
updateLocalGitState?: () => Promise<void>;
|
|
28
29
|
}) => Promise<void>;
|
|
29
30
|
export type InstanceRouteSkeletonTitleBuilder = (values: {
|
|
30
31
|
locales: string[];
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useLocation, useRoute } from "preact-iso";
|
|
3
|
-
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
3
|
+
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
|
|
4
4
|
import { removeAt } from "../../shared/utils/array.js";
|
|
5
5
|
import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
|
|
6
6
|
import { toTitleCase } from "../../shared/utils/string.js";
|
|
7
7
|
import { validateLocaleIdentifier } from "../../shared/validation/identifier.js";
|
|
8
8
|
import { deleteInstanceByEntityNameAndId, getChildInstancesForInstanceByEntityName, } from "../api/declarations.js";
|
|
9
|
+
import { GitClientContext } from "../context/gitClient.js";
|
|
9
10
|
import { useEntityFromRoute } from "../hooks/useEntityFromRoute.js";
|
|
10
11
|
import { useInstanceNamesByEntity } from "../hooks/useInstanceNamesByEntity.js";
|
|
11
12
|
import { useGetDeclFromDeclName, } from "../hooks/useSecondaryDeclarations.js";
|
|
12
13
|
import { useSetting } from "../hooks/useSettings.js";
|
|
13
14
|
import { homeTitle } from "../routes/Home.js";
|
|
14
15
|
import { NotFound } from "../routes/NotFound.js";
|
|
16
|
+
import { runWithLoading } from "../signals/loading.js";
|
|
15
17
|
import { Layout } from "./Layout.js";
|
|
16
18
|
import { TypeInput } from "./typeInputs/TypeInput.js";
|
|
17
19
|
import { ValidationErrors } from "./typeInputs/utils/ValidationErrors.js";
|
|
@@ -24,6 +26,7 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
24
26
|
const [instanceContent, setInstanceContent] = useState();
|
|
25
27
|
const [childInstances, setChildInstances] = useState([]);
|
|
26
28
|
const [customId, setCustomId] = useState("");
|
|
29
|
+
const client = useContext(GitClientContext);
|
|
27
30
|
const { route } = useLocation();
|
|
28
31
|
useEffect(() => {
|
|
29
32
|
document.title =
|
|
@@ -32,7 +35,7 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
32
35
|
}, [entity, id, instanceContent, locales, titleBuilder]);
|
|
33
36
|
useEffect(() => {
|
|
34
37
|
if (entity && instanceContent === undefined && declsLoaded) {
|
|
35
|
-
init({ locales, entity, instanceId: id, setInstanceContent, getDeclFromDeclName })
|
|
38
|
+
runWithLoading(() => init({ locales, entity, instanceId: id, setInstanceContent, getDeclFromDeclName }))
|
|
36
39
|
.then(() => id
|
|
37
40
|
? getChildInstancesForInstanceByEntityName(locales, entity.name, id).then(result => {
|
|
38
41
|
setChildInstances(result.instances);
|
|
@@ -47,7 +50,7 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
47
50
|
event.preventDefault();
|
|
48
51
|
if (entity && instanceContent !== undefined) {
|
|
49
52
|
const buttonName = event.submitter?.getAttribute("name") ?? undefined;
|
|
50
|
-
onSubmit({
|
|
53
|
+
runWithLoading(() => onSubmit({
|
|
51
54
|
locales,
|
|
52
55
|
entity,
|
|
53
56
|
instanceId: id,
|
|
@@ -60,7 +63,8 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
60
63
|
setCustomId,
|
|
61
64
|
setInstanceContent,
|
|
62
65
|
childInstances,
|
|
63
|
-
|
|
66
|
+
updateLocalGitState: client?.updateLocalState,
|
|
67
|
+
})).catch((error) => {
|
|
64
68
|
console.error("Error submitting instance data:", error);
|
|
65
69
|
});
|
|
66
70
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LoadingOverlay: () => import("preact").JSX.Element;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { loading } from "../signals/loading.js";
|
|
3
|
+
export const LoadingOverlay = () => (_jsx("div", { class: "loading-overlay" + (loading.value ? " loading-overlay--open" : ""), "aria-hidden": !loading.value, children: "Loading\u2026" }));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from "preact/hooks";
|
|
3
|
-
|
|
3
|
+
import { LoadingOverlay } from "./LoadingOverlay.js";
|
|
4
|
+
export const ModalDialog = ({ children, ...props }) => {
|
|
4
5
|
const ref = useRef(null);
|
|
5
6
|
useEffect(() => {
|
|
6
7
|
if (ref.current) {
|
|
@@ -12,5 +13,5 @@ export const ModalDialog = props => {
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
}, [props.open]);
|
|
15
|
-
return
|
|
16
|
+
return (_jsxs("dialog", { ...props, open: undefined, ref: ref, children: [_jsx(LoadingOverlay, {}), children] }));
|
|
16
17
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
|
+
import { useContext, useState } from "preact/hooks";
|
|
3
|
+
import { GitContext } from "../../context/git.js";
|
|
4
|
+
import { GitClientContext } from "../../context/gitClient.js";
|
|
5
|
+
import { ModalDialog } from "../ModalDialog.js";
|
|
6
|
+
import { GitBranchManager } from "./GitBranchManager.js";
|
|
7
|
+
import { GitFileManager } from "./GitFileManager.js";
|
|
8
|
+
export const Git = () => {
|
|
9
|
+
const [isOpen, setIsOpen] = useContext(GitContext);
|
|
10
|
+
const [mode, setMode] = useState("files");
|
|
11
|
+
const client = useContext(GitClientContext);
|
|
12
|
+
if (!client || !client.isRepo) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsxs(ModalDialog, { open: isOpen, class: "git", closedBy: "any", onClose: () => {
|
|
16
|
+
setIsOpen(false);
|
|
17
|
+
}, children: [_jsxs("header", { children: [_jsx("h2", { children: (() => {
|
|
18
|
+
switch (mode) {
|
|
19
|
+
case "branches":
|
|
20
|
+
return "Branches";
|
|
21
|
+
case "files":
|
|
22
|
+
return "Files";
|
|
23
|
+
default:
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
})() }), mode === "branches" ? (_jsx("button", { onClick: () => {
|
|
27
|
+
void client.fetch();
|
|
28
|
+
}, children: "Fetch" })) : null, mode !== "files" ? (_jsx("button", { class: "git__tab git__tab--files", onClick: () => {
|
|
29
|
+
setMode("files");
|
|
30
|
+
}, children: "View Files" })) : null, mode !== "branches" ? (_jsx("button", { class: "git__tab git__tab--branches", onClick: () => {
|
|
31
|
+
setMode("branches");
|
|
32
|
+
}, children: "Manage Branches" })) : null, _jsx("button", { class: "close", onClick: () => {
|
|
33
|
+
setIsOpen(false);
|
|
34
|
+
}, children: "Close" })] }), (() => {
|
|
35
|
+
switch (mode) {
|
|
36
|
+
case "branches":
|
|
37
|
+
return _jsx(GitBranchManager, { client: client });
|
|
38
|
+
case "files":
|
|
39
|
+
return _jsx(GitFileManager, { client: client });
|
|
40
|
+
default:
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
})()] }), isOpen && mode === "files" ? null : (_jsxs("aside", { class: "git", children: [_jsx("h2", { class: "h1-faded", children: "Version Control" }), _jsx(GitFileManager, { client: client, manageBranches: () => {
|
|
44
|
+
setIsOpen(true);
|
|
45
|
+
setMode("branches");
|
|
46
|
+
} })] }))] }));
|
|
47
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
export const GitBranchManager = ({ client }) => {
|
|
3
|
+
return (_jsx("div", { class: "git-branches", children: _jsx("ul", { class: "branches", children: client.allBranches.map(branch => {
|
|
4
|
+
const branchInfo = client.branches[branch];
|
|
5
|
+
return (_jsxs("li", { class: [
|
|
6
|
+
"form-row form-row--compact form-row--separated",
|
|
7
|
+
branch === client.currentBranch ? "current" : undefined,
|
|
8
|
+
branchInfo?.remote ? "remote" : undefined,
|
|
9
|
+
]
|
|
10
|
+
.filter(className => className !== undefined)
|
|
11
|
+
.join(" "), children: [_jsxs("span", { class: "branch__full-name form-row__fill form-row__text", title: branch, children: [branchInfo?.remote ? (_jsxs("span", { class: "branch__origin", children: [branchInfo.remote, "/"] })) : null, _jsx("span", { class: "branch__name", children: branchInfo?.name ?? branch })] }), _jsxs("div", { className: "form-row__group", children: [branch === client.currentBranch ? (_jsx("span", { class: "branch__current-indicator form-row__text", children: " (Current)" })) : null, _jsx("button", { onClick: () => {
|
|
12
|
+
void client.switchBranch(branch);
|
|
13
|
+
}, disabled: branch === client.currentBranch, children: "Switch" }), _jsx("button", { class: "destructive", onClick: () => {
|
|
14
|
+
void client.deleteBranch(branch);
|
|
15
|
+
}, disabled: branch === client.currentBranch || branch.startsWith("remotes/"), children: "Delete" })] })] }, branch));
|
|
16
|
+
}) }) }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
import type { InstanceContainerOverview } from "../../../shared/utils/instances.ts";
|
|
3
|
+
export type GitEntityOverview = [
|
|
4
|
+
entityName: string,
|
|
5
|
+
entityNamePlural: string,
|
|
6
|
+
instances: InstanceContainerOverview[]
|
|
7
|
+
];
|
|
8
|
+
type Props<A extends string> = {
|
|
9
|
+
filesByEntity: GitEntityOverview[];
|
|
10
|
+
fileButtons: {
|
|
11
|
+
label: string;
|
|
12
|
+
action: A;
|
|
13
|
+
}[];
|
|
14
|
+
onFileButtonClick: (entityName: string, instance: InstanceContainerOverview, action: A) => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export declare const GitFileList: <A extends string>(props: Props<A>) => ComponentChildren;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { useLocation } from "preact-iso";
|
|
3
|
+
import { GitStatusIndicator } from "./GitStatusIndicator.js";
|
|
4
|
+
export const GitFileList = ({ filesByEntity, onFileButtonClick, fileButtons, }) => {
|
|
5
|
+
const { route } = useLocation();
|
|
6
|
+
return filesByEntity.length === 0 ? (_jsx("p", { class: "no-changes", children: "No changes" })) : (_jsx("ul", { class: "git-entity-list", children: filesByEntity.map(([entityName, entityNamePlural, instances]) => (_jsxs("li", { class: "git-entity-list-item", children: [_jsx("span", { class: "title", children: entityNamePlural }), _jsx("ul", { class: "git-instance-list", children: instances.map(instance => (_jsxs("li", { class: "form-row form-row--compact git-instance-list-item", children: [_jsx("span", { class: "title form-row__fill", children: instance.displayName }), _jsx(GitStatusIndicator, { status: instance.gitStatus }), _jsx("button", { onClick: () => {
|
|
7
|
+
route(`/entities/${entityName}/instances/${instance.id}`);
|
|
8
|
+
}, children: "View" }), fileButtons.map(({ label, action }) => (_jsx("button", { onClick: () => {
|
|
9
|
+
void onFileButtonClick(entityName, instance, action);
|
|
10
|
+
}, children: label }, label)))] }, instance.id))) })] }, entityName))) }));
|
|
11
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from "preact/hooks";
|
|
3
|
+
import { GitFileList } from "./GitFileList.js";
|
|
4
|
+
export const GitFileManager = ({ client, manageBranches }) => {
|
|
5
|
+
const [commitMessage, setCommitMessage] = useState("");
|
|
6
|
+
const commit = () => {
|
|
7
|
+
void client.commit(commitMessage);
|
|
8
|
+
};
|
|
9
|
+
const onCreateBranch = () => {
|
|
10
|
+
const newBranchName = prompt("Enter new branch name:");
|
|
11
|
+
if (newBranchName !== null) {
|
|
12
|
+
void client.createBranch(newBranchName);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const onSwitchBranch = (event) => {
|
|
16
|
+
void client.switchBranch(event.currentTarget.value);
|
|
17
|
+
};
|
|
18
|
+
const onFileButtonClick = useCallback(async (entityName, instance, action) => {
|
|
19
|
+
switch (action) {
|
|
20
|
+
case "stage":
|
|
21
|
+
return client.stage(entityName, instance);
|
|
22
|
+
case "unstage":
|
|
23
|
+
return client.unstage(entityName, instance);
|
|
24
|
+
case "reset":
|
|
25
|
+
return client.reset(entityName, instance);
|
|
26
|
+
}
|
|
27
|
+
}, [client]);
|
|
28
|
+
return (_jsxs("div", { class: "git-files", children: [_jsxs("div", { class: "form-row form-row--sides", children: [_jsxs("div", { class: "form-row__group", children: [_jsxs("button", { onClick: () => void client.push(), children: ["Push", client.commitsAhead > 0 ? ` (${client.commitsAhead.toString()})` : ""] }), _jsxs("button", { onClick: () => void client.pull(), children: ["Pull", client.commitsBehind > 0 ? ` (${client.commitsBehind.toString()})` : ""] })] }), manageBranches ? _jsx("button", { onClick: manageBranches, children: "Manage Branches" }) : null] }), _jsxs("div", { class: "form-row", children: [_jsx("div", { class: "select-wrapper form-row__fill", children: _jsx("select", { value: client.currentBranch, onInput: onSwitchBranch, children: client.allBranches.map(branch => (_jsx("option", { value: branch, children: branch }, branch))) }) }), _jsx("button", { onClick: onCreateBranch, children: "New branch" })] }), _jsxs("div", { class: "form-row", children: [_jsx("input", { class: "form-row__fill", type: "text", value: commitMessage, onInput: event => {
|
|
29
|
+
setCommitMessage(event.currentTarget.value);
|
|
30
|
+
}, placeholder: "added X to instance Y, \u2026" }), _jsx("button", { onClick: commit, disabled: commitMessage.length === 0 || client.indexFiles.length === 0, children: "Commit" })] }), _jsxs("div", { class: "git-section-title", children: [_jsx("h3", { children: "Files to be committed" }), _jsx("button", { onClick: () => void client.unstageAll(), children: "Unstage all" })] }), _jsx(GitFileList, { filesByEntity: client.indexFiles, fileButtons: [{ label: "Unstage", action: "unstage" }], onFileButtonClick: onFileButtonClick }), _jsxs("div", { class: "git-section-title", children: [_jsx("h3", { children: "Working tree changes" }), _jsx("button", { onClick: () => void client.stageAll(), children: "Stage all" })] }), _jsx(GitFileList, { filesByEntity: client.workingTreeFiles, fileButtons: [
|
|
31
|
+
{ label: "Stage", action: "stage" },
|
|
32
|
+
{ label: "Reset", action: "reset" },
|
|
33
|
+
], onFileButtonClick: onFileButtonClick })] }));
|
|
34
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { getGitStatusForDisplay, getLabelForGitStatus, } from "../../../shared/utils/git.js";
|
|
3
|
+
export const GitStatusIndicator = ({ status }) => {
|
|
4
|
+
const gitStatusForDisplay = getGitStatusForDisplay(status);
|
|
5
|
+
return (_jsx("span", { class: `git-status git-status--${gitStatusForDisplay ?? ""}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay }));
|
|
6
|
+
};
|
|
@@ -12,7 +12,7 @@ export const StringTypeInput = ({ type, value, disabled, onChange }) => {
|
|
|
12
12
|
const errors = validateStringConstraints(type, value);
|
|
13
13
|
return (_jsx("div", { class: "field field--string", children: isMarkdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { class: "editor editor--markdown", children: [_jsxs("div", { class: "textarea-grow-wrap", children: [_jsx("textarea", { value: value, minLength: minLength, maxLength: maxLength, onInput: event => {
|
|
14
14
|
onChange(event.currentTarget.value);
|
|
15
|
-
}, "aria-invalid": errors.length > 0, disabled: disabled }), _jsxs("p", { class: "help", children: ["This textarea supports", " ", _jsx("a", { href: "https://www.markdownguide.org/getting-started/", target: "_blank", rel: "noreferrer", children: "Markdown" }), "."] }), _jsx(MarkdownHighlighting, { class: "textarea-grow-wrap__mirror editor-highlighting", string: value })] }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }), _jsx("div", { class: "preview", children: _jsx(Markdown, { string: value, outerHeadingLevel: 2 }) })] })) : (_jsxs("div", { class: "editor", children: [_jsx("input", { type: "text", value: value, minLength: minLength, maxLength: maxLength, pattern: pattern === undefined
|
|
15
|
+
}, "aria-invalid": errors.length > 0, disabled: disabled }), _jsxs("p", { class: "help", children: ["This textarea supports", " ", _jsx("a", { href: "https://www.markdownguide.org/getting-started/", target: "_blank", rel: "noreferrer", children: "Markdown" }), "."] }), _jsx(MarkdownHighlighting, { class: "textarea-grow-wrap__mirror editor-highlighting", string: value + " " })] }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }), _jsx("div", { class: "preview", children: _jsx(Markdown, { string: value, outerHeadingLevel: 2 }) })] })) : (_jsxs("div", { class: "editor", children: [_jsx("input", { type: "text", value: value, minLength: minLength, maxLength: maxLength, pattern: pattern === undefined
|
|
16
16
|
? undefined
|
|
17
17
|
: pattern.startsWith("^(?:") && pattern.endsWith(")$")
|
|
18
18
|
? pattern.slice(4, -2)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { SerializedEntityDecl } from "../../shared/schema/declarations/EntityDecl.ts";
|
|
2
|
+
export type EntitySummary = {
|
|
3
|
+
declaration: SerializedEntityDecl;
|
|
4
|
+
instanceCount: number;
|
|
5
|
+
isLocaleEntity: boolean;
|
|
6
|
+
};
|
|
2
7
|
export declare const EntitiesContext: import("preact").Context<{
|
|
3
|
-
entities:
|
|
4
|
-
declaration: SerializedEntityDecl;
|
|
5
|
-
instanceCount: number;
|
|
6
|
-
isLocaleEntity: boolean;
|
|
7
|
-
}[];
|
|
8
|
+
entities: EntitySummary[];
|
|
8
9
|
reloadEntities: () => Promise<void>;
|
|
9
10
|
}>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { SetStateAction } from "preact/compat";
|
|
2
2
|
import type { Dispatch } from "preact/hooks";
|
|
3
|
-
export
|
|
3
|
+
export type GitContext = [isOpen: boolean, setIsOpen: Dispatch<SetStateAction<boolean>>];
|
|
4
|
+
export declare const GitContext: import("preact").Context<GitContext>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type GitFileStatus } from "../../shared/utils/git.ts";
|
|
2
|
+
import type { InstanceContainerOverview } from "../../shared/utils/instances.ts";
|
|
3
|
+
import type { GitEntityOverview } from "../components/git/GitFileList.tsx";
|
|
4
|
+
export type GitBranchSummary = {
|
|
5
|
+
current: boolean;
|
|
6
|
+
name: string;
|
|
7
|
+
commit: string;
|
|
8
|
+
label: string;
|
|
9
|
+
linkedWorkTree: boolean;
|
|
10
|
+
remote?: string;
|
|
11
|
+
};
|
|
12
|
+
export type GitClient = {
|
|
13
|
+
isRepo: boolean;
|
|
14
|
+
commitsAhead: number;
|
|
15
|
+
commitsBehind: number;
|
|
16
|
+
indexFiles: GitEntityOverview[];
|
|
17
|
+
workingTreeFiles: GitEntityOverview[];
|
|
18
|
+
allBranches: string[];
|
|
19
|
+
currentBranch: string;
|
|
20
|
+
branches: Record<string, GitBranchSummary>;
|
|
21
|
+
isDetached: boolean;
|
|
22
|
+
updateLocalState: () => Promise<void>;
|
|
23
|
+
getGitStatusOfInstance: (entityName: string, instanceId: string) => GitFileStatus | undefined;
|
|
24
|
+
fetch: () => Promise<void>;
|
|
25
|
+
stage: (entityName: string, instance: InstanceContainerOverview) => Promise<void>;
|
|
26
|
+
stageAll: (entityName?: string) => Promise<void>;
|
|
27
|
+
unstage: (entityName: string, instance: InstanceContainerOverview) => Promise<void>;
|
|
28
|
+
unstageAll: (entityName?: string) => Promise<void>;
|
|
29
|
+
reset: (entityName: string, instance: InstanceContainerOverview) => Promise<void>;
|
|
30
|
+
commit: (commitMessage: string) => Promise<void>;
|
|
31
|
+
push: () => Promise<void>;
|
|
32
|
+
pull: () => Promise<void>;
|
|
33
|
+
createBranch: (newBranchName: string) => Promise<void>;
|
|
34
|
+
switchBranch: (targetBranch: string) => Promise<void>;
|
|
35
|
+
deleteBranch: (targetBranch: string) => Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
export declare const useGitClient: () => GitClient;
|