@mpeggroup/mpeg-sdl-editor 1.3.1
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/.github/workflows/check-bun-dependencies.yml +13 -0
- package/.github/workflows/lint-pr-message.yml +13 -0
- package/.github/workflows/release-bun-webapp.yml +15 -0
- package/.github/workflows/validate-bun-webapp-pr.yml +9 -0
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/build.ts +8 -0
- package/functional_tests/README.md +25 -0
- package/functional_tests/features/environment.py +50 -0
- package/functional_tests/features/steps/helper/selenium.py +15 -0
- package/functional_tests/features/steps/webapp.py +30 -0
- package/functional_tests/features/webapp.feature +5 -0
- package/functional_tests/pip-requirements.txt +2 -0
- package/html/favicon.svg +126 -0
- package/html/index.html +14 -0
- package/html/style.css +2 -0
- package/package.json +61 -0
- package/src/App.css +3 -0
- package/src/App.tsx +266 -0
- package/src/assets/mpeg.svg +143 -0
- package/src/codemirror/ruler.ts +58 -0
- package/src/components/DesktopPanels.tsx +65 -0
- package/src/components/Editor.tsx +384 -0
- package/src/components/MobileDrawer.tsx +57 -0
- package/src/components/Navbar.tsx +226 -0
- package/src/components/ResizableLayout.tsx +69 -0
- package/src/components/Settings.tsx +170 -0
- package/src/components/StatusBar.tsx +47 -0
- package/src/components/SubNav.tsx +58 -0
- package/src/hooks/useAutoDisplayCompletions.ts +26 -0
- package/src/hooks/useDragResize.ts +104 -0
- package/src/hooks/useEnableLinting.ts +21 -0
- package/src/hooks/useFileOperations.ts +102 -0
- package/src/hooks/useMobileDetection.ts +32 -0
- package/src/hooks/usePrettier.ts +40 -0
- package/src/hooks/useRulerWidth.ts +42 -0
- package/src/hooks/useShowSemanticWarnings.ts +23 -0
- package/src/hooks/useShowSymanticErrors.ts +23 -0
- package/src/hooks/useShowSyntaxErrors.ts +23 -0
- package/src/hooks/useTheme.ts +21 -0
- package/src/hooks/useToast.ts +37 -0
- package/src/logo/getLogoSvg.ts +5 -0
- package/src/logo/svg.module.d.ts +5 -0
- package/src/main.tsx +13 -0
- package/src/sdl/sdlComplete.ts +86 -0
- package/src/sdl/sdlLanguage.ts +18 -0
- package/src/sdl/sdlLinter.ts +157 -0
- package/tailwind.config.ts +16 -0
- package/tests/Editor_test.tsx +29 -0
- package/tests/happydom.ts +3 -0
- package/tests/sdlLanguage_test.ts +39 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
} from "react";
|
|
8
|
+
import CodeMirror, { ViewUpdate } from "@uiw/react-codemirror";
|
|
9
|
+
import {
|
|
10
|
+
codeFolding,
|
|
11
|
+
ensureSyntaxTree,
|
|
12
|
+
foldAll,
|
|
13
|
+
foldGutter,
|
|
14
|
+
LanguageSupport,
|
|
15
|
+
type TagStyle,
|
|
16
|
+
unfoldAll,
|
|
17
|
+
} from "@codemirror/language";
|
|
18
|
+
import { classHighlighter, highlightCode } from "@lezer/highlight";
|
|
19
|
+
import {
|
|
20
|
+
vscodeDarkInit,
|
|
21
|
+
vscodeDarkStyle,
|
|
22
|
+
vscodeLightInit,
|
|
23
|
+
vscodeLightStyle,
|
|
24
|
+
} from "@uiw/codemirror-theme-vscode";
|
|
25
|
+
import { EditorView } from "@codemirror/view";
|
|
26
|
+
import {
|
|
27
|
+
SemanticError,
|
|
28
|
+
SemanticWarning,
|
|
29
|
+
SyntaxError,
|
|
30
|
+
} from "@mpeggroup/mpeg-sdl-parser";
|
|
31
|
+
export { ViewUpdate } from "@codemirror/view";
|
|
32
|
+
import { lintGutter } from "@codemirror/lint";
|
|
33
|
+
import { autocompletion } from "@codemirror/autocomplete";
|
|
34
|
+
import { sdl } from "../sdl/sdlLanguage.ts";
|
|
35
|
+
import { sdlLinter } from "../sdl/sdlLinter.ts";
|
|
36
|
+
import { ruler } from "../codemirror/ruler.ts";
|
|
37
|
+
|
|
38
|
+
type TagWithNameAndModified = {
|
|
39
|
+
name: string | undefined;
|
|
40
|
+
modified: Array<unknown>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const darkTheme = vscodeDarkInit({ settings: { fontSize: "11px" } });
|
|
44
|
+
const lightTheme = vscodeLightInit({ settings: { fontSize: "11px" } });
|
|
45
|
+
|
|
46
|
+
interface EditorProps {
|
|
47
|
+
code: string;
|
|
48
|
+
onCodeChange: (code: string) => void;
|
|
49
|
+
onCursorChange: (position: { line: number; col: number }) => void;
|
|
50
|
+
onSyntaxErrorChange: (syntaxErrors: SyntaxError[]) => void;
|
|
51
|
+
onSemanticErrorChange: (syntaxErrors: SemanticError[]) => void;
|
|
52
|
+
onSemanticWarningChange: (syntaxErrors: SemanticWarning[]) => void;
|
|
53
|
+
theme: "light" | "dark";
|
|
54
|
+
rulerWidth: number;
|
|
55
|
+
autoDisplayCompletions: boolean;
|
|
56
|
+
enableLinting: boolean;
|
|
57
|
+
showSyntaxErrors: boolean;
|
|
58
|
+
showSemanticErrors: boolean;
|
|
59
|
+
showSemanticWarnings: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface EditorRef {
|
|
63
|
+
expandAll: () => void;
|
|
64
|
+
collapseAll: () => void;
|
|
65
|
+
getStyledCode: () => string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function extractThemeStyleAttributes(themeStyle: TagStyle[]) {
|
|
69
|
+
const styleAttributesByTagName = new Map<string, string>();
|
|
70
|
+
|
|
71
|
+
themeStyle.forEach((style: TagStyle) => {
|
|
72
|
+
let attributes = "";
|
|
73
|
+
|
|
74
|
+
Object.keys(style).forEach((key) => {
|
|
75
|
+
if ((key !== "tag") && (key !== "class")) {
|
|
76
|
+
const value = style[key as keyof typeof style];
|
|
77
|
+
|
|
78
|
+
if (typeof value === "string") {
|
|
79
|
+
attributes += `${key}: ${value};`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (attributes.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (style.tag instanceof Array) {
|
|
89
|
+
style.tag.forEach((tag) => {
|
|
90
|
+
const actualTag = tag as unknown as TagWithNameAndModified;
|
|
91
|
+
|
|
92
|
+
if (actualTag.name && (actualTag.modified.length === 0)) {
|
|
93
|
+
styleAttributesByTagName.set(actualTag.name, attributes);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
const actualTag = style.tag as unknown as TagWithNameAndModified;
|
|
98
|
+
|
|
99
|
+
if (actualTag.name && (actualTag.modified.length === 0)) {
|
|
100
|
+
styleAttributesByTagName.set(actualTag.name, attributes);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return styleAttributesByTagName;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const lightStyleAttributesByTagName = extractThemeStyleAttributes(
|
|
109
|
+
vscodeLightStyle,
|
|
110
|
+
);
|
|
111
|
+
const darkStyleAttributesByTagName = extractThemeStyleAttributes(
|
|
112
|
+
vscodeDarkStyle,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
function getStyledCode(
|
|
116
|
+
sdlLanguageSupport: LanguageSupport,
|
|
117
|
+
code: string,
|
|
118
|
+
theme: string,
|
|
119
|
+
): string {
|
|
120
|
+
const styleAttributesByTagName = theme === "dark"
|
|
121
|
+
? darkStyleAttributesByTagName
|
|
122
|
+
: lightStyleAttributesByTagName;
|
|
123
|
+
|
|
124
|
+
let richText = "";
|
|
125
|
+
|
|
126
|
+
function emitSpan(text: string, classes: string | undefined) {
|
|
127
|
+
let spanStyleAttributes;
|
|
128
|
+
|
|
129
|
+
if (classes) {
|
|
130
|
+
let tagName = classes;
|
|
131
|
+
|
|
132
|
+
if (classes?.startsWith("tok-")) {
|
|
133
|
+
// If classes starts with "tok-" it is a class added by classHighlighter
|
|
134
|
+
tagName = classes.substring(4);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const styleAttributes = styleAttributesByTagName.get(tagName);
|
|
138
|
+
|
|
139
|
+
if (styleAttributes) {
|
|
140
|
+
spanStyleAttributes = styleAttributes;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const textNode = document.createTextNode(text);
|
|
145
|
+
const p = document.createElement("p");
|
|
146
|
+
|
|
147
|
+
p.appendChild(textNode);
|
|
148
|
+
|
|
149
|
+
let span;
|
|
150
|
+
if (spanStyleAttributes) {
|
|
151
|
+
span = "<span style='" + spanStyleAttributes + "'>" +
|
|
152
|
+
p.innerHTML.replaceAll(" ", " ") +
|
|
153
|
+
"</span>";
|
|
154
|
+
} else {
|
|
155
|
+
span = "<span>" + p.innerHTML.replaceAll(" ", " ") + "</span>";
|
|
156
|
+
}
|
|
157
|
+
richText += span;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function emitBreak() {
|
|
161
|
+
richText += "<br>";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
highlightCode(
|
|
165
|
+
code,
|
|
166
|
+
sdlLanguageSupport.language.parser.parse(code),
|
|
167
|
+
classHighlighter,
|
|
168
|
+
emitSpan,
|
|
169
|
+
emitBreak,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return `<span style="font-family: monospace">${richText}</span>`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const Editor = forwardRef<EditorRef, EditorProps>(
|
|
176
|
+
(
|
|
177
|
+
{
|
|
178
|
+
code,
|
|
179
|
+
onCodeChange,
|
|
180
|
+
onCursorChange,
|
|
181
|
+
onSyntaxErrorChange,
|
|
182
|
+
onSemanticErrorChange,
|
|
183
|
+
onSemanticWarningChange,
|
|
184
|
+
theme,
|
|
185
|
+
rulerWidth,
|
|
186
|
+
autoDisplayCompletions,
|
|
187
|
+
enableLinting,
|
|
188
|
+
showSyntaxErrors,
|
|
189
|
+
showSemanticErrors,
|
|
190
|
+
showSemanticWarnings,
|
|
191
|
+
},
|
|
192
|
+
ref,
|
|
193
|
+
) => {
|
|
194
|
+
const lastCursorPosition = useRef({ line: 1, col: 1 });
|
|
195
|
+
const editorViewRef = useRef<EditorView | null>(null);
|
|
196
|
+
const onSyntaxErrorChangeRef = useRef(onSyntaxErrorChange);
|
|
197
|
+
const onSemanticErrorChangeRef = useRef(onSemanticErrorChange);
|
|
198
|
+
const onSemanticWarningChangeRef = useRef(onSemanticWarningChange);
|
|
199
|
+
|
|
200
|
+
onSyntaxErrorChangeRef.current = onSyntaxErrorChange;
|
|
201
|
+
onSemanticErrorChangeRef.current = onSemanticErrorChange;
|
|
202
|
+
onSemanticWarningChangeRef.current = onSemanticWarningChange;
|
|
203
|
+
|
|
204
|
+
const sdlLanguageSupport = useMemo(() => sdl(), []);
|
|
205
|
+
|
|
206
|
+
// Memoize static extensions that never change
|
|
207
|
+
const staticExtensions = useMemo(() => [
|
|
208
|
+
codeFolding(),
|
|
209
|
+
foldGutter(),
|
|
210
|
+
], []);
|
|
211
|
+
|
|
212
|
+
const rulerExtension = useMemo(() => ruler(rulerWidth), [rulerWidth]);
|
|
213
|
+
|
|
214
|
+
const autocompletionExtension = useMemo(
|
|
215
|
+
() => autocompletion({ activateOnTyping: autoDisplayCompletions }),
|
|
216
|
+
[autoDisplayCompletions],
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const stableOnSyntaxErrorChange = useCallback(
|
|
220
|
+
(errors: SyntaxError[]) => {
|
|
221
|
+
onSyntaxErrorChangeRef.current(errors);
|
|
222
|
+
},
|
|
223
|
+
[],
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const stableOnSemanticErrorChange = useCallback(
|
|
227
|
+
(errors: SemanticError[]) => {
|
|
228
|
+
onSemanticErrorChangeRef.current(errors);
|
|
229
|
+
},
|
|
230
|
+
[],
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const stableOnSemanticWarningChange = useCallback(
|
|
234
|
+
(errors: SemanticWarning[]) => {
|
|
235
|
+
onSemanticWarningChangeRef.current(errors);
|
|
236
|
+
},
|
|
237
|
+
[],
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Memoize dynamic extensions that depend on props
|
|
241
|
+
const dynamicExtensions = useMemo(
|
|
242
|
+
() =>
|
|
243
|
+
enableLinting
|
|
244
|
+
? [
|
|
245
|
+
// TODO: only include the lint gutter if showSyntaxErrors, showSemanticErrors, or showSemanticWarnings is true
|
|
246
|
+
lintGutter(),
|
|
247
|
+
// TODO: pass showSyntaxErrors, showSemanticErrors, showSemanticWarnings to sdlLinter
|
|
248
|
+
sdlLinter({
|
|
249
|
+
onSyntaxErrorChange: stableOnSyntaxErrorChange,
|
|
250
|
+
onSemanticErrorChange: stableOnSemanticErrorChange,
|
|
251
|
+
onSemanticWarningChange: stableOnSemanticWarningChange,
|
|
252
|
+
showSyntaxErrors,
|
|
253
|
+
showSemanticErrors,
|
|
254
|
+
showSemanticWarnings,
|
|
255
|
+
}),
|
|
256
|
+
]
|
|
257
|
+
: [],
|
|
258
|
+
[
|
|
259
|
+
enableLinting,
|
|
260
|
+
stableOnSyntaxErrorChange,
|
|
261
|
+
stableOnSemanticErrorChange,
|
|
262
|
+
stableOnSemanticWarningChange,
|
|
263
|
+
showSyntaxErrors,
|
|
264
|
+
showSemanticErrors,
|
|
265
|
+
showSemanticWarnings,
|
|
266
|
+
],
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const extensions = useMemo(() => [
|
|
270
|
+
...staticExtensions,
|
|
271
|
+
rulerExtension,
|
|
272
|
+
autocompletionExtension,
|
|
273
|
+
sdlLanguageSupport,
|
|
274
|
+
...dynamicExtensions,
|
|
275
|
+
], [
|
|
276
|
+
staticExtensions,
|
|
277
|
+
rulerExtension,
|
|
278
|
+
autocompletionExtension,
|
|
279
|
+
sdlLanguageSupport,
|
|
280
|
+
dynamicExtensions,
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
// Memoize imperative methods
|
|
284
|
+
const expandAll = useCallback(() => {
|
|
285
|
+
if (editorViewRef.current) {
|
|
286
|
+
const view = editorViewRef.current;
|
|
287
|
+
const state = view.state;
|
|
288
|
+
|
|
289
|
+
view.dispatch({});
|
|
290
|
+
|
|
291
|
+
ensureSyntaxTree(view.state, state.doc.length, 5000);
|
|
292
|
+
|
|
293
|
+
unfoldAll(view);
|
|
294
|
+
}
|
|
295
|
+
}, []);
|
|
296
|
+
|
|
297
|
+
const collapseAll = useCallback(() => {
|
|
298
|
+
if (editorViewRef.current) {
|
|
299
|
+
const view = editorViewRef.current;
|
|
300
|
+
const state = view.state;
|
|
301
|
+
|
|
302
|
+
view.dispatch({});
|
|
303
|
+
|
|
304
|
+
ensureSyntaxTree(view.state, state.doc.length, 5000);
|
|
305
|
+
|
|
306
|
+
foldAll(view);
|
|
307
|
+
}
|
|
308
|
+
}, []);
|
|
309
|
+
|
|
310
|
+
// Expose methods via ref
|
|
311
|
+
useImperativeHandle(ref, () => ({
|
|
312
|
+
expandAll,
|
|
313
|
+
collapseAll,
|
|
314
|
+
getStyledCode: () => {
|
|
315
|
+
return getStyledCode(sdlLanguageSupport, code, theme);
|
|
316
|
+
},
|
|
317
|
+
}), [expandAll, collapseAll, sdlLanguageSupport, code, theme]);
|
|
318
|
+
|
|
319
|
+
const onInternalCodeChange = useCallback((newCode: string) => {
|
|
320
|
+
onCodeChange(newCode);
|
|
321
|
+
}, [onCodeChange]);
|
|
322
|
+
|
|
323
|
+
const onViewUpdate = useCallback((viewUpdate: ViewUpdate) => {
|
|
324
|
+
if (viewUpdate.state) {
|
|
325
|
+
const state = viewUpdate.state;
|
|
326
|
+
const selection = state.selection.main;
|
|
327
|
+
|
|
328
|
+
if (selection) {
|
|
329
|
+
const head = selection.head;
|
|
330
|
+
const line = state.doc.lineAt(head);
|
|
331
|
+
const lineNumber = line.number;
|
|
332
|
+
const columnNumber = head - line.from + 1;
|
|
333
|
+
|
|
334
|
+
// Only update if position actually changed
|
|
335
|
+
if (
|
|
336
|
+
lastCursorPosition.current.line !== lineNumber ||
|
|
337
|
+
lastCursorPosition.current.col !== columnNumber
|
|
338
|
+
) {
|
|
339
|
+
const newPosition = { line: lineNumber, col: columnNumber };
|
|
340
|
+
lastCursorPosition.current = newPosition;
|
|
341
|
+
onCursorChange(newPosition);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}, [onCursorChange]);
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<div className="h-full w-full">
|
|
349
|
+
<CodeMirror
|
|
350
|
+
value={code}
|
|
351
|
+
theme={theme === "dark" ? darkTheme : lightTheme}
|
|
352
|
+
width="100%"
|
|
353
|
+
height="100%"
|
|
354
|
+
className="h-full w-full border border-base-300 rounded-md"
|
|
355
|
+
onChange={onInternalCodeChange}
|
|
356
|
+
onUpdate={onViewUpdate}
|
|
357
|
+
onCreateEditor={(view) => {
|
|
358
|
+
editorViewRef.current = view;
|
|
359
|
+
}}
|
|
360
|
+
indentWithTab
|
|
361
|
+
extensions={extensions}
|
|
362
|
+
basicSetup={{
|
|
363
|
+
lineNumbers: true,
|
|
364
|
+
syntaxHighlighting: true,
|
|
365
|
+
history: true,
|
|
366
|
+
bracketMatching: true,
|
|
367
|
+
closeBrackets: true,
|
|
368
|
+
highlightActiveLineGutter: true,
|
|
369
|
+
highlightActiveLine: true,
|
|
370
|
+
highlightSelectionMatches: true,
|
|
371
|
+
autocompletion: false,
|
|
372
|
+
defaultKeymap: true,
|
|
373
|
+
searchKeymap: false,
|
|
374
|
+
historyKeymap: true,
|
|
375
|
+
foldGutter: false,
|
|
376
|
+
closeBracketsKeymap: false,
|
|
377
|
+
foldKeymap: false,
|
|
378
|
+
completionKeymap: true,
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
},
|
|
384
|
+
);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Theme } from "../hooks/useTheme.ts";
|
|
3
|
+
|
|
4
|
+
interface MobileDrawerProps {
|
|
5
|
+
theme: Theme;
|
|
6
|
+
isSettingsShown: boolean;
|
|
7
|
+
onToggleSettings: () => void;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function MobileDrawer(
|
|
12
|
+
{ theme, isSettingsShown, onToggleSettings, children }: MobileDrawerProps,
|
|
13
|
+
) {
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
{isSettingsShown && (
|
|
17
|
+
<div
|
|
18
|
+
className="fixed inset-0 bg-black/50 z-40 transition-opacity duration-300"
|
|
19
|
+
onClick={onToggleSettings}
|
|
20
|
+
/>
|
|
21
|
+
)}
|
|
22
|
+
<div
|
|
23
|
+
className={`fixed top-0 right-0 h-full w-80 bg-base-100 shadow-xl z-40 transform transition-transform duration-300 ease-in-out ${
|
|
24
|
+
isSettingsShown ? "translate-x-0" : "translate-x-full"
|
|
25
|
+
}`}
|
|
26
|
+
>
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
onClick={onToggleSettings}
|
|
30
|
+
className={`absolute top-2 right-2 btn btn-ghost px-2 py-2 z-10 ${
|
|
31
|
+
theme === "dark" ? "hover:bg-gray-500 hover:bg-opacity-20" : ""
|
|
32
|
+
}`}
|
|
33
|
+
title="Close settings"
|
|
34
|
+
>
|
|
35
|
+
<svg
|
|
36
|
+
className="w-6 h-6"
|
|
37
|
+
fill="none"
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
>
|
|
41
|
+
<path
|
|
42
|
+
strokeLinecap="round"
|
|
43
|
+
strokeLinejoin="round"
|
|
44
|
+
strokeWidth={2}
|
|
45
|
+
d="M6 18L18 6M6 6l12 12"
|
|
46
|
+
/>
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
<div className="h-full p-4 pt-4 overflow-hidden">
|
|
50
|
+
<div className="overflow-y-auto h-full">
|
|
51
|
+
{children}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
import type { Theme } from "../hooks/useTheme.ts";
|
|
3
|
+
import { getLogoSvg } from "../logo/getLogoSvg.ts";
|
|
4
|
+
|
|
5
|
+
const logoSvg = getLogoSvg();
|
|
6
|
+
|
|
7
|
+
interface NavbarProps {
|
|
8
|
+
theme: Theme;
|
|
9
|
+
onCopy: () => void;
|
|
10
|
+
onSave: () => void;
|
|
11
|
+
onLoad: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
12
|
+
onShare: () => void;
|
|
13
|
+
onToggleSettings: () => void;
|
|
14
|
+
isSettingsShown: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Navbar({
|
|
18
|
+
theme,
|
|
19
|
+
onCopy,
|
|
20
|
+
onSave,
|
|
21
|
+
onLoad,
|
|
22
|
+
onShare,
|
|
23
|
+
onToggleSettings,
|
|
24
|
+
isSettingsShown,
|
|
25
|
+
}: NavbarProps) {
|
|
26
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
27
|
+
const handleLoadClick = () => {
|
|
28
|
+
fileInputRef.current?.click();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="navbar bg-base-200 py-2 pl-0 pr-2 min-h-14">
|
|
33
|
+
<div className="flex-1 flex items-center">
|
|
34
|
+
<div
|
|
35
|
+
className="h-6 sm:h-8 md:h-10 w-auto mr-2 -ml-2 [&_svg]:h-6 [&_svg]:sm:h-8 [&_svg]:md:h-10 [&_svg]:w-auto"
|
|
36
|
+
dangerouslySetInnerHTML={{ __html: logoSvg }}
|
|
37
|
+
/>
|
|
38
|
+
<h1 className="text-sm sm:text-base md:text-lg font-semibold truncate -ml-3 -mt-1">
|
|
39
|
+
SDL Editor
|
|
40
|
+
</h1>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="flex-none">
|
|
43
|
+
<ul
|
|
44
|
+
className={`menu-horizontal px-0 items-center [&_button]:btn [&_button]:btn-ghost [&_button]:px-2 [&_button]:py-2 [&_label]:px-2 [&_label]:py-2 ${
|
|
45
|
+
theme === "dark"
|
|
46
|
+
? "[&>li>button]:hover:bg-gray-500 [&>li>button]:hover:bg-opacity-20"
|
|
47
|
+
: ""
|
|
48
|
+
}`}
|
|
49
|
+
>
|
|
50
|
+
<li>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
onClick={onCopy}
|
|
54
|
+
title="Copy All"
|
|
55
|
+
className="[&_svg]:w-6 [&_svg]:h-6"
|
|
56
|
+
>
|
|
57
|
+
<svg
|
|
58
|
+
fill="none"
|
|
59
|
+
stroke="currentColor"
|
|
60
|
+
viewBox="0 0 24 24"
|
|
61
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
62
|
+
className="w-6 h-6"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
strokeLinecap="round"
|
|
66
|
+
strokeLinejoin="round"
|
|
67
|
+
strokeWidth={2}
|
|
68
|
+
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
69
|
+
/>
|
|
70
|
+
</svg>
|
|
71
|
+
</button>
|
|
72
|
+
</li>
|
|
73
|
+
<li>
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
onClick={onSave}
|
|
77
|
+
title="Save"
|
|
78
|
+
className="[&_svg]:w-6 [&_svg]:h-6"
|
|
79
|
+
>
|
|
80
|
+
<svg
|
|
81
|
+
viewBox="0 0 24 24"
|
|
82
|
+
fill="none"
|
|
83
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
84
|
+
className="w-6 h-6"
|
|
85
|
+
>
|
|
86
|
+
<path
|
|
87
|
+
d="M19 15V17C19 18.1046 18.1046 19 17 19H7C5.89543 19 5 18.1046 5 17V15M12 5V15M12 15L10 13M12 15L14 13"
|
|
88
|
+
stroke="currentColor"
|
|
89
|
+
strokeWidth="2"
|
|
90
|
+
strokeLinecap="round"
|
|
91
|
+
strokeLinejoin="round"
|
|
92
|
+
/>
|
|
93
|
+
</svg>
|
|
94
|
+
</button>
|
|
95
|
+
</li>
|
|
96
|
+
<li>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
onClick={handleLoadClick}
|
|
100
|
+
title="Load"
|
|
101
|
+
className="[&_svg]:w-6 [&_svg]:h-6"
|
|
102
|
+
>
|
|
103
|
+
<svg
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
fill="none"
|
|
106
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
107
|
+
className="w-6 h-6"
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
d="M19 15V17C19 18.1046 18.1046 19 17 19H7C5.89543 19 5 18.1046 5 17V15M12 15L12 5M12 5L14 7M12 5L10 7"
|
|
111
|
+
stroke="currentColor"
|
|
112
|
+
strokeWidth="2"
|
|
113
|
+
strokeLinecap="round"
|
|
114
|
+
strokeLinejoin="round"
|
|
115
|
+
/>
|
|
116
|
+
</svg>
|
|
117
|
+
</button>
|
|
118
|
+
<input
|
|
119
|
+
ref={fileInputRef}
|
|
120
|
+
type="file"
|
|
121
|
+
accept=".sdl,text/plain"
|
|
122
|
+
style={{ display: "none" }}
|
|
123
|
+
onChange={onLoad}
|
|
124
|
+
/>
|
|
125
|
+
</li>
|
|
126
|
+
<li>
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
onClick={onShare}
|
|
130
|
+
title="Share"
|
|
131
|
+
className="[&_svg]:w-6 [&_svg]:h-6"
|
|
132
|
+
>
|
|
133
|
+
<svg
|
|
134
|
+
viewBox="0 0 24 24"
|
|
135
|
+
fill="none"
|
|
136
|
+
stroke="currentColor"
|
|
137
|
+
strokeWidth="2"
|
|
138
|
+
strokeLinecap="round"
|
|
139
|
+
strokeLinejoin="round"
|
|
140
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
141
|
+
className="w-6 h-6"
|
|
142
|
+
>
|
|
143
|
+
<path d="M9 12C9 13.3807 7.88071 14.5 6.5 14.5C5.11929 14.5 4 13.3807 4 12C4 10.6193 5.11929 9.5 6.5 9.5C7.88071 9.5 9 10.6193 9 12Z" />
|
|
144
|
+
<path d="M14 6.5L9 10" />
|
|
145
|
+
<path d="M14 17.5L9 14" />
|
|
146
|
+
<path d="M19 18.5C19 19.8807 17.8807 21 16.5 21C15.1193 21 14 19.8807 14 18.5C14 17.1193 15.1193 16 16.5 16C17.8807 16 19 17.1193 19 18.5Z" />
|
|
147
|
+
<path d="M19 5.5C19 6.88071 17.8807 8 16.5 8C15.1193 8 14 6.88071 14 5.5C14 4.11929 15.1193 3 16.5 3C17.8807 3 19 4.11929 19 5.5Z" />
|
|
148
|
+
</svg>
|
|
149
|
+
</button>
|
|
150
|
+
</li>
|
|
151
|
+
{
|
|
152
|
+
<li>
|
|
153
|
+
<button
|
|
154
|
+
type="button"
|
|
155
|
+
onClick={onToggleSettings}
|
|
156
|
+
title={isSettingsShown ? "Close settings" : "Open settings"}
|
|
157
|
+
className="[&_svg]:w-6 [&_svg]:h-6"
|
|
158
|
+
>
|
|
159
|
+
{isSettingsShown
|
|
160
|
+
? (
|
|
161
|
+
<svg
|
|
162
|
+
fill="none"
|
|
163
|
+
stroke="currentColor"
|
|
164
|
+
strokeWidth="2"
|
|
165
|
+
strokeLinecap="round"
|
|
166
|
+
strokeLinejoin="round"
|
|
167
|
+
viewBox="0 0 24 24"
|
|
168
|
+
className="w-6 h-6"
|
|
169
|
+
>
|
|
170
|
+
<path
|
|
171
|
+
fill="currentColor"
|
|
172
|
+
opacity="0.3"
|
|
173
|
+
fillRule="evenodd"
|
|
174
|
+
clipRule="evenodd"
|
|
175
|
+
d="M11 3C10.4477 3 10 3.44772 10 4V4.56876C10 4.99658 9.71288 5.36825 9.31776 5.53229C8.9225 5.6964 8.46228 5.63386 8.15966 5.33123L7.75734 4.92891C7.36681 4.53839 6.73365 4.53839 6.34312 4.92891L4.92891 6.34313C4.53838 6.73365 4.53838 7.36681 4.92891 7.75734L5.33123 8.15966C5.63386 8.46229 5.6964 8.9225 5.53229 9.31776C5.36825 9.71288 4.99658 10 4.56877 10L4 10C3.44772 10 3 10.4477 3 11V13C3 13.5523 3.44772 14 4 14H4.56879C4.99659 14 5.36825 14.2871 5.53228 14.6822C5.69638 15.0775 5.63384 15.5377 5.33123 15.8403L4.92889 16.2426C4.53837 16.6331 4.53837 17.2663 4.92889 17.6568L6.34311 19.071C6.73363 19.4616 7.36679 19.4616 7.75732 19.071L8.1596 18.6688C8.46223 18.3661 8.92247 18.3036 9.31774 18.4677C9.71287 18.6317 10 19.0034 10 19.4313V20C10 20.5523 10.4477 21 11 21H13C13.5523 21 14 20.5523 14 20V19.4312C14 19.0034 14.2871 18.6318 14.6822 18.4677C15.0775 18.3036 15.5377 18.3661 15.8403 18.6688L16.2426 19.071C16.6331 19.4616 17.2663 19.4616 17.6568 19.071L19.071 17.6568C19.4616 17.2663 19.4616 16.6331 19.071 16.2426L18.6688 15.8403C18.3661 15.5377 18.3036 15.0775 18.4677 14.6822C18.6318 14.2871 19.0034 14 19.4312 14H20C20.5523 14 21 13.5523 21 13V11C21 10.4477 20.5523 10 20 10L19.4313 10C19.0034 10 18.6317 9.71287 18.4677 9.31774C18.3036 8.92247 18.3661 8.46223 18.6688 8.1596L19.071 7.75734C19.4615 7.36681 19.4616 6.73365 19.071 6.34312L17.6568 4.92891C17.2663 4.53838 16.6331 4.53838 16.2426 4.92891L15.8403 5.33123C15.5377 5.63384 15.0775 5.69638 14.6822 5.53228C14.2871 5.36825 14 4.99659 14 4.56879V4C14 3.44772 13.5523 3 13 3H11ZM12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z"
|
|
176
|
+
/>
|
|
177
|
+
<path d="M11 3H13C13.5523 3 14 3.44772 14 4V4.56879C14 4.99659 14.2871 5.36825 14.6822 5.53228C15.0775 5.69638 15.5377 5.63384 15.8403 5.33123L16.2426 4.92891C16.6331 4.53838 17.2663 4.53838 17.6568 4.92891L19.071 6.34312C19.4616 6.73365 19.4615 7.36681 19.071 7.75734L18.6688 8.1596C18.3661 8.46223 18.3036 8.92247 18.4677 9.31774C18.6317 9.71287 19.0034 10 19.4313 10L20 10C20.5523 10 21 10.4477 21 11V13C21 13.5523 20.5523 14 20 14H19.4312C19.0034 14 18.6318 14.2871 18.4677 14.6822C18.3036 15.0775 18.3661 15.5377 18.6688 15.8403L19.071 16.2426C19.4616 16.6331 19.4616 17.2663 19.071 17.6568L17.6568 19.071C17.2663 19.4616 16.6331 19.4616 16.2426 19.071L15.8403 18.6688C15.5377 18.3661 15.0775 18.3036 14.6822 18.4677C14.2871 18.6318 14 19.0034 14 19.4312V20C14 20.5523 13.5523 21 13 21H11C10.4477 21 10 20.5523 10 20V19.4313C10 19.0034 9.71287 18.6317 9.31774 18.4677C8.92247 18.3036 8.46223 18.3661 8.1596 18.6688L7.75732 19.071C7.36679 19.4616 6.73363 19.4616 6.34311 19.071L4.92889 17.6568C4.53837 17.2663 4.53837 16.6331 4.92889 16.2426L5.33123 15.8403C5.63384 15.5377 5.69638 15.0775 5.53228 14.6822C5.36825 14.2871 4.99659 14 4.56879 14H4C3.44772 14 3 13.5523 3 13V11C3 10.4477 3.44772 10 4 10L4.56877 10C4.99658 10 5.36825 9.71288 5.53229 9.31776C5.6964 8.9225 5.63386 8.46229 5.33123 8.15966L4.92891 7.75734C4.53838 7.36681 4.53838 6.73365 4.92891 6.34313L6.34312 4.92891C6.73365 4.53839 7.36681 4.53839 7.75734 4.92891L8.15966 5.33123C8.46228 5.63386 8.9225 5.6964 9.31776 5.53229C9.71288 5.36825 10 4.99658 10 4.56876V4C10 3.44772 10.4477 3 11 3Z" />
|
|
178
|
+
<path d="M14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z" />
|
|
179
|
+
</svg>
|
|
180
|
+
)
|
|
181
|
+
: (
|
|
182
|
+
<svg
|
|
183
|
+
fill="none"
|
|
184
|
+
strokeWidth="2"
|
|
185
|
+
strokeLinecap="round"
|
|
186
|
+
strokeLinejoin="round"
|
|
187
|
+
stroke="currentColor"
|
|
188
|
+
viewBox="0 0 24 24"
|
|
189
|
+
className="w-6 h-6"
|
|
190
|
+
>
|
|
191
|
+
<path d="M11 3H13C13.5523 3 14 3.44772 14 4V4.56879C14 4.99659 14.2871 5.36825 14.6822 5.53228C15.0775 5.69638 15.5377 5.63384 15.8403 5.33123L16.2426 4.92891C16.6331 4.53838 17.2663 4.53838 17.6568 4.92891L19.071 6.34312C19.4616 6.73365 19.4615 7.36681 19.071 7.75734L18.6688 8.1596C18.3661 8.46223 18.3036 8.92247 18.4677 9.31774C18.6317 9.71287 19.0034 10 19.4313 10L20 10C20.5523 10 21 10.4477 21 11V13C21 13.5523 20.5523 14 20 14H19.4312C19.0034 14 18.6318 14.2871 18.4677 14.6822C18.3036 15.0775 18.3661 15.5377 18.6688 15.8403L19.071 16.2426C19.4616 16.6331 19.4616 17.2663 19.071 17.6568L17.6568 19.071C17.2663 19.4616 16.6331 19.4616 16.2426 19.071L15.8403 18.6688C15.5377 18.3661 15.0775 18.3036 14.6822 18.4677C14.2871 18.6318 14 19.0034 14 19.4312V20C14 20.5523 13.5523 21 13 21H11C10.4477 21 10 20.5523 10 20V19.4313C10 19.0034 9.71287 18.6317 9.31774 18.4677C8.92247 18.3036 8.46223 18.3661 8.1596 18.6688L7.75732 19.071C7.36679 19.4616 6.73363 19.4616 6.34311 19.071L4.92889 17.6568C4.53837 17.2663 4.53837 16.6331 4.92889 16.2426L5.33123 15.8403C5.63384 15.5377 5.69638 15.0775 5.53228 14.6822C5.36825 14.2871 4.99659 14 4.56879 14H4C3.44772 14 3 13.5523 3 13V11C3 10.4477 3.44772 10 4 10L4.56877 10C4.99658 10 5.36825 9.71288 5.53229 9.31776C5.6964 8.9225 5.63386 8.46229 5.33123 8.15966L4.92891 7.75734C4.53838 7.36681 4.53838 6.73365 4.92891 6.34313L6.34312 4.92891C6.73365 4.53839 7.36681 4.53839 7.75734 4.92891L8.15966 5.33123C8.46228 5.63386 8.9225 5.6964 9.31776 5.53229C9.71288 5.36825 10 4.99658 10 4.56876V4C10 3.44772 10.4477 3 11 3Z" />
|
|
192
|
+
<path d="M14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z" />
|
|
193
|
+
</svg>
|
|
194
|
+
)}
|
|
195
|
+
</button>
|
|
196
|
+
</li>
|
|
197
|
+
}
|
|
198
|
+
<li>
|
|
199
|
+
<a
|
|
200
|
+
href="https://github.com/MPEGGroup/mpeg-sdl-editor"
|
|
201
|
+
target="_blank"
|
|
202
|
+
rel="noopener noreferrer"
|
|
203
|
+
title="GitHub Repository"
|
|
204
|
+
className="btn btn-ghost px-2 py-2 [&_svg]:w-6 [&_svg]:h-6"
|
|
205
|
+
>
|
|
206
|
+
<svg
|
|
207
|
+
fill="none"
|
|
208
|
+
stroke="currentColor"
|
|
209
|
+
viewBox="0 0 24 24"
|
|
210
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
211
|
+
className="w-6 h-6"
|
|
212
|
+
>
|
|
213
|
+
<g transform="translate(-82.000000, -7398.000000)">
|
|
214
|
+
<path
|
|
215
|
+
fill="currentColor"
|
|
216
|
+
d="M94,7399 C99.523,7399 104,7403.59 104,7409.253 C104,7413.782 101.138,7417.624 97.167,7418.981 C96.66,7419.082 96.48,7418.762 96.48,7418.489 C96.48,7418.151 96.492,7417.047 96.492,7415.675 C96.492,7414.719 96.172,7414.095 95.813,7413.777 C98.04,7413.523 100.38,7412.656 100.38,7408.718 C100.38,7407.598 99.992,7406.684 99.35,7405.966 C99.454,7405.707 99.797,7404.664 99.252,7403.252 C99.252,7403.252 98.414,7402.977 96.505,7404.303 C95.706,7404.076 94.85,7403.962 94,7403.958 C93.15,7403.962 92.295,7404.076 91.497,7404.303 C89.586,7402.977 88.746,7403.252 88.746,7403.252 C88.203,7404.664 88.546,7405.707 88.649,7405.966 C88.01,7406.684 87.619,7407.598 87.619,7408.718 C87.619,7412.646 89.954,7413.526 92.175,7413.785 C91.889,7414.041 91.63,7414.493 91.54,7415.156 C90.97,7415.418 89.522,7415.871 88.63,7414.304 C88.63,7414.304 88.101,7413.319 87.097,7413.247 C87.097,7413.247 86.122,7413.234 87.029,7413.87 C87.029,7413.87 87.684,7414.185 88.139,7415.37 C88.139,7415.37 88.726,7417.2 91.508,7416.58 C91.513,7417.437 91.522,7418.245 91.522,7418.489 C91.522,7418.76 91.338,7419.077 90.839,7418.982 C86.865,7417.627 84,7413.783 84,7409.253 C84,7403.59 88.478,7399 94,7399"
|
|
217
|
+
/>
|
|
218
|
+
</g>
|
|
219
|
+
</svg>
|
|
220
|
+
</a>
|
|
221
|
+
</li>
|
|
222
|
+
</ul>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
}
|