@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
package/src/App.tsx
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SemanticError,
|
|
3
|
+
SemanticWarning,
|
|
4
|
+
SyntaxError,
|
|
5
|
+
} from "@mpeggroup/mpeg-sdl-parser";
|
|
6
|
+
import { Editor } from "./components/Editor.tsx";
|
|
7
|
+
import type { EditorRef } from "./components/Editor.tsx";
|
|
8
|
+
import { Navbar } from "./components/Navbar.tsx";
|
|
9
|
+
import { SubNav } from "./components/SubNav.tsx";
|
|
10
|
+
import { Settings } from "./components/Settings.tsx";
|
|
11
|
+
import { StatusBar } from "./components/StatusBar.tsx";
|
|
12
|
+
import { ResizableLayout } from "./components/ResizableLayout.tsx";
|
|
13
|
+
import { useToast } from "./hooks/useToast.ts";
|
|
14
|
+
import { useFileOperations } from "./hooks/useFileOperations.ts";
|
|
15
|
+
import { useTheme } from "./hooks/useTheme.ts";
|
|
16
|
+
import { usePrettier } from "./hooks/usePrettier.ts";
|
|
17
|
+
import { useRulerWidth } from "./hooks/useRulerWidth.ts";
|
|
18
|
+
import { useAutoDisplayCompletions } from "./hooks/useAutoDisplayCompletions.ts";
|
|
19
|
+
import { useEnableLinting } from "./hooks/useEnableLinting.ts";
|
|
20
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
21
|
+
import { useMobileDetection } from "./hooks/useMobileDetection.ts";
|
|
22
|
+
import { useShowSyntaxErrors } from "./hooks/useShowSyntaxErrors.ts";
|
|
23
|
+
import { useShowSemanticErrors } from "./hooks/useShowSymanticErrors.ts";
|
|
24
|
+
import { useShowSemanticWarnings } from "./hooks/useShowSemanticWarnings.ts";
|
|
25
|
+
|
|
26
|
+
function getInitialCodeFromHash(): string | null {
|
|
27
|
+
const hash = globalThis.location.hash;
|
|
28
|
+
if (hash.startsWith("#c=")) {
|
|
29
|
+
try {
|
|
30
|
+
return atob(hash.slice(3));
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function App() {
|
|
39
|
+
const defaultCode =
|
|
40
|
+
"// Start typing your SDL here... <Ctrl+Space> for completions\n";
|
|
41
|
+
|
|
42
|
+
const [code, setCodeInternal] = useState<string>(
|
|
43
|
+
() => getInitialCodeFromHash() ?? defaultCode,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const setCode = useCallback((newCode: string) => {
|
|
47
|
+
setCodeInternal(newCode);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const handleHashChange = () => {
|
|
52
|
+
const hashCode = getInitialCodeFromHash();
|
|
53
|
+
if (hashCode !== null) {
|
|
54
|
+
setCode(hashCode);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
globalThis.addEventListener("hashchange", handleHashChange);
|
|
58
|
+
return () => globalThis.removeEventListener("hashchange", handleHashChange);
|
|
59
|
+
}, []);
|
|
60
|
+
const [toastState, showToast] = useToast();
|
|
61
|
+
const { theme, toggleTheme } = useTheme();
|
|
62
|
+
const isMobile = useMobileDetection();
|
|
63
|
+
const [isSettingsShown, setSettingsShown] = useState(false);
|
|
64
|
+
const { rulerWidth, setRulerWidth } = useRulerWidth();
|
|
65
|
+
const { autoDisplayCompletions, setAutoDisplayCompletions } =
|
|
66
|
+
useAutoDisplayCompletions();
|
|
67
|
+
const { enableLinting, setEnableLinting } = useEnableLinting();
|
|
68
|
+
const { showSyntaxErrors, setShowSyntaxErrors } = useShowSyntaxErrors();
|
|
69
|
+
const { showSemanticErrors, setShowSemanticErrors } = useShowSemanticErrors();
|
|
70
|
+
const { showSemanticWarnings, setShowSemanticWarnings } =
|
|
71
|
+
useShowSemanticWarnings();
|
|
72
|
+
const [cursorPosition, setCursorPosition] = useState<
|
|
73
|
+
{ line: number; col: number }
|
|
74
|
+
>({ line: 1, col: 1 });
|
|
75
|
+
const editorRef = useRef<EditorRef>(null);
|
|
76
|
+
|
|
77
|
+
const onCursorChange = useCallback(
|
|
78
|
+
(position: { line: number; col: number }) => {
|
|
79
|
+
setCursorPosition((prev) => {
|
|
80
|
+
// Only update if position actually changed
|
|
81
|
+
if (prev.line !== position.line || prev.col !== position.col) {
|
|
82
|
+
return position;
|
|
83
|
+
}
|
|
84
|
+
return prev;
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
[],
|
|
88
|
+
);
|
|
89
|
+
const handleShare = useCallback(async () => {
|
|
90
|
+
const encoded = btoa(code);
|
|
91
|
+
const url =
|
|
92
|
+
`${globalThis.location.origin}${globalThis.location.pathname}#c=${encoded}`;
|
|
93
|
+
try {
|
|
94
|
+
await navigator.clipboard.writeText(url);
|
|
95
|
+
showToast("URL copied!");
|
|
96
|
+
} catch {
|
|
97
|
+
showToast("Failed to copy URL", "error");
|
|
98
|
+
}
|
|
99
|
+
}, [code, showToast]);
|
|
100
|
+
|
|
101
|
+
const {
|
|
102
|
+
handleSave,
|
|
103
|
+
handleLoad,
|
|
104
|
+
handleCopy,
|
|
105
|
+
} = useFileOperations({
|
|
106
|
+
code,
|
|
107
|
+
setCode,
|
|
108
|
+
showToast,
|
|
109
|
+
editorRef,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const lineCount = useMemo(() => code.split("\n").length, [code]);
|
|
113
|
+
const characterCount = useMemo(() => code.length, [code]);
|
|
114
|
+
const [syntaxErrors, setSyntaxErrors] = useState<SyntaxError[]>([]);
|
|
115
|
+
const [semanticErrors, setSemanticErrors] = useState<SemanticError[]>([]);
|
|
116
|
+
const [semanticWarnings, setSemanticWarnings] = useState<SemanticWarning[]>(
|
|
117
|
+
[],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const onSyntaxErrorChange = useCallback((newErrors: SyntaxError[]) => {
|
|
121
|
+
setSyntaxErrors(newErrors);
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
124
|
+
const onSemanticErrorChange = useCallback((newErrors: SemanticError[]) => {
|
|
125
|
+
setSemanticErrors(newErrors);
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
const onSemanticWarningChange = useCallback(
|
|
129
|
+
(newErrors: SemanticWarning[]) => {
|
|
130
|
+
setSemanticWarnings(newErrors);
|
|
131
|
+
},
|
|
132
|
+
[],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const syntaxErrorCount = useMemo(() => syntaxErrors.length, [
|
|
136
|
+
syntaxErrors,
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
const semanticErrorCount = useMemo(() => semanticErrors.length, [
|
|
140
|
+
semanticErrors,
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
const semanticWarningCount = useMemo(() => semanticWarnings.length, [
|
|
144
|
+
semanticWarnings,
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
const { handlePrettify } = usePrettier({
|
|
148
|
+
code,
|
|
149
|
+
setCode,
|
|
150
|
+
showToast,
|
|
151
|
+
rulerWidth,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const toggleSettings = useCallback(() => {
|
|
155
|
+
setSettingsShown((prev) => !prev);
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
const handleExpandAll = useCallback(() => {
|
|
159
|
+
if (editorRef.current) {
|
|
160
|
+
editorRef.current.expandAll();
|
|
161
|
+
}
|
|
162
|
+
}, []);
|
|
163
|
+
|
|
164
|
+
const handleCollapseAll = useCallback(() => {
|
|
165
|
+
if (editorRef.current) {
|
|
166
|
+
editorRef.current.collapseAll();
|
|
167
|
+
}
|
|
168
|
+
}, []);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div className="flex flex-col h-screen">
|
|
172
|
+
<Navbar
|
|
173
|
+
theme={theme}
|
|
174
|
+
onCopy={handleCopy}
|
|
175
|
+
onSave={handleSave}
|
|
176
|
+
onLoad={handleLoad}
|
|
177
|
+
onShare={handleShare}
|
|
178
|
+
onToggleSettings={toggleSettings}
|
|
179
|
+
isSettingsShown={isSettingsShown}
|
|
180
|
+
/>
|
|
181
|
+
<div className="grow overflow-hidden" data-testid="main-content">
|
|
182
|
+
<ResizableLayout
|
|
183
|
+
theme={theme}
|
|
184
|
+
isSettingsShown={isSettingsShown}
|
|
185
|
+
isMobile={isMobile}
|
|
186
|
+
onToggleSettings={toggleSettings}
|
|
187
|
+
>
|
|
188
|
+
<div className="flex flex-col h-full">
|
|
189
|
+
<SubNav
|
|
190
|
+
theme={theme}
|
|
191
|
+
onPrettify={handlePrettify}
|
|
192
|
+
onExpandAll={handleExpandAll}
|
|
193
|
+
onCollapseAll={handleCollapseAll}
|
|
194
|
+
/>
|
|
195
|
+
<div className="grow overflow-auto">
|
|
196
|
+
<Editor
|
|
197
|
+
ref={editorRef}
|
|
198
|
+
code={code}
|
|
199
|
+
onCodeChange={setCode}
|
|
200
|
+
onCursorChange={onCursorChange}
|
|
201
|
+
onSyntaxErrorChange={onSyntaxErrorChange}
|
|
202
|
+
onSemanticErrorChange={onSemanticErrorChange}
|
|
203
|
+
onSemanticWarningChange={onSemanticWarningChange}
|
|
204
|
+
theme={theme}
|
|
205
|
+
rulerWidth={rulerWidth}
|
|
206
|
+
autoDisplayCompletions={autoDisplayCompletions}
|
|
207
|
+
enableLinting={enableLinting}
|
|
208
|
+
showSyntaxErrors={showSyntaxErrors}
|
|
209
|
+
showSemanticErrors={showSemanticErrors}
|
|
210
|
+
showSemanticWarnings={showSemanticWarnings}
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
<StatusBar
|
|
214
|
+
lineCount={lineCount}
|
|
215
|
+
characterCount={characterCount}
|
|
216
|
+
syntaxErrorCount={syntaxErrorCount}
|
|
217
|
+
semanticErrorCount={semanticErrorCount}
|
|
218
|
+
semanticWarningCount={semanticWarningCount}
|
|
219
|
+
cursorPosition={cursorPosition}
|
|
220
|
+
enableLinting={enableLinting}
|
|
221
|
+
showSyntaxErrors={showSyntaxErrors}
|
|
222
|
+
showSemanticErrors={showSemanticErrors}
|
|
223
|
+
showSemanticWarnings={showSemanticWarnings}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div className="h-full md:p-2 md:pl-0">
|
|
228
|
+
<Settings
|
|
229
|
+
theme={theme}
|
|
230
|
+
onToggleTheme={toggleTheme}
|
|
231
|
+
rulerWidth={rulerWidth}
|
|
232
|
+
onRulerWidthChange={setRulerWidth}
|
|
233
|
+
autoDisplayCompletions={autoDisplayCompletions}
|
|
234
|
+
onAutoDisplayCompletionsChange={setAutoDisplayCompletions}
|
|
235
|
+
enableLinting={enableLinting}
|
|
236
|
+
onEnableLintingChange={setEnableLinting}
|
|
237
|
+
showSyntaxErrors={showSyntaxErrors}
|
|
238
|
+
onShowSyntaxErrorsChange={setShowSyntaxErrors}
|
|
239
|
+
showSemanticErrors={showSemanticErrors}
|
|
240
|
+
onShowSemanticErrorsChange={setShowSemanticErrors}
|
|
241
|
+
showSemanticWarnings={showSemanticWarnings}
|
|
242
|
+
onShowSemanticWarningsChange={setShowSemanticWarnings}
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
</ResizableLayout>
|
|
246
|
+
</div>
|
|
247
|
+
{toastState && (
|
|
248
|
+
<div className="toast toast-top toast-end mt-14">
|
|
249
|
+
<div
|
|
250
|
+
className={`alert ${
|
|
251
|
+
toastState.type === "success"
|
|
252
|
+
? "alert-success"
|
|
253
|
+
: toastState.type === "warning"
|
|
254
|
+
? "alert-warning"
|
|
255
|
+
: "alert-error"
|
|
256
|
+
}`}
|
|
257
|
+
>
|
|
258
|
+
<span>{toastState.message}</span>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export default App;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg
|
|
3
|
+
id="Layer_1"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
version="1.1"
|
|
6
|
+
viewBox="0 0 1800 500"
|
|
7
|
+
>
|
|
8
|
+
<!-- Generator: Adobe Illustrator 29.5.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 141) -->
|
|
9
|
+
<defs>
|
|
10
|
+
<style>.st0 {
|
|
11
|
+
fill: #08a6e0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.st1 {
|
|
15
|
+
fill: #006fa9;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.st2 {
|
|
19
|
+
fill: #0071bb;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.st3 {
|
|
23
|
+
fill: #002a53;
|
|
24
|
+
}</style>
|
|
25
|
+
</defs>
|
|
26
|
+
<path
|
|
27
|
+
class="st3"
|
|
28
|
+
d="M421.2,445.7c0,6.2-5,11.2-11.2,11.2s-11.2-5-11.2-11.2,5-11.2,11.2-11.2,11.2,5,11.2,11.2"
|
|
29
|
+
/>
|
|
30
|
+
<g>
|
|
31
|
+
<rect class="st2" x="292.5" y="348.4" width="39.2" height="39.2" />
|
|
32
|
+
<rect class="st1" x="292.5" y="230.9" width="39.2" height="39.2" />
|
|
33
|
+
<rect class="st2" x="292.5" y="113.4" width="39.2" height="39.2" />
|
|
34
|
+
<path
|
|
35
|
+
class="st2"
|
|
36
|
+
d="M292.5,74.2h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6s-19.6,8.8-19.6,19.6v19.6Z"
|
|
37
|
+
/>
|
|
38
|
+
<path
|
|
39
|
+
class="st2"
|
|
40
|
+
d="M463.1,348.4h0c10.8,0,19.6-8.8,19.6-19.6v-19.6h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6"
|
|
41
|
+
/>
|
|
42
|
+
<rect class="st2" x="443.6" y="191.7" width="39.2" height="39.2" />
|
|
43
|
+
<path
|
|
44
|
+
class="st1"
|
|
45
|
+
d="M443.6,152.6h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6s-19.6,8.8-19.6,19.6v19.6Z"
|
|
46
|
+
/>
|
|
47
|
+
<rect class="st2" x="342.8" y="270.1" width="39.2" height="39.2" />
|
|
48
|
+
<rect class="st2" x="342.8" y="152.6" width="39.2" height="39.2" />
|
|
49
|
+
<rect class="st2" x="393.2" y="211.3" width="39.2" height="39.2" />
|
|
50
|
+
<path
|
|
51
|
+
class="st2"
|
|
52
|
+
d="M393.2,133h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6s-19.6,8.8-19.6,19.6v19.6Z"
|
|
53
|
+
/>
|
|
54
|
+
<rect class="st3" x="292.5" y="309.2" width="39.2" height="39.2" />
|
|
55
|
+
<rect class="st3" x="292.5" y="191.7" width="39.2" height="39.2" />
|
|
56
|
+
<rect class="st3" x="292.5" y="74.2" width="39.2" height="39.2" />
|
|
57
|
+
<rect class="st3" x="443.6" y="270.1" width="39.2" height="39.2" />
|
|
58
|
+
<rect class="st3" x="443.6" y="152.6" width="39.2" height="39.2" />
|
|
59
|
+
<path
|
|
60
|
+
class="st3"
|
|
61
|
+
d="M362.4,387.6h0c10.8,0,19.6-8.8,19.6-19.6v-19.6h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6"
|
|
62
|
+
/>
|
|
63
|
+
<rect class="st3" x="342.8" y="230.9" width="39.2" height="39.2" />
|
|
64
|
+
<rect class="st3" x="393.2" y="172.1" width="39.2" height="39.2" />
|
|
65
|
+
<rect class="st2" x="393.2" y="289.6" width="39.2" height="39.2" />
|
|
66
|
+
<rect class="st3" x="393.2" y="250.5" width="39.2" height="39.2" />
|
|
67
|
+
<rect class="st3" x="342.8" y="113.4" width="39.2" height="39.2" />
|
|
68
|
+
<path
|
|
69
|
+
class="st0"
|
|
70
|
+
d="M312.1,426.7h0c10.8,0,19.6-8.8,19.6-19.6v-19.6h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6"
|
|
71
|
+
/>
|
|
72
|
+
<rect class="st0" x="292.5" y="270.1" width="39.2" height="39.2" />
|
|
73
|
+
<rect class="st0" x="292.5" y="152.6" width="39.2" height="39.2" />
|
|
74
|
+
<rect class="st0" x="443.6" y="230.9" width="39.2" height="39.2" />
|
|
75
|
+
<rect class="st0" x="342.8" y="309.2" width="39.2" height="39.2" />
|
|
76
|
+
<path
|
|
77
|
+
class="st3"
|
|
78
|
+
d="M563.9,270.1h0c10.8,0,19.6-8.8,19.6-19.6v-19.6h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6"
|
|
79
|
+
/>
|
|
80
|
+
<path
|
|
81
|
+
class="st2"
|
|
82
|
+
d="M544.3,230.9h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6s-19.6,8.8-19.6,19.6v19.6Z"
|
|
83
|
+
/>
|
|
84
|
+
<g>
|
|
85
|
+
<rect class="st2" x="493.9" y="230.9" width="39.2" height="39.2" />
|
|
86
|
+
<path
|
|
87
|
+
class="st3"
|
|
88
|
+
d="M513.5,152.6h0c-10.8,0-19.6,8.8-19.6,19.6v19.6h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6"
|
|
89
|
+
/>
|
|
90
|
+
<path
|
|
91
|
+
class="st3"
|
|
92
|
+
d="M533.1,270.1h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6s19.6-8.8,19.6-19.6v-19.6Z"
|
|
93
|
+
/>
|
|
94
|
+
<rect class="st0" x="493.9" y="191.7" width="39.2" height="39.2" />
|
|
95
|
+
</g>
|
|
96
|
+
<rect class="st0" x="342.8" y="191.7" width="39.2" height="39.2" />
|
|
97
|
+
<path
|
|
98
|
+
class="st3"
|
|
99
|
+
d="M412.8,368h0c10.8,0,19.6-8.8,19.6-19.6v-19.6h-39.2v19.6c0,10.8,8.8,19.6,19.6,19.6"
|
|
100
|
+
/>
|
|
101
|
+
<rect class="st0" x="393.2" y="133" width="39.2" height="39.2" />
|
|
102
|
+
<path
|
|
103
|
+
class="st0"
|
|
104
|
+
d="M342.8,113.4h39.2v-19.6c0-10.8-8.8-19.6-19.6-19.6s-19.6,8.8-19.6,19.6v19.6Z"
|
|
105
|
+
/>
|
|
106
|
+
<path
|
|
107
|
+
class="st3"
|
|
108
|
+
d="M482.7,88.2c0,10.8-8.8,19.6-19.6,19.6s-19.6-8.8-19.6-19.6,8.8-19.6,19.6-19.6,19.6,8.8,19.6,19.6"
|
|
109
|
+
/>
|
|
110
|
+
<path
|
|
111
|
+
class="st1"
|
|
112
|
+
d="M432.4,398.1c0,10.8-8.8,19.6-19.6,19.6s-19.6-8.8-19.6-19.6,8.8-19.6,19.6-19.6,19.6,8.8,19.6,19.6"
|
|
113
|
+
/>
|
|
114
|
+
<path
|
|
115
|
+
class="st0"
|
|
116
|
+
d="M521.9,85.4c0,6.2-5,11.2-11.2,11.2s-11.2-5-11.2-11.2,5-11.2,11.2-11.2,11.2,5,11.2,11.2"
|
|
117
|
+
/>
|
|
118
|
+
<path
|
|
119
|
+
class="st1"
|
|
120
|
+
d="M505.1,49c0,7.7-6.3,14-14,14s-14-6.3-14-14,6.3-14,14-14,14,6.3,14,14"
|
|
121
|
+
/>
|
|
122
|
+
<path
|
|
123
|
+
class="st0"
|
|
124
|
+
d="M465.9,426.1c0,7.7-6.3,14-14,14s-14-6.3-14-14,6.3-14,14-14,14,6.3,14,14"
|
|
125
|
+
/>
|
|
126
|
+
<path
|
|
127
|
+
class="st2"
|
|
128
|
+
d="M1297.6,143.3h0c0,10.3-8.4,18.7-18.7,18.7h-106.3v37.1h87.5c10.3,0,18.7,8.4,18.7,18.7s-8.4,18.7-18.7,18.7h-87.5v41.9h106.3c10.3,0,18.7,8.4,18.7,18.7s-8.4,18.7-18.7,18.7h-143.7V124.5h143.7c10.3,0,18.7,8.4,18.7,18.7"
|
|
129
|
+
/>
|
|
130
|
+
<path
|
|
131
|
+
class="st2"
|
|
132
|
+
d="M1464.5,162h-69.5c-20.7,0-37.5,16.8-37.5,37.5v41.5c0,20.7,16.8,37.5,37.5,37.5h35.1c22,0,39.8-17.8,39.8-39.8v-2h-37.5c-10.3,0-18.7-8.4-18.7-18.7s8.4-18.7,18.7-18.7h74.9v39.5c0,42.7-34.6,77.3-77.3,77.3h-35.1c-41.4,0-74.9-33.6-74.9-74.9v-41.5c0-41.4,33.6-74.9,74.9-74.9h69.5c10.3,0,18.7,8.4,18.7,18.7s-8.4,18.7-18.7,18.7"
|
|
133
|
+
/>
|
|
134
|
+
<path
|
|
135
|
+
class="st2"
|
|
136
|
+
d="M660.2,199.5v97.7c0,10.3-8.4,18.7-18.7,18.7s-18.7-8.4-18.7-18.7v-97.7c0-41.4,33.6-74.9,74.9-74.9s43.7,6.2,56.2,18.7c12.5-12.5,31.2-18.7,56.2-18.7,41.4,0,75,33.6,75,74.9v97.7c0,10.3-8.4,18.7-18.7,18.7s-18.7-8.4-18.7-18.7v-97.7c0-20.7-16.7-37.5-37.4-37.5-25,0-37.5,12.5-37.5,37.5v97.7c0,10.3-8.4,18.7-18.7,18.7s-18.7-8.4-18.7-18.7v-97.7c0-25-12.5-37.5-37.5-37.5-20.7,0-37.4,16.8-37.4,37.5"
|
|
137
|
+
/>
|
|
138
|
+
<path
|
|
139
|
+
class="st2"
|
|
140
|
+
d="M1029.1,124.5c50.4,0,75.3,25.4,74.9,76.2-.3,40.9-34,73.7-74.9,73.7h-74.9v22.8c0,10.3-8.4,18.7-18.7,18.7s-18.7-8.4-18.7-18.7V124.5h112.4ZM1066.6,199.5c0-25-12.5-37.5-37.5-37.5h-74.9v74.9h74.9c25,0,37.5-12.5,37.5-37.5"
|
|
141
|
+
/>
|
|
142
|
+
</g>
|
|
143
|
+
</svg>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Extension, Facet } from "@codemirror/state";
|
|
2
|
+
import { EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view";
|
|
3
|
+
|
|
4
|
+
const baseTheme = EditorView.baseTheme({
|
|
5
|
+
"&light .cm-ruler": { borderRight: "1px dotted black", opacity: 0.2 },
|
|
6
|
+
"&dark .cm-ruler": { borderRight: "1px dotted white", opacity: 0.2 },
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const rulerWidth = Facet.define<number, number>({
|
|
10
|
+
combine: (values) => values.length ? Math.min(...values) : 10,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const showRuler = ViewPlugin.fromClass(
|
|
14
|
+
class {
|
|
15
|
+
rulerDiv: HTMLDivElement;
|
|
16
|
+
currentColumn: number;
|
|
17
|
+
|
|
18
|
+
constructor(view: EditorView) {
|
|
19
|
+
this.currentColumn = view.state.facet(rulerWidth);
|
|
20
|
+
|
|
21
|
+
this.rulerDiv = view.dom.appendChild(document.createElement("div"));
|
|
22
|
+
this.rulerDiv.classList.add("cm-ruler");
|
|
23
|
+
this.rulerDiv.style.cssText =
|
|
24
|
+
"position: absolute; top: 0; height: 100%; width: 1px; pointer-events: none; overflow: hidden;";
|
|
25
|
+
|
|
26
|
+
this.updateRulePosition(view, this.currentColumn);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
update(update: ViewUpdate) {
|
|
30
|
+
const newColumn = update.state.facet(rulerWidth);
|
|
31
|
+
if (update.geometryChanged || newColumn !== this.currentColumn) {
|
|
32
|
+
this.currentColumn = newColumn;
|
|
33
|
+
this.updateRulePosition(update.view, newColumn);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
updateRulePosition(view: EditorView, column: number) {
|
|
38
|
+
const defaultCharacterWidth = view.defaultCharacterWidth;
|
|
39
|
+
const gutterWidth = view.contentDOM.getBoundingClientRect().x;
|
|
40
|
+
|
|
41
|
+
this.rulerDiv.style.left = `${
|
|
42
|
+
gutterWidth + (column * defaultCharacterWidth) + 6
|
|
43
|
+
}px`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
destroy() {
|
|
47
|
+
this.rulerDiv.remove();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export function ruler(column: number): Extension {
|
|
53
|
+
return [
|
|
54
|
+
baseTheme,
|
|
55
|
+
rulerWidth.of(column),
|
|
56
|
+
showRuler,
|
|
57
|
+
];
|
|
58
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface DesktopPanelsProps {
|
|
4
|
+
leftChild: React.ReactNode;
|
|
5
|
+
rightChild: React.ReactNode;
|
|
6
|
+
splitPercentage: number;
|
|
7
|
+
isSettingsShown: boolean;
|
|
8
|
+
isDragging: boolean;
|
|
9
|
+
onMouseDown: (e: React.MouseEvent) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function DesktopPanels({
|
|
13
|
+
leftChild,
|
|
14
|
+
rightChild,
|
|
15
|
+
splitPercentage,
|
|
16
|
+
isSettingsShown,
|
|
17
|
+
isDragging,
|
|
18
|
+
onMouseDown,
|
|
19
|
+
}: DesktopPanelsProps) {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
{/* Left panel */}
|
|
23
|
+
<div
|
|
24
|
+
className="flex flex-col"
|
|
25
|
+
style={{ width: !isSettingsShown ? "100%" : `${splitPercentage}%` }}
|
|
26
|
+
>
|
|
27
|
+
{leftChild}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
{/* Divider - only show when panel is not collapsed */}
|
|
31
|
+
|
|
32
|
+
{isSettingsShown && (
|
|
33
|
+
<div
|
|
34
|
+
className={`w-1 bg-base-300 hover:bg-primary/50 cursor-col-resize transition-all duration-150 ${
|
|
35
|
+
isDragging ? "bg-primary/70 w-1.5" : ""
|
|
36
|
+
} relative group`}
|
|
37
|
+
onMouseDown={onMouseDown}
|
|
38
|
+
title="Drag to resize panels"
|
|
39
|
+
>
|
|
40
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
41
|
+
<div
|
|
42
|
+
className={`w-0.5 h-12 bg-base-content/30 rounded-full transition-all ${
|
|
43
|
+
isDragging
|
|
44
|
+
? "bg-primary-content h-16"
|
|
45
|
+
: "group-hover:bg-base-content/50 group-hover:h-16"
|
|
46
|
+
}`}
|
|
47
|
+
>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{/* Right panel - only show when not collapsed */}
|
|
54
|
+
|
|
55
|
+
{isSettingsShown && (
|
|
56
|
+
<div
|
|
57
|
+
className="flex flex-col"
|
|
58
|
+
style={{ width: `${Math.max(100 - splitPercentage, 5)}%` }}
|
|
59
|
+
>
|
|
60
|
+
{rightChild}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|