@mpeggroup/mpeg-sdl-editor 1.4.2 → 1.4.4
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 -5
- package/bun.lock +76 -34
- package/html/index.html +1 -1
- package/package.json +30 -30
- package/src/App.tsx +29 -56
- package/src/codemirror/ruler.ts +3 -9
- package/src/components/DesktopPanels.tsx +2 -6
- package/src/components/Editor.tsx +94 -109
- package/src/components/MobileDrawer.tsx +8 -12
- package/src/components/Navbar.tsx +35 -42
- package/src/components/ResizableLayout.tsx +20 -24
- package/src/components/Settings.tsx +38 -49
- package/src/components/StatusBar.tsx +2 -6
- package/src/components/SubNav.tsx +1 -6
- package/src/hooks/useAutoDisplayCompletions.ts +2 -7
- package/src/hooks/useDragResize.ts +63 -57
- package/src/hooks/useFileOperations.ts +1 -3
- package/src/hooks/useMobileDetection.ts +2 -6
- package/src/hooks/usePrettier.ts +1 -3
- package/src/hooks/useRulerWidth.ts +1 -10
- package/src/hooks/useShowSemanticWarnings.ts +1 -3
- package/src/hooks/useShowSymanticErrors.ts +7 -9
- package/src/hooks/useShowSyntaxErrors.ts +7 -9
- package/src/hooks/useToast.ts +1 -5
- package/src/sdl/sdlComplete.ts +5 -17
- package/src/sdl/sdlLinter.ts +1 -6
- package/tailwind.config.ts +2 -6
- package/tests/Editor_test.tsx +1 -3
- package/tsconfig.json +6 -2
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export function useAutoDisplayCompletions(defaultValue: boolean = true) {
|
|
4
|
-
const [autoDisplayCompletions, setAutoDisplayCompletionsInternal] = useState<
|
|
5
|
-
boolean
|
|
6
|
-
>(() => {
|
|
4
|
+
const [autoDisplayCompletions, setAutoDisplayCompletionsInternal] = useState<boolean>(() => {
|
|
7
5
|
const stored = localStorage.getItem("autoDisplayCompletions");
|
|
8
6
|
if (stored !== null) {
|
|
9
7
|
return stored === "true";
|
|
@@ -12,10 +10,7 @@ export function useAutoDisplayCompletions(defaultValue: boolean = true) {
|
|
|
12
10
|
});
|
|
13
11
|
|
|
14
12
|
useEffect(() => {
|
|
15
|
-
localStorage.setItem(
|
|
16
|
-
"autoDisplayCompletions",
|
|
17
|
-
String(autoDisplayCompletions),
|
|
18
|
-
);
|
|
13
|
+
localStorage.setItem("autoDisplayCompletions", String(autoDisplayCompletions));
|
|
19
14
|
}, [autoDisplayCompletions]);
|
|
20
15
|
|
|
21
16
|
const setAutoDisplayCompletions = useCallback((value: boolean) => {
|
|
@@ -19,63 +19,69 @@ export function useDragResize({
|
|
|
19
19
|
const [dragStartX, setDragStartX] = useState(0);
|
|
20
20
|
const [dragStartPercentage, setDragStartPercentage] = useState(0);
|
|
21
21
|
|
|
22
|
-
const handleMouseDown = useCallback(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
22
|
+
const handleMouseDown = useCallback(
|
|
23
|
+
(e: React.MouseEvent) => {
|
|
24
|
+
if (!isSettingsShown) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
|
|
30
|
+
const container = containerRef.current;
|
|
31
|
+
|
|
32
|
+
if (!container) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const containerRect = container.getBoundingClientRect();
|
|
37
|
+
const mouseX = e.clientX - containerRect.left;
|
|
38
|
+
const currentPercentage = (mouseX / containerRect.width) * 100;
|
|
39
|
+
|
|
40
|
+
setDragStartX(e.clientX);
|
|
41
|
+
setDragStartPercentage(currentPercentage);
|
|
42
|
+
setIsDragging(true);
|
|
43
|
+
},
|
|
44
|
+
[isSettingsShown, containerRef],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const handleMouseMove = useCallback(
|
|
48
|
+
(e: MouseEvent) => {
|
|
49
|
+
if (!isDragging || !containerRef.current || !isSettingsShown) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const container = containerRef.current;
|
|
54
|
+
const containerRect = container.getBoundingClientRect();
|
|
55
|
+
const containerWidth = containerRect.width;
|
|
56
|
+
|
|
57
|
+
// Calculate the delta from where the drag started
|
|
58
|
+
const deltaX = e.clientX - dragStartX;
|
|
59
|
+
const deltaPercentage = (deltaX / containerWidth) * 100;
|
|
60
|
+
|
|
61
|
+
// Calculate new percentage based on drag start position + delta
|
|
62
|
+
let newPercentage = dragStartPercentage + deltaPercentage;
|
|
63
|
+
|
|
64
|
+
// Apply minimum width constraints
|
|
65
|
+
const minLeftPercentage = (minLeftWidth / containerWidth) * 100;
|
|
66
|
+
const minRightPercentage = (minRightWidth / containerWidth) * 100;
|
|
67
|
+
|
|
68
|
+
// Clamp the percentage to valid bounds
|
|
69
|
+
newPercentage = Math.max(minLeftPercentage, newPercentage);
|
|
70
|
+
newPercentage = Math.min(100 - minRightPercentage, newPercentage);
|
|
71
|
+
|
|
72
|
+
onSplitPercentageChange(newPercentage);
|
|
73
|
+
},
|
|
74
|
+
[
|
|
75
|
+
isDragging,
|
|
76
|
+
dragStartX,
|
|
77
|
+
dragStartPercentage,
|
|
78
|
+
minLeftWidth,
|
|
79
|
+
minRightWidth,
|
|
80
|
+
isSettingsShown,
|
|
81
|
+
onSplitPercentageChange,
|
|
82
|
+
containerRef,
|
|
83
|
+
],
|
|
84
|
+
);
|
|
79
85
|
|
|
80
86
|
const handleMouseUp = useCallback(() => {
|
|
81
87
|
setIsDragging(false);
|
|
@@ -9,9 +9,7 @@ interface UseFileOperationsProps {
|
|
|
9
9
|
editorRef: React.RefObject<EditorRef | null>;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function useFileOperations(
|
|
13
|
-
{ code, setCode, showToast, editorRef }: UseFileOperationsProps,
|
|
14
|
-
) {
|
|
12
|
+
export function useFileOperations({ code, setCode, showToast, editorRef }: UseFileOperationsProps) {
|
|
15
13
|
const handleSave = useCallback(async () => {
|
|
16
14
|
try {
|
|
17
15
|
if (globalThis.window.showSaveFilePicker) {
|
|
@@ -3,9 +3,7 @@ import { useEffect, useState } from "react";
|
|
|
3
3
|
export function useMobileDetection(breakpoint: number = 768) {
|
|
4
4
|
const [isMobile, setIsMobile] = useState(() => {
|
|
5
5
|
// Only check on client-side, return false for SSR
|
|
6
|
-
if (
|
|
7
|
-
typeof globalThis !== "undefined" && globalThis.innerWidth !== undefined
|
|
8
|
-
) {
|
|
6
|
+
if (typeof globalThis !== "undefined" && globalThis.innerWidth !== undefined) {
|
|
9
7
|
return globalThis.innerWidth < breakpoint;
|
|
10
8
|
}
|
|
11
9
|
return false;
|
|
@@ -13,9 +11,7 @@ export function useMobileDetection(breakpoint: number = 768) {
|
|
|
13
11
|
|
|
14
12
|
useEffect(() => {
|
|
15
13
|
// Only run on client-side
|
|
16
|
-
if (
|
|
17
|
-
typeof globalThis === "undefined" || globalThis.innerWidth === undefined
|
|
18
|
-
) {
|
|
14
|
+
if (typeof globalThis === "undefined" || globalThis.innerWidth === undefined) {
|
|
19
15
|
return;
|
|
20
16
|
}
|
|
21
17
|
|
package/src/hooks/usePrettier.ts
CHANGED
|
@@ -10,9 +10,7 @@ interface UsePrettierProps {
|
|
|
10
10
|
rulerWidth: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function usePrettier(
|
|
14
|
-
{ code, setCode, showToast, rulerWidth }: UsePrettierProps,
|
|
15
|
-
) {
|
|
13
|
+
export function usePrettier({ code, setCode, showToast, rulerWidth }: UsePrettierProps) {
|
|
16
14
|
const prettifyInProgressRef = useRef(false);
|
|
17
15
|
|
|
18
16
|
const handlePrettify = useCallback(async () => {
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export const RULER_WIDTH_OPTIONS = [
|
|
4
|
-
60,
|
|
5
|
-
80,
|
|
6
|
-
100,
|
|
7
|
-
120,
|
|
8
|
-
140,
|
|
9
|
-
160,
|
|
10
|
-
180,
|
|
11
|
-
200,
|
|
12
|
-
] as const;
|
|
3
|
+
export const RULER_WIDTH_OPTIONS = [60, 80, 100, 120, 140, 160, 180, 200] as const;
|
|
13
4
|
export type RulerWidth = (typeof RULER_WIDTH_OPTIONS)[number];
|
|
14
5
|
|
|
15
6
|
function isValidRulerWidth(value: number): value is RulerWidth {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export function useShowSemanticWarnings(defaultValue: boolean = true) {
|
|
4
|
-
const [showSemanticWarnings, setShowSemanticWarningsInternal] = useState<
|
|
5
|
-
boolean
|
|
6
|
-
>(() => {
|
|
4
|
+
const [showSemanticWarnings, setShowSemanticWarningsInternal] = useState<boolean>(() => {
|
|
7
5
|
const stored = localStorage.getItem("showSemanticWarnings");
|
|
8
6
|
if (stored !== null) {
|
|
9
7
|
return stored === "true";
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export function useShowSemanticErrors(defaultValue: boolean = true) {
|
|
4
|
-
const [showSemanticErrors, setShowSemanticErrorsInternal] = useState<boolean>(
|
|
5
|
-
()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
);
|
|
4
|
+
const [showSemanticErrors, setShowSemanticErrorsInternal] = useState<boolean>(() => {
|
|
5
|
+
const stored = localStorage.getItem("showSemanticErrors");
|
|
6
|
+
if (stored !== null) {
|
|
7
|
+
return stored === "true";
|
|
8
|
+
}
|
|
9
|
+
return defaultValue;
|
|
10
|
+
});
|
|
13
11
|
|
|
14
12
|
useEffect(() => {
|
|
15
13
|
localStorage.setItem("showSemanticErrors", String(showSemanticErrors));
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export function useShowSyntaxErrors(defaultValue: boolean = true) {
|
|
4
|
-
const [showSyntaxErrors, setShowSyntaxErrorsInternal] = useState<boolean>(
|
|
5
|
-
()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
);
|
|
4
|
+
const [showSyntaxErrors, setShowSyntaxErrorsInternal] = useState<boolean>(() => {
|
|
5
|
+
const stored = localStorage.getItem("showSyntaxErrors");
|
|
6
|
+
if (stored !== null) {
|
|
7
|
+
return stored === "true";
|
|
8
|
+
}
|
|
9
|
+
return defaultValue;
|
|
10
|
+
});
|
|
13
11
|
|
|
14
12
|
useEffect(() => {
|
|
15
13
|
localStorage.setItem("showSyntaxErrors", String(showSyntaxErrors));
|
package/src/hooks/useToast.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
export type ToastType = "success" | "warning" | "error";
|
|
4
|
-
export type ShowToastFunction = (
|
|
5
|
-
message: string,
|
|
6
|
-
type?: ToastType,
|
|
7
|
-
duration?: number,
|
|
8
|
-
) => void;
|
|
4
|
+
export type ShowToastFunction = (message: string, type?: ToastType, duration?: number) => void;
|
|
9
5
|
|
|
10
6
|
interface ToastState {
|
|
11
7
|
message: string;
|
package/src/sdl/sdlComplete.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CompletionContext,
|
|
3
|
-
type CompletionResult,
|
|
4
|
-
} from "@codemirror/autocomplete";
|
|
1
|
+
import { CompletionContext, type CompletionResult } from "@codemirror/autocomplete";
|
|
5
2
|
import type { SyntaxNode } from "@lezer/common";
|
|
6
3
|
import { syntaxTree } from "@codemirror/language";
|
|
7
4
|
import { foldNodeProp } from "@codemirror/language";
|
|
8
|
-
import {
|
|
9
|
-
getPotentialSyntacticTokens,
|
|
10
|
-
TokenTypeId,
|
|
11
|
-
} from "@mpeggroup/mpeg-sdl-parser";
|
|
5
|
+
import { getPotentialSyntacticTokens, TokenTypeId } from "@mpeggroup/mpeg-sdl-parser";
|
|
12
6
|
|
|
13
7
|
function isCommentCompletion(lastNode: SyntaxNode): boolean {
|
|
14
8
|
return lastNode.type.id === TokenTypeId.Comment;
|
|
@@ -37,10 +31,7 @@ function isBlockScopeCompletion(lastNode: SyntaxNode): boolean {
|
|
|
37
31
|
return parentNode.type.prop(foldNodeProp) !== undefined;
|
|
38
32
|
}
|
|
39
33
|
|
|
40
|
-
function getCompletionResult(
|
|
41
|
-
completionOptions: string[],
|
|
42
|
-
from: number,
|
|
43
|
-
): CompletionResult {
|
|
34
|
+
function getCompletionResult(completionOptions: string[], from: number): CompletionResult {
|
|
44
35
|
return {
|
|
45
36
|
from,
|
|
46
37
|
options: completionOptions.map((label) => ({ label })),
|
|
@@ -73,11 +64,8 @@ function sdlComplete(context: CompletionContext): CompletionResult | null {
|
|
|
73
64
|
|
|
74
65
|
const completions = getPotentialSyntacticTokens(lastNode.cursor());
|
|
75
66
|
|
|
76
|
-
if (completions &&
|
|
77
|
-
return getCompletionResult(
|
|
78
|
-
completions,
|
|
79
|
-
lastTag ? lastNode.from + lastTag.index : context.pos,
|
|
80
|
-
);
|
|
67
|
+
if (completions && completions.length > 0) {
|
|
68
|
+
return getCompletionResult(completions, lastTag ? lastNode.from + lastTag.index : context.pos);
|
|
81
69
|
}
|
|
82
70
|
|
|
83
71
|
return null;
|
package/src/sdl/sdlLinter.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { syntaxTree } from "@codemirror/language";
|
|
2
|
-
import {
|
|
3
|
-
type Diagnostic,
|
|
4
|
-
forEachDiagnostic,
|
|
5
|
-
linter,
|
|
6
|
-
setDiagnostics,
|
|
7
|
-
} from "@codemirror/lint";
|
|
2
|
+
import { type Diagnostic, forEachDiagnostic, linter, setDiagnostics } from "@codemirror/lint";
|
|
8
3
|
import { type Extension } from "@codemirror/state";
|
|
9
4
|
import { EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view";
|
|
10
5
|
import {
|
package/tailwind.config.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const tailwindConfig: import("tailwindcss").Config & { daisyui?: any } = {
|
|
2
|
-
content: [
|
|
3
|
-
"./src/**/*.{html,js,jsx,ts,tsx}",
|
|
4
|
-
],
|
|
2
|
+
content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
|
|
5
3
|
theme: {
|
|
6
4
|
extend: {},
|
|
7
5
|
},
|
|
8
|
-
plugins: [
|
|
9
|
-
require("daisyui"),
|
|
10
|
-
],
|
|
6
|
+
plugins: [require("daisyui")],
|
|
11
7
|
daisyui: {
|
|
12
8
|
themes: ["light", "dark"],
|
|
13
9
|
},
|
package/tests/Editor_test.tsx
CHANGED
|
@@ -21,9 +21,7 @@ describe("Editor component", () => {
|
|
|
21
21
|
const textboxes = getAllByRole("textbox");
|
|
22
22
|
|
|
23
23
|
// Check that at least one textbox contains the expected content
|
|
24
|
-
const found = textboxes.some(
|
|
25
|
-
(el) => el.textContent && el.textContent.includes("int a;"),
|
|
26
|
-
);
|
|
24
|
+
const found = textboxes.some((el) => el.textContent && el.textContent.includes("int a;"));
|
|
27
25
|
expect(found).toBe(true);
|
|
28
26
|
});
|
|
29
27
|
});
|
package/tsconfig.json
CHANGED
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
// Some stricter flags (disabled by default)
|
|
23
23
|
"noUnusedLocals": false,
|
|
24
24
|
"noUnusedParameters": false,
|
|
25
|
-
"noPropertyAccessFromIndexSignature": false
|
|
26
|
-
|
|
25
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
26
|
+
|
|
27
|
+
// Type definitions
|
|
28
|
+
"types": ["bun", "node"]
|
|
29
|
+
},
|
|
30
|
+
"exclude": ["dist"]
|
|
27
31
|
}
|