) => {
15 | const deltaY = event.deltaY;
16 | // console.log(`滚动距离:${deltaY}px`);
17 | const element = topbarTag.current;
18 | if(element){
19 | const x = element.scrollLeft;
20 | const toX = x + deltaY;
21 | element.scrollTo(toX,0);
22 | }
23 | };
24 |
25 | const handleToEnd = ()=>{
26 | const element = topbarTag.current;
27 | if(element){
28 | setTimeout(()=>element.scrollTo(10000000,0),100);
29 | }
30 | };
31 |
32 | useEffect(() => {
33 | eventBus.on('scrollTopbarToEnd',handleToEnd);
34 | return ()=>{
35 | eventBus.off('scrollTopbarToEnd',handleToEnd);
36 | };
37 | }, []);
38 |
39 | return {props.children}
;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/components/backDashboardButton.module.scss:
--------------------------------------------------------------------------------
1 | .backButton {
2 | transition: all 0.34s;
3 | padding: 4px;
4 | border-radius: var(--radius-md);
5 | font-family: Georgia, serif;
6 | color: var(--primary);
7 | font-size: 22px;
8 | background: transparent;
9 | border: none;
10 | display: inline-flex;
11 | align-items: center;
12 |
13 | .backIcon {
14 | width: 0;
15 | transition: all 0.34s;
16 | }
17 |
18 | &:hover {
19 | background: rgba(0, 0, 0, 0.1);
20 | cursor: pointer;
21 | padding: 4px 8px;
22 |
23 | .backIcon {
24 | width: 22px;
25 | }
26 |
27 | }
28 | }
29 |
30 | .terreImg {
31 | border-radius: 4px;
32 | }
33 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/components/iconWithTextItem.module.scss:
--------------------------------------------------------------------------------
1 | .wrapper{
2 | cursor: pointer;
3 | padding: 10px 7px;
4 | display: flex;
5 | flex-flow: column;
6 | align-items: center;
7 | }
8 |
9 | .wrapper:hover{
10 | background: var(--black-5pct);
11 | border-radius: var(--radius-md);
12 | }
13 |
14 | .icon{
15 | width: 32px;
16 | height: 32px;
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | }
21 |
22 | .icon svg{
23 | color: var(--primary);
24 | font-size: 25px;
25 | height: 25px;
26 | width: 25px;
27 | }
28 |
29 | .text{
30 | color: var(--text-sub);
31 | font-size: 13px;
32 | margin-top: 3px;
33 | }
34 |
35 | .wrapperS{
36 | cursor: pointer;
37 | padding: 5px 13px;
38 | display: flex;
39 | align-items: center;
40 | }
41 |
42 | .wrapperS:hover{
43 | background: var(--black-5pct);
44 | }
45 |
46 | .iconS{
47 | display: flex;
48 | justify-content: center;
49 | align-items: center;
50 | }
51 |
52 | .textS{
53 | margin-left: 4px;
54 | font-size: 14px;
55 | }
56 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/components/topbarTab.module.scss:
--------------------------------------------------------------------------------
1 | .tab {
2 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14);
3 | //border: var(--border-md);
4 | border-radius: var(--radius-md);
5 | background: var(--bg-card);
6 | min-height: 125px;
7 | overflow: auto;
8 | display: flex;
9 | margin: 0 4px 8px 4px;
10 | padding: 4px 8px 0 8px;
11 |
12 | &::-webkit-scrollbar {
13 | //width: 3px;
14 | height: 8px;
15 | }
16 |
17 | &::-webkit-scrollbar-thumb {
18 | background-color: var(--bg-scrollbar-thumb);
19 | background-clip: content-box;
20 | border-radius: var(--radius-md);
21 | border: 2px solid transparent;
22 | }
23 |
24 | ::-webkit-scrollbar-track {
25 | background-color: transparent;
26 | }
27 |
28 | &:hover::-webkit-scrollbar-thumb {
29 | background-color: var(--bg-scrollbar-thumb-hover);
30 | }
31 |
32 | & {
33 | scrollbar-width: thin; /* Firefox */
34 | scrollbar-color: var(--bg-scrollbar-thumb) var(--bg-scrollbar); /* Firefox */
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/AddSentence/addSentence.module.scss:
--------------------------------------------------------------------------------
1 | .AsIconColor{
2 | color: var(--primary);
3 | font-size: 18px;
4 | height: 18px;
5 | width: 18px;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Export/ExportTab.tsx:
--------------------------------------------------------------------------------
1 | import TopbarTab from "@/pages/editor/Topbar/components/TopbarTab";
2 | import {TabItem} from "@/pages/editor/Topbar/components/TabItem";
3 | import {IconWithTextItem} from "@/pages/editor/Topbar/components/IconWithTextItem";
4 | import s from './export.module.scss';
5 | import AndroidIcon from "material-icon-theme/icons/android.svg";
6 | import {api} from "@/api";
7 | import { Desktop24Filled, Desktop24Regular, Globe24Filled, Globe24Regular, bundleIcon } from "@fluentui/react-icons";
8 | import useEditorStore from "@/store/useEditorStore";
9 | import { t } from "@lingui/macro";
10 |
11 | export function ExportTab() {
12 | const subPage = useEditorStore.use.subPage();
13 | const gameName = subPage;
14 | const GlobeIcon = bundleIcon(Globe24Filled, Globe24Regular);
15 | const DesktopIcon = bundleIcon(Desktop24Filled, Desktop24Regular);
16 |
17 | return
18 |
19 | api.manageGameControllerEjectGameAsWeb(gameName)}
20 | icon={}
21 | text={t`导出为网页`}/>
22 | api.manageGameControllerEjectGameAsExe(gameName)}
23 | icon={}
24 | text={t`导出为可执行文件`}/>
25 | api.manageGameControllerEjectGameAsAndroid(gameName)}
26 | icon={
}
27 | text={t`导出为安卓项目文件`}/>
28 |
29 | ;
30 | }
31 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Export/export.module.scss:
--------------------------------------------------------------------------------
1 | .iconColor{
2 | color: var(--primary);
3 | font-size: 25px;
4 | height: 25px;
5 | width: 25px;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/GameConfig/ConfigTab.tsx:
--------------------------------------------------------------------------------
1 | import TopbarTab from "@/pages/editor/Topbar/components/TopbarTab";
2 | import GameConfig from "@/pages/editor/Topbar/tabs/GameConfig/GameConfig";
3 |
4 | export default function ConfigTab(){
5 | return
6 |
7 | ;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/GameConfig/constants.ts:
--------------------------------------------------------------------------------
1 | export const textboxThemes = [
2 | {key: 'imss', text: 'IMSS'},
3 | {key: 'standard', text: 'Standard'}
4 | ];
5 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Help/HelpTab.tsx:
--------------------------------------------------------------------------------
1 | import TopbarTab from "@/pages/editor/Topbar/components/TopbarTab";
2 | import {TabItem} from "@/pages/editor/Topbar/components/TabItem";
3 | import {IconWithTextItem} from "@/pages/editor/Topbar/components/IconWithTextItem";
4 | import s from './help.module.scss';
5 | import { Question24Filled, Question24Regular, bundleIcon } from "@fluentui/react-icons";
6 | import { t } from "@lingui/macro";
7 |
8 | export function HelpTab() {
9 | const QuestionIcon = bundleIcon(Question24Filled, Question24Regular);
10 |
11 | return
12 |
13 | window.open("https://docs.openwebgal.com/", "_blank")}
15 | icon={} text={t`帮助`}
16 | />
17 |
18 | ;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Help/help.module.scss:
--------------------------------------------------------------------------------
1 | .helpIconColor{
2 | color: var(--primary);
3 | font-size: 25px;
4 | height: 25px;
5 | width: 25px;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Settings/constants.ts:
--------------------------------------------------------------------------------
1 | export const candidateFontSizes: number[] = [
2 | 8, 9, 10, 10.5, 11, 12, 14, 16, 18, 20, 24, 28
3 | ];
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/Settings/settingsTab.module.scss:
--------------------------------------------------------------------------------
1 | .iconColor {
2 | color: var(--primary);
3 | font-size: 25px;
4 | height: 25px;
5 | width: 25px;
6 | }
7 |
8 | .previewTips {
9 | color: var(--primary);
10 | text-align: left;
11 | }
12 |
13 | .tips {
14 | color: var(--primary);
15 | font-weight: bolder;
16 | font-size: smaller;
17 | text-align: left;
18 | height: 100%;
19 | width: 500px;
20 | background: var(--primary-5pct);
21 | padding: 4px;
22 | border-radius: 4px;
23 | }
24 |
25 | .fontFamilyInput {
26 | max-width: 320px;
27 | margin: 0 5px 0 0;
28 | }
29 |
30 | .prompt {
31 | padding-bottom: 8px;
32 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/Topbar/tabs/ViewConfig/viewTab.module.scss:
--------------------------------------------------------------------------------
1 | .iconColor{
2 | color: var(--primary);
3 | font-size: 25px;
4 | height: 25px;
5 | width: 25px;
6 | }
7 |
8 | .previewTips {
9 | color: var(--primary);
10 | text-align: left;
11 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/editor/editor.module.scss:
--------------------------------------------------------------------------------
1 | .editor {
2 | width: 100%;
3 | height: 100%;
4 | display: flex;
5 | flex-flow: column;
6 | }
7 |
8 | .container {
9 | flex: 1;
10 | display: flex;
11 | overflow: auto;
12 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorMainAria/TemplateEditorMainAria.tsx:
--------------------------------------------------------------------------------
1 | import styles from './templateEditorMainAria.module.scss';
2 | import { useTemplateEditorContext } from '@/store/useTemplateEditorStore';
3 | import TemplateEditorToolbar from './TemplateEditorToolbar';
4 | import TemplateGraphicalEditor from '../TemplateGraphicalEditor/TemplateGraphicalEditor';
5 | import TemplatePreview from './TemplatePreview';
6 | import TabsManager from './TabsManager';
7 | import TextEditor from '@/pages/templateEditor/TextEditor/TextEditor';
8 |
9 | export default function TemplateMainAria() {
10 |
11 | const currentTab = useTemplateEditorContext((state) => state.currentTab);
12 | const isClass = currentTab?.class && currentTab?.class?.length > 0;
13 | const isCodeMode = useTemplateEditorContext((state) => state.isCodeMode) || !isClass;
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | {
22 | currentTab?.path &&
23 | (isCodeMode
24 | ?
25 | :
26 | )
27 | }
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorMainAria/TemplateEditorToolbar.tsx:
--------------------------------------------------------------------------------
1 | import { useTemplateEditorContext } from '@/store/useTemplateEditorStore';
2 | import styles from './templateEditorToolbar.module.scss';
3 | import { CodeTextEditFilled, CodeTextEditRegular, SlideGridFilled, SlideGridRegular, bundleIcon } from '@fluentui/react-icons';
4 | import { FileCodeOne, ListView } from '@icon-park/react';
5 |
6 | export default function TemplateEditorToolbar() {
7 |
8 | const currentTab = useTemplateEditorContext((state) => state.currentTab);
9 | const isClass = currentTab?.class && currentTab?.class?.length > 0;
10 | const isCodeMode = useTemplateEditorContext((state) => state.isCodeMode);
11 | const updateIsCodeMode = useTemplateEditorContext((state) => state.updateIsCodeMode);
12 |
13 | return (
14 |
15 |
16 | {null}
17 |
18 |
19 | {
20 | isClass &&
21 | <>
22 |
updateIsCodeMode(true)}
25 | >
26 |
27 | 代码编辑器
28 |
29 |
updateIsCodeMode(false)}
32 | >
33 |
34 | 图形编辑器
35 |
36 | >
37 | }
38 |
39 |
40 | );
41 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorMainAria/templateEditorMainAria.module.scss:
--------------------------------------------------------------------------------
1 | .mainAria {
2 | flex: 1;
3 | width: 100%;
4 | height: 100%;
5 | display: flex;
6 | flex-direction: column;
7 | overflow: hidden;
8 | }
9 |
10 | .editor {
11 | flex: 1;
12 | border-left: var(--border-md);
13 | border-top: var(--border-md);
14 | border-radius: var(--radius-md) 0 0 0;
15 | display: flex;
16 | flex-direction: column;
17 | width: 100%;
18 | overflow: hidden;
19 | }
20 |
21 | .editorContent {
22 | flex: 1;
23 | width: 100%;
24 | height: 100%;
25 | overflow: hidden;
26 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorMainAria/templateEditorToolbar.module.scss:
--------------------------------------------------------------------------------
1 | .editorToolbar {
2 | width: 100%;
3 | border-top: var(--border-sm);
4 | display: flex;
5 | justify-content: space-between;
6 | align-items: center;
7 | padding: 0 4px;
8 | font-size: 12px;
9 | line-height: 24px;
10 | -webkit-user-select: none;
11 | user-select: none;
12 | }
13 |
14 | .toolbarButtonGroup {
15 | display: flex;
16 | flex-direction: row;
17 | gap: 4px;
18 | }
19 |
20 | .toolbarButton {
21 | padding: 0 6px;
22 | cursor: pointer;
23 | display: flex;
24 | flex-direction: row;
25 | align-items: center;
26 | gap: 4px;
27 | color: var(--text-sub);
28 | height: 24px;
29 | min-height: 24px;
30 |
31 | &:hover {
32 | background: var(--bg-button-hover);
33 | }
34 | }
35 |
36 | .toolbarButtonActive {
37 | background: var(--bg-button-hover);
38 | color: var(--primary);
39 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorMainAria/templatePreview.module.scss:
--------------------------------------------------------------------------------
1 | .preview {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | overflow: hidden;
6 | width: 100%;
7 | padding: 4px 4px 0 0;
8 | }
9 |
10 | .previewWindow {
11 | height: 100%;
12 | max-width: 100%;
13 | aspect-ratio: 16 / 9;
14 | border: var(--border-md);
15 | border-radius: var(--radius-md);
16 | }
17 |
18 | .divider {
19 | width: 100%;
20 | height: 4px;
21 | overflow: hidden;
22 | -webkit-user-select: none;
23 | user-select: none;
24 | display: table-cell;
25 | vertical-align: middle;
26 | text-align: center;
27 | padding: 0.75px 4px;
28 |
29 | & .dividerLine {
30 | visibility: hidden;
31 | height: 100%;
32 | width: 100%;
33 | background: var(--primary);
34 | border-radius: var(--radius-md);
35 | }
36 | }
37 |
38 | .divider:hover {
39 | cursor: ns-resize;
40 |
41 | & .dividerLine {
42 | visibility: visible;
43 | }
44 | }
45 |
46 | .dividerActive {
47 | cursor: ns-resize;
48 |
49 | & .dividerLine {
50 | visibility: visible;
51 | }
52 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorSidebar/ComponentTree/componentNode.module.scss:
--------------------------------------------------------------------------------
1 | .componentNode {
2 | width: 100%;
3 | border: var(--border-md);
4 | border-radius: var(--radius-md);
5 | height: 32px;
6 | min-height: 32px;
7 | display: flex;
8 | justify-content: space-between;
9 | align-items: center;
10 | padding: 0 8px;
11 | cursor: pointer;
12 | font-weight: 500;
13 |
14 | &:hover {
15 | background: var(--bg-button-hover);
16 | }
17 | }
18 |
19 | .classNode {
20 | width: 100%;
21 | height: 24px;
22 | display: flex;
23 | justify-content: start;
24 | align-items: center;
25 | padding: 0 16px;
26 | cursor: pointer;
27 | border-radius: var(--radius-md);
28 | font-size: 13.5px;
29 | font-weight: 400;
30 |
31 | &:hover {
32 | background: var(--bg-button-hover);
33 | }
34 | }
35 |
36 | .classNodeActive {
37 | background: var(--bg-button-hover);
38 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorSidebar/ComponentTree/componentTree.module.scss:
--------------------------------------------------------------------------------
1 | .componentTree {
2 | height: 100%;
3 | width: 100%;
4 | overflow: auto;
5 | display: flex;
6 | flex-direction: column;
7 | gap: 3px;
8 | padding: 4px;
9 | -webkit-user-select: none;
10 | user-select: none;
11 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateEditorSidebar/templateEditorSidebar.module.scss:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | display: flex;
3 | flex-direction: column;
4 | height: 100%;
5 | padding: 4px 0 4px 4px;
6 | }
7 |
8 | .toolbar {
9 | display: flex;
10 | justify-content: space-between;
11 | align-items: center;
12 | padding-bottom: 4px;
13 | gap: 8px;
14 | //margin-left: 4px;
15 | }
16 |
17 | .title {
18 | //flex: 1;
19 | flex-grow: 1;
20 | //text-align: right;
21 | padding-right: 12px;
22 | overflow: hidden;
23 | text-wrap: nowrap;
24 | text-overflow: ellipsis;
25 | display: inline-block;
26 | font-size: 16px;
27 | }
28 |
29 | .componentTree {
30 | width: 100%;
31 | background: var(--bg-card);
32 | border: var(--border-md);
33 | border-radius: var(--radius-md);
34 | }
35 |
36 | .divider {
37 | width: 100%;
38 | height: 4px;
39 | overflow: hidden;
40 | -webkit-user-select: none;
41 | user-select: none;
42 | display: table-cell;
43 | vertical-align: middle;
44 | text-align: center;
45 | padding: 0.75px 4px;
46 |
47 | & .dividerLine {
48 | visibility: hidden;
49 | }
50 | }
51 |
52 | .divider:hover {
53 | cursor: ns-resize;
54 |
55 | & .dividerLine {
56 | visibility: visible;
57 | height: 100%;
58 | width: 100%;
59 | background: var(--primary);
60 | border-radius: var(--radius-md);
61 | }
62 | }
63 |
64 | .dividerActive {
65 | cursor: ns-resize;
66 |
67 | & .dividerLine {
68 | visibility: visible;
69 | }
70 | }
71 |
72 | .assets {
73 | flex: 1;
74 | background: var(--bg-card);
75 | border: var(--border-md);
76 | border-radius: var(--radius-md);
77 | overflow: hidden;
78 | // min-height: 120px;
79 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor.module.scss:
--------------------------------------------------------------------------------
1 | .propertyEditor {
2 | display: flex;
3 | align-items: center;
4 | }
5 |
6 | .propertyEditorText{
7 | //font-weight: bold;
8 | //color: var(--primary);
9 | }
10 |
11 | .tableHeadText{
12 | font-weight: bold;
13 | //color: var(--primary);
14 | }
15 |
16 | .propertyEditorMain{
17 | padding: 5px 10px;
18 | }
19 |
20 | .propertyNameCell{
21 | display: flex;
22 | align-items: center;
23 | gap: 0.5rem;
24 | }
25 |
26 | .propertyDelete{
27 | visibility: hidden;
28 | }
29 |
30 | .helpLink{
31 | visibility: hidden;
32 | }
33 |
34 | .propertyNameCell:hover > .propertyDelete{
35 | visibility: visible;
36 | }
37 |
38 | .propertyNameCell:hover > .helpLink{
39 | visibility: visible;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGBackgroundPosition.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGBackgroundPosition(props: IPropertyEditorProps) {
8 |
9 | const selectId = useId();
10 |
11 | const isOther = !props.prop.propValue.match(/left|right|center|top|bottom/g);
12 | const initialValue = props.prop.propValue;
13 | const [value, setValue] = useState(initialValue);
14 | const [valueType, setValueType] = useState(isOther ? 'other' : initialValue);
15 |
16 |
17 | return
18 |
34 | {valueType === 'other' && <> setValue(data.value)}
37 | style={{margin: '0 8px'}}
38 | />
39 |
40 | >}
41 |
;
42 | }
43 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGBackgroundSize.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGBackgroundSize(props: IPropertyEditorProps) {
8 |
9 | const selectId = useId();
10 |
11 | const isOther = !props.prop.propValue.match(/cover|contain/g);
12 | const initialValue = props.prop.propValue;
13 | const [value, setValue] = useState(initialValue);
14 | const [valueType, setValueType] = useState(isOther ? 'other' : initialValue);
15 |
16 |
17 | return
18 |
31 | {valueType === 'other' && <> setValue(data.value)}
34 | style={{margin: '0 8px'}}
35 | />
36 |
37 | >}
38 |
;
39 | }
40 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGColor.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Popover, PopoverSurface, PopoverTrigger, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 | import WGColorPicker
7 | from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/components/WGColorPicker";
8 |
9 | export default function WGColor(props: IPropertyEditorProps) {
10 |
11 | const [color, setColor] = useState(props.prop.propValue);
12 | const [isShowColorPicker, setIsShowColorPicker] = useState(false);
13 |
14 | const submitColor = () => {
15 | props.prop.propValue = color;
16 | setIsShowColorPicker(false);
17 | props.onSubmit();
18 | };
19 |
20 |
21 | return
22 |
23 |
24 | {color}
25 |
26 |
{
27 | // 恢复编辑值为默认
28 | setColor(props.prop.propValue);
29 | setIsShowColorPicker(data.open);
30 | }}>
31 |
32 |
33 |
34 |
35 | setColor(newValue)}/>
36 |
37 |
38 |
39 |
;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGCommonEditor.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGCommonNumberEditor(props: IPropertyEditorProps) {
8 | const initialValue = props.prop.propValue;
9 | const [value, setValue] = useState(initialValue);
10 |
11 | const submit = () => {
12 | props.prop.propValue = `${value}`;
13 | props.onSubmit();
14 | };
15 |
16 | return
17 | setValue(data.value)}
20 | style={{marginRight:10}}
21 | />
22 |
23 |
;
24 | }
25 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGCommonLengthEditor.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGCommonLengthEditor(props: IPropertyEditorProps) {
8 | // 使用正则表达式来分离数字和单位
9 | const initialValue = props.prop.propValue.match(/(\d+)(\D+)/) || ['','100','px']; // 默认为100px
10 | const [width, setWidth] = useState(initialValue[1]);
11 | const [unit, setUnit] = useState(initialValue[2]);
12 |
13 | const submit = () => {
14 | // 将宽度和单位拼接为一个字符串,不使用空格分隔
15 | props.prop.propValue = `${width}${unit}`;
16 | props.onSubmit();
17 | };
18 |
19 | return
20 | setWidth(data.value)}
24 | style={{marginRight:10}}
25 | />
26 |
35 |
36 |
;
37 | }
38 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGCommonNumberEditor.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGCommonNumberEditor(props: IPropertyEditorProps) {
8 | const initialValue = props.prop.propValue;
9 | const [number, setNumber] = useState(initialValue);
10 |
11 | const submit = () => {
12 | props.prop.propValue = `${number}`;
13 | props.onSubmit();
14 | };
15 |
16 | return
17 | setNumber(data.value)}
21 | style={{marginRight:10}}
22 | />
23 |
24 |
;
25 | }
26 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGCursor.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 |
6 | export default function WGCursor(props: IPropertyEditorProps) {
7 |
8 | const selectId = useId();
9 |
10 | return
11 |
19 |
;
20 | }
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGCustomProperty.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Button, Input, Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 | import {useState} from "react";
6 |
7 | export default function WGCustomProperty(props: IPropertyEditorProps) {
8 |
9 | const [propertyValue, setPropertyValue] = useState(props.prop.propValue);
10 |
11 | const submit = () => {
12 | props.prop.propValue = propertyValue;
13 | props.onSubmit();
14 | };
15 |
16 | return
17 | {
18 | setPropertyValue(data.value);
19 | }}/>
20 |
21 |
;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGFontWeight.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 |
6 | export default function WGFontWeight(props: IPropertyEditorProps) {
7 |
8 | const selectId = useId();
9 |
10 | return
11 |
18 |
;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGPosition.tsx:
--------------------------------------------------------------------------------
1 | import {IPropertyEditorProps} from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable";
2 | import {Select, useId} from "@fluentui/react-components";
3 | import s from '../propertyEditor.module.scss';
4 | import {t} from "@lingui/macro";
5 |
6 | export default function WGPosition(props: IPropertyEditorProps) {
7 |
8 | const selectId = useId();
9 |
10 | return
11 |
19 |
;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGText.tsx:
--------------------------------------------------------------------------------
1 | import { IPropertyEditorProps } from '@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable';
2 | import { Button, Input, Select, useId } from '@fluentui/react-components';
3 | import s from '../propertyEditor.module.scss';
4 | import { t } from '@lingui/macro';
5 | import { useState } from 'react';
6 |
7 | export default function WGTextAlignEditor(props: IPropertyEditorProps) {
8 | const initialValue = props.prop.propValue;
9 | const [align, setAlign] = useState(initialValue);
10 |
11 | const submit = () => {
12 | props.prop.propValue = align;
13 | props.onSubmit();
14 | };
15 |
16 | return (
17 |
18 |
24 |
25 |
26 | );
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/propertyEditor/WGTextShadow.tsx:
--------------------------------------------------------------------------------
1 | import {useState} from 'react';
2 | import {Button, Input, Select} from '@fluentui/react-components';
3 | import {t} from '@lingui/macro';
4 | import s from '../propertyEditor.module.scss';
5 | import {IPropertyEditorProps} from '@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor/editorTable';
6 |
7 | export default function WGTextShadowEditor(props: IPropertyEditorProps) {
8 | const initialValue = props.prop.propValue || '0px 0px 0px rgba(0,0,0,0)'; // 默认值为无阴影
9 | const [hOffset, setHOffset] = useState((initialValue.match(/(-?\d+)px/) || [, '0'])[1]);
10 | const [vOffset, setVOffset] = useState((initialValue.match(/-?\d+px\s+(-?\d+)px/) || [, '0'])[1]);
11 | const [blurRadius, setBlurRadius] = useState((initialValue.match(/(-?\d+)px\s+rgba/) || [, '0'])[1]);
12 | const [color, setColor] = useState((initialValue.match(/rgba\(.+\)/) || ['rgba(0,0,0,0)'])[0]);
13 |
14 | const submit = () => {
15 | props.prop.propValue = `${hOffset}px ${vOffset}px ${blurRadius}px ${color}`;
16 | props.onSubmit();
17 | };
18 |
19 | return (
20 |
21 | setHOffset(data.value)}
22 | style={{marginRight: 10, width: 70}}/>
23 | setVOffset(data.value)}
24 | style={{marginRight: 10, width: 70}}/>
25 | setBlurRadius(data.value)}
29 | style={{marginRight: 10, width: 70}}
30 | />
31 | setColor(data.value)} style={{marginRight: 10}}/>
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/templateGraphicalEditor.module.scss:
--------------------------------------------------------------------------------
1 | .title{
2 | font-weight: bold;
3 | color: var(--primary);
4 | padding: 10px 0 10px 15px;
5 | }
6 |
7 | .templateGraphicalEditor{
8 | padding: 0 5px 0 5px;
9 | overflow: auto;
10 | height: 100%;
11 | background: var(--bg-card);
12 | }
13 |
14 | .addStateWrapper{
15 | padding-left: 10px;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/utils/formCss.ts:
--------------------------------------------------------------------------------
1 | import {ICssExtractResult} from "@/pages/templateEditor/TemplateGraphicalEditor/utils/extractCss";
2 |
3 | export function formCss(extractedCss: ICssExtractResult): string {
4 | let commonPropsStr = '';
5 | let propsWithStateStr = '';
6 |
7 | for (const prop of extractedCss.commonProps) {
8 | commonPropsStr = commonPropsStr + `${prop.propName}: ${prop.propValue};\n`;
9 | }
10 |
11 | for (const state of extractedCss.propsWithState) {
12 | let currentStateStr = '';
13 | currentStateStr = currentStateStr + `&:${state.state} {\n`;
14 |
15 | for (const prop of state.props) {
16 | currentStateStr = currentStateStr + `${prop.propName}: ${prop.propValue};\n`;
17 | }
18 |
19 | currentStateStr = currentStateStr + `}\n`;
20 |
21 | propsWithStateStr = propsWithStateStr + currentStateStr;
22 | }
23 |
24 |
25 | return `${commonPropsStr}\n${propsWithStateStr}`;
26 | }
27 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/utils/getMdnLink.ts:
--------------------------------------------------------------------------------
1 | import {i18n} from "@lingui/core";
2 |
3 | export function getMdnLink(prop:string){
4 |
5 | let lang = i18n.locale;
6 | if(lang === 'zhCn')
7 | lang = 'zh-CN';
8 |
9 | return `https://developer.mozilla.org/${lang}/docs/Web/CSS/${prop}`;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/utils/updateScssFile.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {WebgalParser} from "@/pages/editor/GraphicalEditor/parser";
3 | import {api} from "@/api";
4 |
5 | export async function updateScssFile(filePath:string, className:string, classCss:string){
6 | const orgCssFileResp = await axios.get(filePath);
7 | const orgCssText = orgCssFileResp.data;
8 |
9 | const parsed = WebgalParser.parseScssToWebgalStyleObj(orgCssText);
10 |
11 | parsed.classNameStyles[className] = classCss;
12 |
13 | const newStr = formScssFromWebGALStyleObj(parsed);
14 |
15 | await api.assetsControllerEditTextFile({path:filePath,textFile:newStr});
16 | }
17 |
18 | function formScssFromWebGALStyleObj(styleObj:ReturnType){
19 | let styleSheet = '';
20 |
21 | const keys = Object.keys(styleObj.classNameStyles);
22 |
23 | for(const key of keys){
24 | const classStyleText = styleObj.classNameStyles[key];
25 | styleSheet = styleSheet + `.${key} {\n${classStyleText}}\n\n`;
26 | }
27 |
28 | styleSheet = styleSheet + styleObj.others +'\n';
29 |
30 | return styleSheet;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/withStateEditor/SingleStateEditor.tsx:
--------------------------------------------------------------------------------
1 | import {IWebgalCssProp} from "@/pages/templateEditor/TemplateGraphicalEditor/utils/extractCss";
2 | import {t} from "@lingui/macro";
3 | import WebgalClassEditor from "@/pages/templateEditor/TemplateGraphicalEditor/WebgalClassEditor";
4 | import s from './singleStateEditor.module.scss';
5 |
6 | export function getStateNameMap(){
7 | return {
8 | 'hover': t`鼠标悬浮样式`,
9 | 'active': t`鼠标按下样式`
10 | };
11 | }
12 |
13 | export default function SingleStateEditor(props: { stateName: string, props: IWebgalCssProp[], onSubmit: () => void }) {
14 | const stateNameMap = getStateNameMap();
15 |
16 | // @ts-ignore
17 | const thisStateName: string = stateNameMap?.[props.stateName] ?? '';
18 |
19 | return
20 |
21 | {thisStateName}
22 |
23 |
24 |
25 |
26 |
;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/withStateEditor/index.tsx:
--------------------------------------------------------------------------------
1 | import {IWebgalPropsWithState} from "@/pages/templateEditor/TemplateGraphicalEditor/utils/extractCss";
2 | import SingleStateEditor from "@/pages/templateEditor/TemplateGraphicalEditor/withStateEditor/SingleStateEditor";
3 |
4 | export interface IPropWithStateProps {
5 | propWithState: IWebgalPropsWithState[],
6 | onSubmit: () => void;
7 | }
8 |
9 | export default function WithStateEditor(props: IPropWithStateProps) {
10 | const editors = props.propWithState.map(e => );
15 | return <>
16 | {editors}
17 | >;
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TemplateGraphicalEditor/withStateEditor/singleStateEditor.module.scss:
--------------------------------------------------------------------------------
1 | .stateName{
2 | font-weight: bold;
3 | color: var(--primary);
4 | padding: 10px 0 10px 15px;
5 | }
6 |
7 | .stateWrapper{
8 | //background: var(--primary-5pct);
9 | padding: 0 0 10px 0;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/TextEditor/textEditor.module.scss:
--------------------------------------------------------------------------------
1 | .textEditor_main{
2 | height: 100%;
3 | width: 100%;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/origine2/src/pages/templateEditor/templateEditor.module.scss:
--------------------------------------------------------------------------------
1 | .editor {
2 | width: 100vw;
3 | height: 100vh;
4 | display: flex;
5 | }
6 |
7 | .divider {
8 | width: 4px;
9 | height: 100%;
10 | overflow: hidden;
11 | -webkit-user-select: none;
12 | user-select: none;
13 | display: flex;
14 | align-items: center;
15 | cursor: ew-resize;
16 | text-align: center;
17 | padding: 44px 0.75px 8px 0.75px;
18 |
19 | & .dividerLine {
20 | visibility: hidden;
21 | height: 100%;
22 | width: 100%;
23 | background: var(--primary);
24 | border-radius: var(--radius-md);
25 | }
26 | }
27 |
28 | .divider:hover {
29 |
30 | & .dividerLine {
31 | visibility: visible;
32 | }
33 | }
34 |
35 | .dividerActive {
36 |
37 | & .dividerLine {
38 | visibility: visible;
39 | }
40 | }
--------------------------------------------------------------------------------
/packages/origine2/src/primereact.scss:
--------------------------------------------------------------------------------
1 | .p-splitter {
2 | border: none;
3 | }
4 |
5 | .p-splitter-panel {
6 | //height: 100%;
7 | //overflow: auto;
8 | }
9 |
10 | .p-splitter-panel {
11 | flex-basis: initial !important;
12 | flex-grow: 0;
13 | //max-width: 100%;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/origine2/src/runtime/WG_ORIGINE_RUNTIME.ts:
--------------------------------------------------------------------------------
1 | export const WG_ORIGINE_RUNTIME = {
2 | textEditor:{
3 | isInitWasm:false
4 | }
5 | };
6 | // 当前要发给 LSP 的场景名称
7 | export const lspSceneName = {value: ""};
8 |
9 | class EditorLineHolder{
10 | private mapSceneUrlToSentence = new Map();
11 | public recordSceneEdittingLine(sceneUrl:string,lineNumber:number){
12 | this.mapSceneUrlToSentence.set(sceneUrl,lineNumber);
13 | // console.log(this.mapSceneUrlToSentence);
14 | }
15 | public getSceneLine(sceneUrl:string){
16 | return this.mapSceneUrlToSentence.get(sceneUrl) ??0;
17 | }
18 | }
19 |
20 | export const editorLineHolder = new EditorLineHolder();
21 |
--------------------------------------------------------------------------------
/packages/origine2/src/types/debugProtocol.ts:
--------------------------------------------------------------------------------
1 | import {IStageState} from "@/types/stageInterface";
2 |
3 | export enum DebugCommand {
4 | // 跳转
5 | JUMP,
6 | // 同步自客户端
7 | SYNCFC,
8 | // 同步自编辑器
9 | SYNCFE,
10 | // 执行指令
11 | EXE_COMMAND,
12 | // 重新拉取模板样式文件
13 | REFETCH_TEMPLATE_FILES,
14 | // 设置指定控件是否可见
15 | SET_COMPONENT_VISIBILITY,
16 | // 临时场景
17 | TEMP_SCENE,
18 | // 字体优化
19 | FONT_OPTIMIZATION,
20 | }
21 |
22 | export interface IDebugMessage {
23 | event: string;
24 | data: {
25 | command: DebugCommand;
26 | sceneMsg: {
27 | sentence: number;
28 | scene: string;
29 | };
30 | message: string;
31 | stageSyncMsg: IStageState;
32 | };
33 | }
34 |
35 | export interface IComponentsVisibility {
36 | showStarter: boolean; // 是否显示初始界面(用于使得bgm可以播放)
37 | showTitle: boolean; // 是否显示标题界面
38 | showMenuPanel: boolean; // 是否显示Menu界面
39 | showTextBox: boolean;
40 | showControls: boolean;
41 | controlsVisibility: boolean;
42 | showBacklog: boolean;
43 | showExtra: boolean;
44 | showGlobalDialog: boolean;
45 | showPanicOverlay: boolean;
46 | isEnterGame: boolean;
47 | isShowLogo: boolean;
48 | }
49 |
50 | export interface IComponentVisibilityCommand {
51 | component: keyof IComponentsVisibility;
52 | visibility: boolean;
53 | }
54 |
--------------------------------------------------------------------------------
/packages/origine2/src/types/editor.ts:
--------------------------------------------------------------------------------
1 | import { IPage } from "@/hooks/useHashRoute";
2 | import {IGameEditorState} from "@/types/gameEditor";
3 |
4 | export interface IEditorState {
5 | page: IPage,
6 | subPage: string,
7 | expand: number,
8 | language: 'zhCn' | 'en' | 'ja',
9 | editorFontFamily: string,
10 | editorFontSize: number,
11 | isAutoHideToolbar: boolean, // 是否自动隐藏工具栏
12 | isEnableLivePreview: boolean, // 是否开启实时预览
13 | isAutoWarp: boolean, // 是否开启自动换行
14 | isUseExpFastSync: boolean,
15 | isUseFontOptimization: boolean,
16 | ignoreVersion: string, // 忽略版本
17 | }
18 |
19 | export interface IEditorAction {
20 | updatePage: (editor: IEditorState['page']) => void,
21 | updateSubPage: (subPage: IEditorState['subPage']) => void,
22 | updateExpand: (index: IEditorState['expand']) => void,
23 | updateLanguage: (language: IEditorState['language']) => void,
24 | updateEditorFontFamily: (editorFontFamily: IEditorState['editorFontFamily']) => void,
25 | updateEditorFontSize: (editorFontSize: IEditorState['editorFontSize']) => void,
26 | updateIisAutoHideToolbar: (isAutoHideToolbar: IEditorState['isAutoHideToolbar']) => void,
27 | updateIsEnableLivePreview: (isEnableLivePreview: IEditorState['isEnableLivePreview']) => void,
28 | updateIsAutoWarp: (isAutoWarp: IEditorState['isAutoWarp']) => void,
29 | updateIsUseExpFastSync: (isUseExpFastSync: IGameEditorState['isShowDebugger']) => void,
30 | updateIsUseFontOptimization: (isUseFontOptimization: IEditorState['isUseFontOptimization']) => void,
31 | updateIgnoreVersion: (ignoreVersion: IEditorState['ignoreVersion']) => void,
32 | }
33 |
--------------------------------------------------------------------------------
/packages/origine2/src/types/gameEditor.ts:
--------------------------------------------------------------------------------
1 | export interface ITag {
2 | name: string,
3 | type: 'asset' | 'scene',
4 | path: string,
5 | }
6 | export type IGameEditorSidebarTabs = 'asset' | 'scene';
7 | export type IGameEditorTopbarTabs = 'config' | 'view' | 'settings' | 'help' | 'export' | 'addSentence';
8 |
9 | export interface IGameEditorState {
10 | tags: ITag[],
11 | currentTag: ITag | null,
12 | currentSidebarTab: IGameEditorSidebarTabs,
13 | currentTopbarTab: IGameEditorTopbarTabs | null,
14 | isShowSidebar: boolean,
15 | isCodeMode: boolean,
16 | isShowDebugger: boolean,
17 | }
18 |
19 | export interface IGameEditorAction {
20 | updateTags: (tags: IGameEditorState['tags']) => void,
21 | addTag: (tag: ITag) => void,
22 | removeTag: (tag: ITag) => void,
23 | updateCurrentTag: (currentTag: IGameEditorState['currentTag']) => void,
24 | updateCurrentSidebarTab: (sidebarTab: IGameEditorState['currentSidebarTab']) => void,
25 | updateCurrentTopbarTab: (currentTopbarTab: IGameEditorState['currentTopbarTab']) => void,
26 | updateIsShowSidebar: (isShowSidebar: IGameEditorState['isShowSidebar']) => void,
27 | updateIsCodeMode: (isCodeMode: IGameEditorState['isCodeMode']) => void,
28 | updateIsShowDebugger: (isShowDebugger: IGameEditorState['isShowDebugger']) => void,
29 | }
--------------------------------------------------------------------------------
/packages/origine2/src/types/templateEditor.ts:
--------------------------------------------------------------------------------
1 | export interface ITab {
2 | name: string,
3 | path: string,
4 | class?: string,
5 | }
6 |
7 | export interface ITemplateEditorState {
8 | tabs: ITab[],
9 | currentTab: ITab | null,
10 | expandNode: string[],
11 | isCodeMode: boolean,
12 | isShowDebugger: boolean,
13 | sidebarWidth: number,
14 | componentTreeHeight: number,
15 | previewHeight: number,
16 | }
17 |
18 | export interface ITemplateEditorAction {
19 | updateTabs: (tabs: ITemplateEditorState['tabs']) => void,
20 | updateCurrentTab: (currentTab: ITemplateEditorState['currentTab']) => void,
21 | updateExpandNode: (expandNode: ITemplateEditorState['expandNode']) => void,
22 | updateIsCodeMode: (isCodeMode: ITemplateEditorState['isCodeMode']) => void,
23 | updateIsShowDebugger: (isShowDebugger: ITemplateEditorState['isShowDebugger']) => void,
24 | updateSidebarWidth: (sidebarWidth: ITemplateEditorState['sidebarWidth']) => void,
25 | updateComponentTreeHeight: (componentTreeheight: ITemplateEditorState['componentTreeHeight']) => void,
26 | updatePreviewHeight: (previewHeight: ITemplateEditorState['previewHeight']) => void,
27 | }
--------------------------------------------------------------------------------
/packages/origine2/src/utils/createSelectors.ts:
--------------------------------------------------------------------------------
1 | import { StoreApi, UseBoundStore } from 'zustand';
2 | import { useShallow } from 'zustand/react/shallow';
3 |
4 | type WithSelectors = S extends { getState: () => infer T }
5 | ? S & { use: { [K in keyof T]: () => T[K] } }
6 | : never
7 |
8 | const createSelectors = >>(
9 | _store: S,
10 | ) => {
11 | let store = _store as WithSelectors;
12 | store.use = {};
13 | for (let k of Object.keys(store.getState())) {
14 | (store.use as any)[k] = () => store(useShallow((s) => s[k as keyof typeof s]));
15 | }
16 |
17 | return store;
18 | };
19 |
20 | export default createSelectors;
--------------------------------------------------------------------------------
/packages/origine2/src/utils/eventBus.ts:
--------------------------------------------------------------------------------
1 | import mitt from "mitt";
2 | export const eventBus = mitt();
3 |
--------------------------------------------------------------------------------
/packages/origine2/src/utils/getWsUrl.ts:
--------------------------------------------------------------------------------
1 | export function getWsUrl(route:string):string{
2 | const loc: string = window.location.hostname;
3 | const protocol: string = window.location.protocol;
4 | const port: string = window.location.port; // 获取端口号
5 |
6 | // 默认情况下,不需要在URL中明确指定标准HTTP(80)和HTTPS(443)端口
7 | let defaultPort = '';
8 | if (port && port !== '80' && port !== '443') {
9 | // 如果存在非标准端口号,将其包含在URL中
10 | defaultPort = `:${port}`;
11 | }
12 |
13 | if (protocol !== 'http:' && protocol !== 'https:') {
14 | return '';
15 | }
16 |
17 | // 根据当前协议构建WebSocket URL,并包括端口号(如果有)
18 | let wsUrl = `ws://${loc}${defaultPort}/${route}`;
19 | if (protocol === 'https:') {
20 | wsUrl = `wss://${loc}${defaultPort}/${route}`;
21 | }
22 |
23 | return wsUrl;
24 | }
25 |
--------------------------------------------------------------------------------
/packages/origine2/src/utils/initMonaco.ts:
--------------------------------------------------------------------------------
1 | import {logger} from "@/utils/logger";
2 | import {loader} from "@monaco-editor/react";
3 | import * as monaco from "monaco-editor";
4 | import {configureMonacoWorkers, runClient} from "@/webgalscript/lsp";
5 |
6 | export function initMonaco(){
7 |
8 | logger.info('Welcome to WebGAL live editor!');
9 | configureMonacoWorkers().then();
10 | runClient().then(() => console.log(': LSP client started'));
11 |
12 | loader.config({ monaco });
13 | loader.init();
14 | }
15 |
--------------------------------------------------------------------------------
/packages/origine2/src/utils/localStorageRename.ts:
--------------------------------------------------------------------------------
1 | export const localStorageRename = (oldName: string, newName: string) => {
2 | const value = localStorage.getItem(oldName);
3 | if (value) {
4 | localStorage.setItem(newName, value );
5 | }
6 | localStorage.removeItem(oldName);
7 | };
--------------------------------------------------------------------------------
/packages/origine2/src/utils/logger.ts:
--------------------------------------------------------------------------------
1 | import Cloudlog from 'cloudlogjs';
2 |
3 | /**
4 | * 日志打印工具
5 | */
6 | export const logger = new Cloudlog();
7 |
--------------------------------------------------------------------------------
/packages/origine2/src/utils/normalizeFileName.ts:
--------------------------------------------------------------------------------
1 | const normalizeFileName = (filename: string): string => {
2 | const normalized = filename.trim().replace(/[<>《》::“”'"??!!/\\|@#%&\s]+/g, '-').trim();
3 | return normalized.replace(/^_+|_+$/g, '');
4 | };
5 |
6 | export default normalizeFileName;
7 |
--------------------------------------------------------------------------------
/packages/origine2/src/utils/parser.ts:
--------------------------------------------------------------------------------
1 | import SceneParser from "webgal-parser";
2 | import { IScene } from "webgal-parser/src/interface/sceneInterface";
3 | import { logger } from "./logger";
4 | import { ADD_NEXT_ARG_LIST, SCRIPT_CONFIG } from "webgal-parser/src/config/scriptConfig";
5 |
6 | const parser = new SceneParser((assetList) => {
7 | }, (fileName, assetType) => {
8 | return fileName;
9 | }, ADD_NEXT_ARG_LIST, [...SCRIPT_CONFIG]);
10 |
11 | /**
12 | * 场景解析器
13 | * @param rawScene 原始场景
14 | * @param sceneName 场景名称
15 | * @param sceneUrl 场景url
16 | * @return {IScene} 解析后的场景
17 | */
18 | export const sceneParser = (rawScene: string, sceneName: string, sceneUrl: string): IScene => {
19 | const parsedScene = parser.parse(rawScene, sceneName, sceneUrl);
20 | logger.info(`解析场景:${sceneName},数据为:`, parsedScene);
21 | return parsedScene;
22 | };
23 |
--------------------------------------------------------------------------------
/packages/origine2/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/packages/origine2/src/webgalscript/extension.ts:
--------------------------------------------------------------------------------
1 | import { registerExtension, IExtensionManifest, ExtensionHostKind } from 'vscode/extensions';
2 |
3 | const manifest: IExtensionManifest = {
4 | name: 'webgal',
5 | version: '3.16.2',
6 | publisher: 'webgal',
7 | engines: {
8 | vscode: '*',
9 | },
10 | contributes: {
11 | themes: [
12 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13 | // @ts-ignore id type problem
14 | {
15 | label: 'WebGAL White',
16 | path: '/white.json',
17 | uiTheme: 'vs',
18 | },
19 | ],
20 | grammars: [
21 | {
22 | language: 'webgal',
23 | scopeName: 'source.webgal',
24 | path: '/hl.json',
25 | injectTo: ['source.txt'],
26 | },
27 | ],
28 | },
29 | };
30 |
31 | const { registerFileUrl } = registerExtension(manifest, ExtensionHostKind.LocalProcess);
32 |
33 | registerFileUrl('/white.json', new URL('../config/themes/monokai-light-vs.json', import.meta.url).href);
34 | registerFileUrl('/hl.json', new URL('../config/highlighting/hl.json', import.meta.url).href);
35 |
--------------------------------------------------------------------------------
/packages/origine2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": false,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "baseUrl": "./",
19 | "paths": {
20 | "@/*": ["./src/*"]
21 | }
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/packages/origine2/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/origine2/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 | import { resolve } from 'path';
4 | import { env } from 'process';
5 | import { lingui } from "@lingui/vite-plugin";
6 | import Info from 'unplugin-info/vite';
7 |
8 | let WEBGAL_PORT = 3000; // default port
9 | if (env.WEBGAL_PORT) {
10 | WEBGAL_PORT = Number.parseInt(env.WEBGAL_PORT, 10);
11 | }
12 |
13 | // https://vitejs.dev/config/
14 | export default defineConfig({
15 | plugins: [
16 | react({
17 | babel: {
18 | plugins: ["macros"],
19 | },
20 | }),
21 | lingui(),
22 | Info()
23 | ],
24 | resolve: {
25 | alias: {
26 | '@': resolve('src'),
27 | },
28 | },
29 | server: {
30 | port: WEBGAL_PORT,
31 | proxy: {
32 | // 接口地址代理
33 | '/api': {
34 | target: `http://127.0.0.1:${WEBGAL_PORT + 1}`, // 接口的域名
35 | secure: true, // 如果是https接口,需要配置这个参数
36 | changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
37 | ws:true,
38 | },
39 | '/games': {
40 | target: `http://127.0.0.1:${WEBGAL_PORT + 1}`, // 接口的域名
41 | secure: true, // 如果是https接口,需要配置这个参数
42 | changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
43 | },
44 | '/templates': {
45 | target: `http://127.0.0.1:${WEBGAL_PORT + 1}`, // 接口的域名
46 | secure: true, // 如果是https接口,需要配置这个参数
47 | changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
48 | },
49 | '/template-preview': {
50 | target: `http://127.0.0.1:${WEBGAL_PORT + 1}`, // 接口的域名
51 | secure: true, // 如果是https接口,需要配置这个参数
52 | changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
53 | },
54 | },
55 | },
56 | });
57 |
--------------------------------------------------------------------------------
/packages/terre-electron/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/packages/terre-electron/README.md:
--------------------------------------------------------------------------------
1 | # WebGAL Electron Project
2 |
3 | Add electron support for WebGAL and WebGAL Terre.
4 |
5 | ### Optional: Add mirror of electron-builder
6 |
7 | ```
8 | export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/
9 | export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
10 | ```
11 |
--------------------------------------------------------------------------------
/packages/terre-electron/main.js:
--------------------------------------------------------------------------------
1 | global['isElectron'] = true;
2 | const { app, BrowserWindow, globalShortcut, Menu } = require('electron');
3 | const log = require('electron-log');
4 | require('./dist/main')
5 |
6 | /**
7 | * 关闭默认菜单栏
8 | */
9 | Menu.setApplicationMenu(null);
10 |
11 | /**
12 | * 在应用启动后打开窗口
13 | */
14 | app.whenReady().then(() => {
15 | createWindow()
16 | // 适配 Mac OS
17 | app.on('activate', () => {
18 | if (BrowserWindow.getAllWindows().length === 0) createWindow()
19 | })
20 | })
21 |
22 | /**
23 | * 打开窗口
24 | */
25 | const createWindow = () => {
26 | const win = new BrowserWindow({
27 | width: 1600,
28 | height: 900
29 | })
30 |
31 | win.loadURL('http://localhost:3001').then(r => console.log(r));
32 |
33 | // 注册快捷键 Ctrl + F12 切换开发者工具
34 | globalShortcut.register("Ctrl+F12", () => {
35 | win.isFocused() && win.webContents.toggleDevTools();
36 | });
37 |
38 | // 捕获渲染进程控制台信息
39 | win.webContents.on('console-message', (event, level, message) => {
40 | const logLevels = {
41 | '[silly]': log.silly,
42 | '[debug]': log.debug,
43 | '[verbose]': log.verbose,
44 | '[warn]': log.warn,
45 | '[error]': log.error,
46 | };
47 |
48 | const logMessage = (message) => {
49 | const level = Object.keys(logLevels).find(key => message.toLowerCase().includes(key));
50 | const selectedLevel = level ? logLevels[level] : log.info;
51 | selectedLevel(message);
52 | }
53 |
54 | logMessage(message);
55 | });
56 | }
57 |
58 | /**
59 | * 在关闭所有窗口时退出应用
60 | */
61 | app.on('window-all-closed', () => {
62 | if (process.platform !== 'darwin') app.quit()
63 | })
--------------------------------------------------------------------------------
/packages/terre-electron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webgal-terre-electron",
3 | "version": "1.0.0",
4 | "description": "WebGAL Terre Electron",
5 | "main": "main.js",
6 | "repository": "https://github.com/WebGAL-Technical-Committee/WebGAL-Electron-Project.git",
7 | "author": "Mahiru ",
8 | "license": "MPL-2.0",
9 | "scripts": {
10 | "start": "electron .",
11 | "build": "electron-builder",
12 | "build-universal": "electron-builder --universal",
13 | "build:arm64": "electron-builder --arm64"
14 | },
15 | "devDependencies": {
16 | "electron": "^29.0.0",
17 | "electron-builder": "^24.12.0"
18 | },
19 | "build": {
20 | "productName": "WebGAL Terre",
21 | "appId": "com.openwebgal.webgal-terre",
22 | "copyright": "webgal",
23 | "directories": {
24 | "output": "build"
25 | },
26 | "asar": false,
27 | "win": {
28 | "icon": "public/icon.ico",
29 | "target": [
30 | "dir"
31 | ]
32 | },
33 | "linux": {
34 | "icon": "public/icon.ico",
35 | "target": [
36 | "dir"
37 | ]
38 | },
39 | "mac": {
40 | "icon": "public/icon-mac.ico"
41 | }
42 | },
43 | "dependencies": {
44 | "electron-log": "^5.1.5"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/terre-electron/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre-electron/public/.gitkeep
--------------------------------------------------------------------------------
/packages/terre-electron/public/icon-mac.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre-electron/public/icon-mac.ico
--------------------------------------------------------------------------------
/packages/terre-electron/public/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre-electron/public/icon.ico
--------------------------------------------------------------------------------
/packages/terre2/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir : __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | 'linebreak-style': ["error", "unix"]
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/packages/terre2/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | pnpm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
36 |
37 | /assets/templates/WebGAL_Electron_Template
38 |
39 | Exported_Games/*
40 | public/games/*
41 | public/templates/*
42 |
43 | /assets/templates/Derivative_Engine/*
44 | !/assets/templates/Derivative_Engine/.gitkeep
45 | !public/templates/WebGAL Black
46 | /assets/templates/WebGAL_Template/assets
47 | /assets/templates/WebGAL_Template/index.html
48 | /assets/templates/WebGAL_Template/webgal-serviceworker.js
49 |
--------------------------------------------------------------------------------
/packages/terre2/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/packages/terre2/README.md:
--------------------------------------------------------------------------------
1 | ## WebGAL Terre 2
2 |
3 | 使用 Nest.js 构建的全新 WebGAL 编辑器后端。
4 |
5 | ### 目录说明
6 |
7 | ```text
8 | 需要在打包后保留的:
9 | assets: 用于存储一些上传的临时文件、WebGAL 的相关模板
10 | Exported_Games: 用于存储保存的游戏
11 | public: 用于存储编辑器前端网页、具体的游戏文件
12 | README.md: 自述文档
13 | LICENSE: 许可证
14 |
15 | 开发文件:
16 | 其他文件目录中的文件
17 | ```
18 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/Derivative_Engine/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/Derivative_Engine/.gitkeep
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Default_Template/Stage/Choose/choose.scss:
--------------------------------------------------------------------------------
1 | .Choose_Main {
2 | position: absolute;
3 | width: 100%;
4 | height: 100%;
5 | display: flex;
6 | flex-flow: column;
7 | justify-content: center;
8 | align-items: center;
9 | z-index: 13;
10 | background: rgba(0, 0, 0, 0.05);
11 | }
12 |
13 | .Choose_item {
14 | font-family: "WebgalUI", serif;
15 | cursor: pointer;
16 | min-width: 50%;
17 | padding: 0.25em 1em 0.25em 1em;
18 | font-size: 265%;
19 | color: #8E354A;
20 | text-align: center;
21 | border-radius: 4px;
22 | border: 3px solid rgba(0, 0, 0, 0);
23 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.25);
24 | background: rgba(255, 255, 255, 0.65);
25 | margin: 0.25em 0 0.25em 0;
26 | transition: background-color 0.5s, border 0.5s, font-weight 0.5s, box-shadow 0.5s;
27 |
28 | &:hover {
29 | background: rgba(255, 255, 255, 0.9);
30 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.35);
31 | border: 3px solid #8E354A;
32 | }
33 | }
34 |
35 | .Choose_item_disabled {
36 | font-family: "WebgalUI", serif;
37 | cursor: not-allowed;
38 | min-width: 50%;
39 | padding: 0.25em 1em 0.25em 1em;
40 | font-size: 265%;
41 | color: rgba(142, 53, 74, 0.5);
42 | text-align: center;
43 | border-radius: 4px;
44 | border: 3px solid rgba(0, 0, 0, 0);
45 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.25);
46 | background: rgba(255, 255, 255, 0.5);
47 | margin: 0.25em 0 0.25em 0;
48 | transition: background-color 0.5s, border 0.5s, font-weight 0.5s, box-shadow 0.5s;
49 | }
50 |
51 | .Choose_item_outer {
52 | color: #000;
53 | min-width: 50%;
54 | }
55 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Default_Template/UI/Title/title.scss:
--------------------------------------------------------------------------------
1 | .Title_main {
2 | width: 100%;
3 | height: 100%;
4 | position: absolute;
5 | z-index: 13;
6 | }
7 |
8 | .Title_buttonList {
9 | font-family: "思源宋体", serif;
10 | display: flex;
11 | position: absolute;
12 | left: 0;
13 | min-width: 25%;
14 | height: 100%;
15 | justify-content: center;
16 | align-items: flex-start;
17 | flex-flow: column;
18 | transition: background 0.75s;
19 | padding-left: 120px;
20 | }
21 |
22 | .Title_button {
23 | font-weight: bold;
24 | text-align: center;
25 | flex: 0 1 auto;
26 | cursor: pointer;
27 | padding: 1em 2em 1em 2em;
28 | margin: 20px 0;
29 | transition: all 0.33s;
30 | background: rgba(255, 255, 255, 0.15);
31 | backdrop-filter: blur(5px);
32 | border-radius: 4px;
33 | transform: skewX(-10deg);
34 | background: linear-gradient(to right, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1));
35 |
36 | &:hover {
37 | text-shadow: 0 0 10px rgba(255, 255, 255, 1);
38 | padding: 1em 6em 1em 3em;
39 | }
40 | }
41 |
42 | .Title_button_text {
43 | color: transparent;
44 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
45 | -webkit-background-clip: text;
46 | padding: 0 0.5em 0 0.5em;
47 | font-size: 200%;
48 | font-family: WebgalUI, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
49 | letter-spacing: 0.15em;
50 | }
51 |
52 | .Title_backup_background {
53 | width: 100%;
54 | height: 100%;
55 | position: absolute;
56 | z-index: 13;
57 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
58 | }
59 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Default_Template/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Default_Template/assets/.gitkeep
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Default_Template/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"WebGAL Classic",
3 | "id":"5488d7cf-3523-44f8-bac7-f04ba7a77aa4",
4 | "webgal-version":"4.5.5"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/animationTable.json:
--------------------------------------------------------------------------------
1 | [
2 | "enter-from-left",
3 | "enter-from-bottom",
4 | "enter-from-right",
5 | "shake",
6 | "move-front-and-back",
7 | "enter",
8 | "exit",
9 | "blur",
10 | "oldFilm",
11 | "dotFilm",
12 | "reflectionFilm",
13 | "glitchFilm",
14 | "rgbFilm",
15 | "godrayFilm",
16 | "removeFilm",
17 | "shockwaveIn",
18 | "shockwaveOut"
19 | ]
20 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/blur.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "blur": 0,
4 | "duration": 0
5 | },
6 | {
7 | "blur": 5,
8 | "duration": 300
9 | }
10 | ]
11 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/dotFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "dotFilm": 1,
4 | "duration": 0
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/enter-from-bottom.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "alpha": 0,
4 | "position": {
5 | "x": 0,
6 | "y": 50
7 | },
8 | "blur": 5,
9 | "duration": 0
10 | },
11 | {
12 | "alpha": 1,
13 | "position": {
14 | "x": 0,
15 | "y": 0
16 | },
17 | "blur": 0,
18 | "duration": 500
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/enter-from-left.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "alpha": 0,
4 | "scale": {
5 | "x": 1,
6 | "y": 1
7 | },
8 | "position": {
9 | "x": -50,
10 | "y": 0
11 | },
12 | "rotation": 0,
13 | "blur": 5,
14 | "duration": 0
15 | },
16 | {
17 | "alpha": 1,
18 | "scale": {
19 | "x": 1,
20 | "y": 1
21 | },
22 | "position": {
23 | "x": 0,
24 | "y": 0
25 | },
26 | "rotation": 0,
27 | "blur": 0,
28 | "duration": 500
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/enter-from-right.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "alpha": 0,
4 | "position": {
5 | "x": 50,
6 | "y": 0
7 | },
8 | "blur": 5,
9 | "duration": 0
10 | },
11 | {
12 | "alpha": 1,
13 | "position": {
14 | "x": 0,
15 | "y": 0
16 | },
17 | "blur": 0,
18 | "duration": 500
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/enter.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "alpha": 0,
4 | "duration": 0
5 | },
6 | {
7 | "alpha": 1,
8 | "duration": 300
9 | }
10 | ]
11 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/exit.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "alpha": 1,
4 | "duration": 0
5 | },
6 | {
7 | "alpha": 0,
8 | "duration": 300
9 | }
10 | ]
11 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/glitchFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "glitchFilm": 1,
4 | "duration": 0
5 | }
6 | ]
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/godrayFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "godrayFilm": 1,
4 | "duration": 0
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/move-front-and-back.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "scale": {
4 | "x": 1,
5 | "y": 1
6 | },
7 | "duration": 0
8 | },
9 | {
10 | "scale": {
11 | "x": 1.15,
12 | "y": 1.15
13 | },
14 | "duration": 500
15 | },
16 | {
17 | "scale": {
18 | "x": 1,
19 | "y": 1
20 | },
21 | "duration": 500
22 | }
23 | ]
24 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/oldFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "oldFilm": 1,
4 | "duration": 0
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/reflectionFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "reflectionFilm": 1,
4 | "duration": 0
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/removeFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "oldFilm": 0,
4 | "dotFilm": 0,
5 | "reflectionFilm": 0,
6 | "glitchFilm": 0,
7 | "rgbFilm": 0,
8 | "godrayFilm": 0,
9 | "duration": 0
10 | }
11 | ]
12 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/rgbFilm.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "rgbFilm": 1,
4 | "duration": 0
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/shake.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "position": {
4 | "x": 0,
5 | "y": 0
6 | },
7 | "duration": 0
8 | },
9 | {
10 | "position": {
11 | "x": -100,
12 | "y": 0
13 | },
14 | "duration": 250
15 | },
16 | {
17 | "position": {
18 | "x": 100,
19 | "y": 0
20 | },
21 | "duration": 500
22 | },
23 | {
24 | "position": {
25 | "x": 0,
26 | "y": 0
27 | },
28 | "duration": 250
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/shockwaveIn.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "shockwaveFilter": 0,
4 | "alpha": 0,
5 | "duration": 0
6 | },
7 | {
8 | "shockwaveFilter": 3,
9 | "alpha": 1,
10 | "duration": 2000
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/animation/shockwaveOut.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "shockwaveFilter": 0,
4 | "alpha": 1,
5 | "duration": 0
6 | },
7 | {
8 | "shockwaveFilter": 3,
9 | "alpha": 0,
10 | "duration": 2000
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/background/WebGAL_New_Enter_Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/background/WebGAL_New_Enter_Image.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/background/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/background/bg.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/bgm/s_Title.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/bgm/s_Title.mp3
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/config.txt:
--------------------------------------------------------------------------------
1 | Game_name:欢迎使用WebGAL!;
2 | Game_key:;
3 | Title_img:WebGAL_New_Enter_Image.png;
4 | Title_bgm:s_Title.mp3;
5 | Textbox_theme:imss;
6 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/figure/miniavatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/figure/miniavatar.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/figure/stand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/figure/stand.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/figure/stand2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/figure/stand2.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/scene/start.txt:
--------------------------------------------------------------------------------
1 | ; 初始场景,以及特效演示
2 | bgm:s_Title.mp3;
3 | unlockBgm:s_Title.mp3 -name=雲を追いかけて;
4 | intro:你好|欢迎来到 WebGAL 的世界;
5 | changeBg:bg.png -next;
6 | unlockCg:bg.png -name=良夜; // 解锁CG并赋予名称
7 | changeFigure:stand.png -left -next;
8 | setAnimation:enter-from-left -target=fig-left -next;
9 | WebGAL:欢迎使用 WebGAL!这是一款全新的网页端视觉小说引擎。 -v1.wav;
10 | ; 模版修改演示
11 | WebGAL:普通对话框 -v1.wav -fontSize=large;
12 | miniAvatar:stand.png;
13 | 带有小头像的对话框 -fontSize=default;
14 | setTextbox:hide;
15 | changeFigure:stand.png;
16 | changeFigure:none -next;
17 | choose:选项:选择场景文件|选项2:选择场景文件;
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/template/Stage/Choose/choose.scss:
--------------------------------------------------------------------------------
1 | .Choose_Main {
2 | position: absolute;
3 | width: 100%;
4 | height: 100%;
5 | display: flex;
6 | flex-flow: column;
7 | justify-content: center;
8 | align-items: center;
9 | z-index: 13;
10 | background: rgba(0, 0, 0, 0.05);
11 | }
12 |
13 | .Choose_item {
14 | font-family: "WebgalUI", serif;
15 | cursor: pointer;
16 | min-width: 50%;
17 | padding: 0.25em 1em 0.25em 1em;
18 | font-size: 265%;
19 | color: #8E354A;
20 | text-align: center;
21 | border-radius: 4px;
22 | border: 3px solid rgba(0, 0, 0, 0);
23 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.25);
24 | background: rgba(255, 255, 255, 0.65);
25 | margin: 0.25em 0 0.25em 0;
26 | transition: background-color 0.5s, border 0.5s, font-weight 0.5s, box-shadow 0.5s;
27 |
28 | &:hover {
29 | background: rgba(255, 255, 255, 0.9);
30 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.35);
31 | border: 3px solid #8E354A;
32 | }
33 | }
34 |
35 | .Choose_item_disabled {
36 | font-family: "WebgalUI", serif;
37 | cursor: not-allowed;
38 | min-width: 50%;
39 | padding: 0.25em 1em 0.25em 1em;
40 | font-size: 265%;
41 | color: rgba(142, 53, 74, 0.5);
42 | text-align: center;
43 | border-radius: 4px;
44 | border: 3px solid rgba(0, 0, 0, 0);
45 | box-shadow: 0 0 25px rgba(0, 0, 0, 0.25);
46 | background: rgba(255, 255, 255, 0.5);
47 | margin: 0.25em 0 0.25em 0;
48 | transition: background-color 0.5s, border 0.5s, font-weight 0.5s, box-shadow 0.5s;
49 | }
50 |
51 | .Choose_item_outer {
52 | color: #000;
53 | min-width: 50%;
54 | }
55 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/template/Stage/TextBox/textbox.scss:
--------------------------------------------------------------------------------
1 | .TextBox_main {
2 |
3 | }
4 |
5 | .TextBox_textElement_start {
6 |
7 | }
8 |
9 | .TextBox_textElement_Settled {
10 |
11 | }
12 |
13 | .text {
14 |
15 | }
16 |
17 | .outer {
18 |
19 | }
20 |
21 | .inner {
22 |
23 | }
24 |
25 | .TextBox_showName {
26 |
27 | }
28 |
29 | .outerName {
30 |
31 | }
32 |
33 | .innerName {
34 |
35 | }
36 |
37 | .miniAvatarContainer {
38 |
39 | }
40 |
41 | .miniAvatarImg {
42 |
43 | }
44 |
45 | @keyframes showSoftly {
46 | 0% {
47 | opacity: 0;
48 | }
49 |
50 | 100% {
51 | opacity: 1;
52 | }
53 | }
54 |
55 | @keyframes TextDelayShow {
56 | 0% {
57 | opacity: 0;
58 | }
59 |
60 | 100% {
61 | opacity: 1;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/template/UI/Title/title.scss:
--------------------------------------------------------------------------------
1 | .Title_main {
2 | width: 100%;
3 | height: 100%;
4 | position: absolute;
5 | z-index: 13;
6 | }
7 |
8 | .Title_buttonList {
9 | font-family: "思源宋体", serif;
10 | display: flex;
11 | position: absolute;
12 | left: 0;
13 | min-width: 25%;
14 | height: 100%;
15 | justify-content: center;
16 | align-items: flex-start;
17 | flex-flow: column;
18 | transition: background 0.75s;
19 | padding-left: 120px;
20 | }
21 |
22 | .Title_button {
23 | font-weight: bold;
24 | text-align: center;
25 | flex: 0 1 auto;
26 | cursor: pointer;
27 | padding: 1em 2em 1em 2em;
28 | margin: 20px 0;
29 | transition: all 0.33s;
30 | background: rgba(255, 255, 255, 0.15);
31 | backdrop-filter: blur(5px);
32 | border-radius: 4px;
33 | transform: skewX(-10deg);
34 | background: linear-gradient(to right, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1));
35 |
36 | &:hover {
37 | text-shadow: 0 0 10px rgba(255, 255, 255, 1);
38 | padding: 1em 6em 1em 3em;
39 | }
40 | }
41 |
42 | .Title_button_text {
43 | color: transparent;
44 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
45 | -webkit-background-clip: text;
46 | padding: 0 0.5em 0 0.5em;
47 | font-size: 200%;
48 | font-family: WebgalUI, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
49 | letter-spacing: 0.15em;
50 | }
51 |
52 | .Title_backup_background {
53 | width: 100%;
54 | height: 100%;
55 | position: absolute;
56 | z-index: 13;
57 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
58 | }
59 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/template/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"WebGAL Classic",
3 | "id":"5488d7cf-3523-44f8-bac7-f04ba7a77aa4",
4 | "webgal-version":"4.5.13"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/tex/cherryBlossoms.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/tex/cherryBlossoms.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/tex/raindrop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/tex/raindrop.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/tex/snowFlake_min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/tex/snowFlake_min.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/userStyleSheet.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/userStyleSheet.css
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/video/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/video/.gitkeep
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/game/vocal/v1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/game/vocal/v1.wav
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/favicon.ico
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/icon-192-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/icon-192-maskable.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/icon-192.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/icon-512-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/icon-512-maskable.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/icons/icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/assets/templates/WebGAL_Template/icons/icon-512.png
--------------------------------------------------------------------------------
/packages/terre2/assets/templates/WebGAL_Template/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "WebGAL",
3 | "short_name": "WebGAL",
4 | "start_url": ".",
5 | "display": "fullscreen",
6 | "description": "WebGAL DEMO",
7 | "dir": "auto",
8 | "orientation": "landscape",
9 | "icons": [
10 | {
11 | "src": "./icons/icon-192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "./icons/icon-512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | },
20 | {
21 | "src": "./icons/icon-192-maskable.png",
22 | "type": "image/png",
23 | "sizes": "192x192",
24 | "purpose": "maskable"
25 | },
26 | {
27 | "src": "./icons/icon-512-maskable.png",
28 | "type": "image/png",
29 | "sizes": "512x512",
30 | "purpose": "maskable"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/packages/terre2/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/terre2/public/games/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/public/games/.gitkeep
--------------------------------------------------------------------------------
/packages/terre2/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Nest Template
6 |
7 |
8 |
9 | Going to WebGAL Terre......
10 | Here are some tests to test the state of the API.
11 | Test WebGAL Terre Service
12 | Test WebGAL Terre Api
13 | Go to WebGAL Terre dev page
14 | WebGAL Terre
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/terre2/public/templates/WebGAL Black/UI/Title/title.scss:
--------------------------------------------------------------------------------
1 | .Title_main {
2 | width: 100%;
3 | height: 100%;
4 | position: absolute;
5 | z-index: 13;
6 | }
7 |
8 | .Title_buttonList {
9 | font-family: "思源宋体", serif;
10 | display: flex;
11 | position: absolute;
12 | left: 0;
13 | min-width: 25%;
14 | height: 100%;
15 | justify-content: center;
16 | align-items: flex-start;
17 | flex-flow: column;
18 | transition: background 0.75s;
19 | padding-left: 120px;
20 | }
21 |
22 | .Title_button {
23 | font-weight: bold;
24 | text-align: center;
25 | flex: 0 1 auto;
26 | cursor: pointer;
27 | padding: 1em 2em 1em 2em;
28 | margin: 20px 0;
29 | transition: all 0.33s;
30 | background: rgba(255, 255, 255, 0.15);
31 | backdrop-filter: blur(5px);
32 | border-radius: 4px;
33 | transform: skewX(-10deg);
34 | background: linear-gradient(to right, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.1));
35 |
36 | &:hover {
37 | text-shadow: 0 0 10px rgba(255, 255, 255, 1);
38 | padding: 1em 6em 1em 3em;
39 | }
40 | }
41 |
42 | .Title_button_text {
43 | color: transparent;
44 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
45 | -webkit-background-clip: text;
46 | padding: 0 0.5em 0 0.5em;
47 | font-size: 200%;
48 | font-family: WebgalUI, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
49 | letter-spacing: 0.15em;
50 | }
51 |
52 | .Title_backup_background {
53 | width: 100%;
54 | height: 100%;
55 | position: absolute;
56 | z-index: 13;
57 | background: linear-gradient(135deg, #fdfbfb 0%, #dcddde 100%);
58 | }
59 |
--------------------------------------------------------------------------------
/packages/terre2/public/templates/WebGAL Black/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenWebGAL/WebGAL_Terre/fe4d14e9b46e84ce29504ee0019b95c4712ac9a5/packages/terre2/public/templates/WebGAL Black/assets/.gitkeep
--------------------------------------------------------------------------------
/packages/terre2/public/templates/WebGAL Black/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "WebGAL Black",
3 | "id": "805c5f5a-8f52-461f-8931-613676d6a086",
4 | "webgal-version": "4.5.0"
5 | }
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/assets/assets.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class UploadFilesDto {
4 | @ApiProperty({ description: 'Target directory for the uploaded files' })
5 | targetDirectory: string;
6 | }
7 |
8 | export class CreateNewFileDto {
9 | @ApiProperty({
10 | description: 'The source path where the directory will be created',
11 | })
12 | source: string;
13 |
14 | @ApiProperty({ description: 'Name for the new file' })
15 | name: string;
16 | }
17 |
18 | export class CreateNewFolderDto {
19 | @ApiProperty({
20 | description: 'The source path where the directory will be created',
21 | })
22 | source: string;
23 |
24 | @ApiProperty({ description: 'Name for the new directory' })
25 | name: string;
26 | }
27 |
28 | export class DeleteFileOrDirDto {
29 | @ApiProperty({
30 | description: 'The source path of the file or directory to be deleted',
31 | })
32 | source: string;
33 | }
34 |
35 | export class RenameFileDto {
36 | @ApiProperty({
37 | description: 'The source path of the file or directory to be renamed',
38 | })
39 | source: string;
40 |
41 | @ApiProperty({ description: 'New name for renaming the file or directory' })
42 | newName: string;
43 | }
44 |
45 | export class EditTextFileDto {
46 | @ApiProperty({ description: 'The path of textfile' })
47 | path: string;
48 |
49 | @ApiProperty({
50 | description: 'Text data content',
51 | type: 'string',
52 | })
53 | textFile: string;
54 | }
55 |
56 | export class ApplyTemplateToGameDto {
57 | @ApiProperty({ description: 'The template name to apply' })
58 | templateDir: string;
59 |
60 | @ApiProperty({
61 | description: 'The game name to be applied.',
62 | })
63 | gameDir: string;
64 | }
65 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/assets/assets.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AssetsController } from './assets.controller';
3 | import { AssetsService } from './assets.service';
4 | import { WebgalFsModule } from '../webgal-fs/webgal-fs.module';
5 |
6 | @Module({
7 | imports: [WebgalFsModule],
8 | controllers: [AssetsController],
9 | providers: [AssetsService],
10 | })
11 | export class AssetsModule {}
12 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/assets/assets.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AssetsService {}
5 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/lsp/gateway.ts:
--------------------------------------------------------------------------------
1 | import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
2 | import { Server, WebSocket } from 'ws';
3 | import {
4 | WebSocketMessageReader,
5 | WebSocketMessageWriter,
6 | } from 'vscode-ws-jsonrpc-webgal';
7 | import { createWsConnection } from './webgalLsp';
8 | import { pprintJSON } from '../../util/strings';
9 |
10 | function toIWebSocket(ws: WebSocket): any {
11 | return {
12 | send: (content) => {
13 | const log = pprintJSON(content);
14 | if (!log.includes('data')) {
15 | // console.log(`<- ${log}`);
16 | }
17 | ws.send(content);
18 | },
19 | onMessage: (cb) =>
20 | (ws.onmessage = (event) => {
21 | const log = pprintJSON(event.data);
22 | // console.log(`-> ${log}`);
23 | cb(event.data);
24 | }),
25 | onError: (cb) =>
26 | (ws.onerror = (event) => {
27 | if ('message' in event) {
28 | cb((event as any).message);
29 | }
30 | }),
31 | onClose: (cb) => (ws.onclose = (event) => cb(event.code, event.reason)),
32 | dispose: () => ws.close(),
33 | };
34 | }
35 |
36 | @WebSocketGateway({
37 | path: '/api/lsp2',
38 | transports: 'websocket',
39 | })
40 | export class LspGateway {
41 | @WebSocketServer()
42 | private server: Server;
43 |
44 | afterInit(server: Server) {
45 | this.server = server;
46 | this.listenForMessages();
47 | }
48 |
49 | private pipeSocket(ws) {
50 | const reader = new WebSocketMessageReader(ws);
51 | const writer = new WebSocketMessageWriter(ws);
52 | createWsConnection(reader, writer);
53 | }
54 |
55 | listenForMessages() {
56 | this.server.on('connection', (ws) => {
57 | this.pipeSocket(toIWebSocket(ws));
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/lsp/suggestionRules/getKeywordsAndConstants.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CompletionItem,
3 | CompletionItemKind,
4 | Position,
5 | } from 'vscode-languageserver';
6 |
7 | export function getKeywordsAndConstants(
8 | line: string,
9 | allTextBefore: string,
10 | position: Position,
11 | ): CompletionItem[] {
12 | if (line.endsWith('setTextbox:')) {
13 | return constants;
14 | } else if (line.endsWith(':')) {
15 | return keyWords;
16 | }
17 | return [];
18 | }
19 |
20 | const keyWords: CompletionItem[] = [
21 | {
22 | label: 'none',
23 | kind: CompletionItemKind.Keyword,
24 | documentation: `空值`,
25 | detail: `keyword none`,
26 | insertText: 'none',
27 | },
28 | ];
29 |
30 | const constants: CompletionItem[] = [
31 | {
32 | label: 'hide',
33 | kind: CompletionItemKind.Constant,
34 | documentation: `隐藏文本框`,
35 | detail: `constant hide`,
36 | insertText: 'hide;',
37 | },
38 | ];
39 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/lsp/suggestionRules/reference.ts:
--------------------------------------------------------------------------------
1 | import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';
2 |
3 | const suggestions: CompletionItem[] = [
4 | {
5 | label: 'bg.png',
6 | kind: CompletionItemKind.File,
7 | detail: 'Change background image.',
8 | },
9 | {
10 | label: 'id',
11 | kind: CompletionItemKind.Constant,
12 | detail: 'The ID of the image.',
13 | insertText: 'id=',
14 | },
15 | {
16 | label: 'true',
17 | kind: CompletionItemKind.Constant,
18 | detail: 'The value is true.',
19 | insertText: '${1:true}',
20 | },
21 | {
22 | label: 'false',
23 | kind: CompletionItemKind.Constant,
24 | detail: 'The value is false.',
25 | insertText: '${1:false}',
26 | },
27 | {
28 | label: 'number',
29 | kind: CompletionItemKind.Class,
30 | detail: 'The value is a number.',
31 | insertText: '${1:number}',
32 | },
33 | {
34 | label: 'string',
35 | kind: CompletionItemKind.Class,
36 | detail: 'The value is a string.',
37 | insertText: '${1:string}',
38 | },
39 | ];
40 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/lsp/suggestionRules/template.ts:
--------------------------------------------------------------------------------
1 | import { CompletionItem, Position } from 'vscode-languageserver';
2 |
3 | export function getTemplate(
4 | line: string,
5 | allTextBefore: string,
6 | position: Position,
7 | ): CompletionItem[] {
8 | return [];
9 | }
10 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/manage-game/manage-game.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ManageGameController } from './manage-game.controller';
3 |
4 | describe('ManageGameController', () => {
5 | let controller: ManageGameController;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | controllers: [ManageGameController],
10 | }).compile();
11 |
12 | controller = module.get(ManageGameController);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(controller).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/manage-game/manage-game.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ManageGameService } from './manage-game.service';
3 | import { WebgalFsModule } from '../webgal-fs/webgal-fs.module';
4 | import { ManageGameController } from './manage-game.controller';
5 |
6 | @Module({
7 | imports: [WebgalFsModule],
8 | providers: [ManageGameService],
9 | controllers: [ManageGameController],
10 | })
11 | export class ManageGameModule {}
12 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/manage-game/manage-game.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ManageGameService } from './manage-game.service';
3 |
4 | describe('ManageGameService', () => {
5 | let service: ManageGameService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [ManageGameService],
10 | }).compile();
11 |
12 | service = module.get(ManageGameService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/manage-template/manage-template.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class TemplateConfigDto {
4 | @ApiProperty({ description: 'The name of the template' })
5 | name: string;
6 | @ApiProperty({ description: 'The id of the template' })
7 | id?: string;
8 | @ApiProperty({ description: 'The webgal version of the template' })
9 | 'webgal-version': string;
10 | }
11 |
12 | export class TemplateInfoDto extends TemplateConfigDto {
13 | @ApiProperty({ description: 'The dir of the template' })
14 | dir: string;
15 | }
16 |
17 | export class CreateTemplateDto {
18 | @ApiProperty({ description: 'The name of the template to be created' })
19 | templateName: string;
20 | @ApiProperty({ description: 'The dir of the template' })
21 | templateDir: string;
22 | }
23 |
24 | export class UpdateTemplateConfigDto {
25 | @ApiProperty({ description: 'The dir of the template' })
26 | templateDir: string;
27 | @ApiProperty({ description: 'The new config of the template' })
28 | newTemplateConfig: TemplateConfigDto;
29 | }
30 |
31 | export class GetStyleByClassNameDto {
32 | @ApiProperty({ description: 'The name of class to be fetched' })
33 | className: string;
34 | @ApiProperty({ description: 'The path of stylesheet file to be fetched' })
35 | filePath: string;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/manage-template/manage-template.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ManageTemplateService } from './manage-template.service';
3 | import { WebgalFsModule } from '../webgal-fs/webgal-fs.module';
4 | import { ManageTemplateController } from './manage-template.controller';
5 |
6 | @Module({
7 | imports: [WebgalFsModule],
8 | providers: [ManageTemplateService],
9 | controllers: [ManageTemplateController],
10 | })
11 | export class ManageTemplateModule {}
12 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/template-preview/template-preview.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Controller,
3 | Get,
4 | NotFoundException,
5 | Param,
6 | Req,
7 | StreamableFile,
8 | } from '@nestjs/common';
9 | import { TemplatePreviewService } from './template-preview.service';
10 |
11 | @Controller('template-preview')
12 | export class TemplatePreviewController {
13 | constructor(
14 | private readonly templatePreviewService: TemplatePreviewService,
15 | ) {}
16 |
17 | @Get('/:templateName/game/template/:path(*)')
18 | getTemplateAsset(
19 | @Param('path') path: string,
20 | @Param('templateName') templateName: string,
21 | @Req() req: Request,
22 | ) {
23 | const url = req.url;
24 | const templateFilePath = url.split('/template/')?.[1] ?? '';
25 | const targetPath = `${templateName}/${templateFilePath}`;
26 | const readResult =
27 | this.templatePreviewService.getTemplateFileByPath(targetPath);
28 | if (readResult) {
29 | return new StreamableFile(readResult);
30 | } else {
31 | throw new NotFoundException('The requested file does not exist.');
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/template-preview/template-preview.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TemplatePreviewController } from './template-preview.controller';
3 | import { TemplatePreviewService } from './template-preview.service';
4 | import { WebgalFsModule } from '../webgal-fs/webgal-fs.module';
5 |
6 | @Module({
7 | imports: [WebgalFsModule],
8 | controllers: [TemplatePreviewController],
9 | providers: [TemplatePreviewService],
10 | })
11 | export class TemplatePreviewModule {}
12 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/template-preview/template-preview.service.ts:
--------------------------------------------------------------------------------
1 | import { ConsoleLogger, Injectable } from '@nestjs/common';
2 | import { WebgalFsService } from '../webgal-fs/webgal-fs.service';
3 | import * as fsp from 'fs/promises';
4 | import { createReadStream, ReadStream } from 'fs';
5 |
6 | @Injectable()
7 | export class TemplatePreviewService {
8 | constructor(
9 | private readonly logger: ConsoleLogger,
10 | private readonly webgalFs: WebgalFsService,
11 | ) {}
12 |
13 | /**
14 | * 获取某个模板下的文件
15 | * @param path path 形如 templateName/UI/xxx.scss
16 | */
17 | getTemplateFileByPath(path: string): undefined | ReadStream {
18 | const targetPath = this.webgalFs.getPathFromRoot(
19 | `/public/templates/${path}`,
20 | );
21 | let result: ReadStream | undefined;
22 | try {
23 | result = createReadStream(targetPath);
24 | } catch (e) {
25 | result = undefined;
26 | }
27 | return result;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/webgal-fs/webgal-fs.module.ts:
--------------------------------------------------------------------------------
1 | import { ConsoleLogger, Module } from '@nestjs/common';
2 | import { WebgalFsService } from './webgal-fs.service';
3 |
4 | @Module({
5 | providers: [WebgalFsService, ConsoleLogger],
6 | exports: [WebgalFsService, ConsoleLogger],
7 | })
8 | export class WebgalFsModule {}
9 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/webgal-fs/webgal-fs.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { WebgalFsService } from './webgal-fs.service';
3 |
4 | describe('WebgalFsService', () => {
5 | let service: WebgalFsService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [WebgalFsService],
10 | }).compile();
11 |
12 | service = module.get(WebgalFsService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/terre2/src/Modules/websocket/websocketGateway.ts:
--------------------------------------------------------------------------------
1 | import {
2 | // ConnectedSocket,
3 | MessageBody,
4 | SubscribeMessage,
5 | WebSocketGateway,
6 | WebSocketServer,
7 | } from '@nestjs/websockets';
8 | import { Server, WebSocket } from 'ws';
9 |
10 | @WebSocketGateway({ path: '/api/webgalsync', transports: 'websocket' })
11 | export class WebGalWebSocketGateway {
12 | @WebSocketServer()
13 | private server: Server;
14 |
15 | private connectionList: WebSocket[] = [];
16 |
17 | afterInit(server: Server) {
18 | this.server = server;
19 | }
20 |
21 | handleConnection(client: WebSocket) {
22 | this.connectionList.push(client);
23 | }
24 |
25 | handleDisconnect(client: WebSocket) {
26 | const index = this.connectionList.indexOf(client);
27 | if (index !== -1) {
28 | this.connectionList.splice(index, 1);
29 | }
30 | }
31 |
32 | @SubscribeMessage('message')
33 | handleMessage(
34 | @MessageBody() data: string, // @ConnectedSocket() client: WebSocket,
35 | ): void {
36 | this.connectionList.forEach((socket) => {
37 | const sendData = JSON.stringify({
38 | event: 'message',
39 | data,
40 | });
41 | socket.send(sendData);
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/terre2/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let appController: AppController;
7 |
8 | beforeEach(async () => {
9 | const app: TestingModule = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 |
14 | appController = app.get(AppController);
15 | });
16 |
17 | describe('root', () => {
18 | it('should return "Hello World!"', () => {
19 | expect(appController.apiTest()).toBe('API Test OK');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/packages/terre2/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 | import { ApiTags } from '@nestjs/swagger';
4 |
5 | @Controller()
6 | @ApiTags('Test Server')
7 | export class AppController {
8 | constructor(private readonly appService: AppService) {}
9 |
10 | @Get('/api/test')
11 | apiTest(): string {
12 | return this.appService.getApiTest();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/terre2/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 | import { ServeStaticModule } from '@nestjs/serve-static';
5 | import { join } from 'path';
6 | import { ManageGameModule } from './Modules/manage-game/manage-game.module';
7 | import { ManageTemplateModule } from './Modules/manage-template/manage-template.module';
8 | // import { LspModule } from './Modules/lsp/lsp.module';
9 | import { TemplatePreviewModule } from './Modules/template-preview/template-preview.module';
10 | import { AssetsModule } from './Modules/assets/assets.module';
11 | import { WebGalWebSocketGateway } from './Modules/websocket/websocketGateway';
12 | import { LspGateway } from './Modules/lsp/gateway';
13 |
14 | @Module({
15 | imports: [
16 | // 代码提示
17 | // LspModule,
18 | // 资源管理
19 | AssetsModule,
20 | ManageGameModule,
21 | ManageTemplateModule,
22 | TemplatePreviewModule,
23 | // 静态文件服务:游戏与编辑器静态资源文件
24 | ServeStaticModule.forRoot({
25 | rootPath: join(process.cwd(), 'public'),
26 | serveRoot: '/',
27 | }),
28 | // 静态文件服务:引擎模板
29 | ServeStaticModule.forRoot({
30 | rootPath: join(process.cwd(), 'assets', 'templates', 'WebGAL_Template'),
31 | serveRoot: '/games/:gamename/',
32 | }),
33 | // 静态文件服务:引擎模板预览用游戏
34 | ServeStaticModule.forRoot({
35 | rootPath: join(process.cwd(), 'assets', 'templates', 'WebGAL_Template'),
36 | serveRoot: '/template-preview/:template/',
37 | }),
38 | ],
39 | controllers: [AppController],
40 | providers: [AppService, WebGalWebSocketGateway, LspGateway],
41 | })
42 | export class AppModule {}
43 |
--------------------------------------------------------------------------------
/packages/terre2/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AppService {
5 | getApiTest() {
6 | return 'WebGAL Terre Application API Test OK';
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/terre2/src/http/testApi.http:
--------------------------------------------------------------------------------
1 | POST http://localhost/api/manageGame/mkdir HTTP/1.1
2 | Content-Type: application/json
3 |
4 | {
5 | "source": "public/games/新的游戏/game/bgm",
6 | "name": "123"
7 | }
8 |
9 | ###
10 | POST http://localhost/api/manageGame/rename HTTP/1.1
11 | Content-Type: application/json
12 |
13 | {
14 | "source": "public/games/新的游戏/game/bgm/123",
15 | "newName": "456"
16 | }
17 |
18 | ###
19 | POST http://localhost/api/manageGame/delete HTTP/1.1
20 | Content-Type: application/json
21 |
22 | {
23 | "source": "public/games/新的游戏/game/bgm/456"
24 | }
25 |
26 | ###
27 | POST http://localhost/api/manageTemplate/getStyleByClassName HTTP/1.1
28 | Content-Type: application/json
29 |
30 | {
31 | "filePath": "templates/新的模板/UI/Title/title.scss",
32 | "className": "Title_main"
33 | }
--------------------------------------------------------------------------------
/packages/terre2/src/http/testApplyTemplate.http:
--------------------------------------------------------------------------------
1 | POST http://localhost:3001/api/manageTemplate/applyTemplateToGame
2 | Content-Type: application/json
3 |
4 | {
5 | "templateName": "新的模板333333",
6 | "gameName": "ng"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/terre2/src/logger.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 |
4 | const logsDir = path.join(process.cwd(), 'logs');
5 | const logFile = path.join(logsDir, 'log.txt');
6 | const logFileMaxSize = 1024 * 1024;
7 |
8 | // 确保日志目录存在
9 | if (!fs.existsSync(logsDir)) {
10 | try {
11 | fs.mkdirSync(logsDir);
12 | } catch (error) {
13 | console.error('Error creating logs directory:', error);
14 | }
15 | }
16 |
17 | const checkLogFileSize = (filePath: string, maxSize: number) => {
18 | fs.stat(filePath, (err, stats) => {
19 | if (!err && stats.size > maxSize) {
20 | const oldLogFile = filePath.replace('log.txt', 'log.old.txt');
21 | fs.rename(filePath, oldLogFile, (renameErr) => {
22 | if (renameErr) {
23 | console.error('Error renaming log file:', renameErr);
24 | }
25 | });
26 | }
27 | });
28 | };
29 |
30 | // 替换特殊字符
31 | const cleanLog = (log: string) => log.replace(/(\x1B\[[0-9;]*m)/g, '');
32 |
33 | // 在启动时检查日志文件大小
34 | checkLogFileSize(logFile, logFileMaxSize);
35 |
36 | const logStream = fs.createWriteStream(logFile, { flags: 'a' });
37 |
38 | const originalStdoutWrite = process.stdout.write;
39 | const originalStderrWrite = process.stderr.write;
40 |
41 | process.stdout.write = function (...args: any[]) {
42 | try {
43 | logStream.write(`${new Date().toISOString()} [LOG]: ${cleanLog(args[0])}`);
44 | } catch (_) {}
45 | return originalStdoutWrite.apply(process.stdout, args);
46 | };
47 |
48 | process.stderr.write = function (...args: any[]) {
49 | try {
50 | logStream.write(
51 | `${new Date().toISOString()} [ERROR]: ${cleanLog(args[0])}`,
52 | );
53 | } catch (_) {}
54 | return originalStderrWrite.apply(process.stderr, args);
55 | };
56 |
--------------------------------------------------------------------------------
/packages/terre2/src/util/open.ts:
--------------------------------------------------------------------------------
1 | import open = require('open');
2 | import * as process from 'process';
3 |
4 | export function _open(target: string) {
5 | try {
6 | if (process.platform === 'win32') open(target);
7 | if (process.platform === 'darwin') open(target);
8 | if (process.platform === 'linux') open(target);
9 | } catch (_) {}
10 | }
11 |
--------------------------------------------------------------------------------
/packages/terre2/src/util/strings.ts:
--------------------------------------------------------------------------------
1 | export function pprintJSON(obj: Object, needStringify = false) {
2 | let jsonStr: string;
3 |
4 | if (needStringify) {
5 | jsonStr = JSON.stringify(obj);
6 | } else {
7 | if (typeof obj === 'string') {
8 | jsonStr = obj;
9 | } else {
10 | jsonStr = obj.toString();
11 | }
12 | }
13 | return JSON.stringify(JSON.parse(jsonStr), null, 2);
14 | }
15 |
--------------------------------------------------------------------------------
/packages/terre2/src/util/webgal-parser.ts:
--------------------------------------------------------------------------------
1 | import SceneParser, {
2 | ADD_NEXT_ARG_LIST,
3 | SCRIPT_CONFIG,
4 | } from 'webgal-parser/build/cjs/index.cjs';
5 |
6 | export const webgalParser = new SceneParser(
7 | (assetList) => {
8 | return;
9 | },
10 | (fileName, assetType) => {
11 | return fileName;
12 | },
13 | ADD_NEXT_ARG_LIST,
14 | [...SCRIPT_CONFIG],
15 | );
16 |
--------------------------------------------------------------------------------
/packages/terre2/standalone.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const path = require('path');
3 | const webpack = require('webpack');
4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
5 |
6 | module.exports = {
7 | entry: './src/main',
8 | target: 'node',
9 | // 置为空即可忽略webpack-node-externals插件
10 | externals: {},
11 | // ts文件的处理
12 | module: {
13 | rules: [
14 | {
15 | test: /\.ts?$/,
16 | use: {
17 | loader: 'ts-loader',
18 | options: { transpileOnly: true },
19 | },
20 | exclude: /node_modules/,
21 | },
22 | ],
23 | },
24 | // 打包后的文件名称以及位置
25 | output: {
26 | filename: 'main.js',
27 | path: path.resolve(__dirname, 'dist'),
28 | },
29 | resolve: {
30 | extensions: ['.js', '.ts', '.json'],
31 | },
32 | plugins: [
33 | // 需要进行忽略的插件
34 | new webpack.IgnorePlugin({
35 | checkResource(resource) {
36 | const lazyImports = [
37 | '@nestjs/microservices',
38 | '@nestjs/microservices/microservices-module',
39 | '@nestjs/websockets/socket-module',
40 | 'cache-manager',
41 | 'class-validator',
42 | 'class-transformer',
43 | 'class-transformer/storage',
44 | ];
45 | if (!lazyImports.includes(resource)) {
46 | return false;
47 | }
48 | try {
49 | require.resolve(resource, {
50 | paths: [process.cwd()],
51 | });
52 | } catch (err) {
53 | return true;
54 | }
55 | return false;
56 | },
57 | }),
58 | new ForkTsCheckerWebpackPlugin(),
59 | ],
60 | };
61 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/connection.d.ts:
--------------------------------------------------------------------------------
1 | import { MessageConnection, Logger } from 'vscode-jsonrpc';
2 | import { IWebSocket } from './socket/socket.js';
3 | export declare function listen(options: {
4 | webSocket: WebSocket;
5 | logger?: Logger;
6 | onConnection: (connection: MessageConnection) => void;
7 | }): void;
8 | export declare function toSocket(webSocket: WebSocket): IWebSocket;
9 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/disposable.d.ts:
--------------------------------------------------------------------------------
1 | import { Disposable } from 'vscode-jsonrpc';
2 | export declare class DisposableCollection implements Disposable {
3 | protected readonly disposables: Disposable[];
4 | dispose(): void;
5 | push(disposable: Disposable): Disposable;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './disposable.js';
2 | export * from './socket/index.js';
3 | export * from './logger.js';
4 | export * from './connection.js';
5 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/logger.d.ts:
--------------------------------------------------------------------------------
1 | import { Logger } from 'vscode-jsonrpc';
2 | export declare class ConsoleLogger implements Logger {
3 | error(message: string): void;
4 | warn(message: string): void;
5 | info(message: string): void;
6 | log(message: string): void;
7 | debug(message: string): void;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/server/connection.d.ts:
--------------------------------------------------------------------------------
1 | import { MessageReader, MessageWriter, Disposable, Message } from 'vscode-jsonrpc';
2 | export interface IConnection extends Disposable {
3 | readonly reader: MessageReader;
4 | readonly writer: MessageWriter;
5 | forward(to: IConnection, map?: (message: Message) => Message): void;
6 | onClose(callback: () => void): Disposable;
7 | }
8 | export declare function forward(clientConnection: IConnection, serverConnection: IConnection, map?: (message: Message) => Message): void;
9 | export declare function createConnection(reader: MessageReader, writer: MessageWriter, onDispose: () => void, extensions?: T): IConnection & T;
10 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/server/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './connection.js';
2 | export * from './launch.js';
3 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/server/launch.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | import * as net from 'net';
5 | import * as stream from 'stream';
6 | import * as cp from 'child_process';
7 | import { IConnection } from './connection.js';
8 | import { IWebSocket, IWebSocketConnection } from '../socket/socket.js';
9 | export declare function createServerProcess(serverName: string, command: string, args?: string[], options?: cp.SpawnOptions): IConnection | undefined;
10 | export declare function createWebSocketConnection(socket: IWebSocket): IWebSocketConnection;
11 | export declare function createProcessSocketConnection(process: cp.ChildProcess, outSocket: net.Socket, inSocket?: net.Socket): IConnection;
12 | export declare function createSocketConnection(outSocket: net.Socket, inSocket: net.Socket, onDispose: () => void): IConnection;
13 | export declare function createProcessStreamConnection(process: cp.ChildProcess): IConnection | undefined;
14 | export declare function createStreamConnection(outStream: stream.Readable, inStream: stream.Writable, onDispose: () => void): IConnection;
15 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/socket/connection.d.ts:
--------------------------------------------------------------------------------
1 | import { MessageConnection, Logger } from 'vscode-jsonrpc';
2 | import { IWebSocket } from './socket.js';
3 | export declare function createWebSocketConnection(socket: IWebSocket, logger: Logger): MessageConnection;
4 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/socket/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './socket.js';
2 | export * from './reader.js';
3 | export * from './writer.js';
4 | export * from './connection.js';
5 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/socket/reader.d.ts:
--------------------------------------------------------------------------------
1 | import { Disposable } from 'vscode-jsonrpc';
2 | import { DataCallback, AbstractMessageReader, MessageReader } from 'vscode-jsonrpc/lib/common/messageReader.js';
3 | import { IWebSocket } from './socket.js';
4 | export declare class WebSocketMessageReader extends AbstractMessageReader implements MessageReader {
5 | protected readonly socket: IWebSocket;
6 | protected state: 'initial' | 'listening' | 'closed';
7 | protected callback: DataCallback | undefined;
8 | protected readonly events: Array<{
9 | message?: any;
10 | error?: any;
11 | }>;
12 | constructor(socket: IWebSocket);
13 | listen(callback: DataCallback): Disposable;
14 | dispose(): void;
15 | protected readMessage(message: any): void;
16 | protected fireError(error: any): void;
17 | protected fireClose(): void;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/socket/socket.d.ts:
--------------------------------------------------------------------------------
1 | import { Disposable } from 'vscode-jsonrpc';
2 | import { IConnection } from '../server/connection.js';
3 | export interface IWebSocket extends Disposable {
4 | send(content: string): void;
5 | onMessage(cb: (data: any) => void): void;
6 | onError(cb: (reason: any) => void): void;
7 | onClose(cb: (code: number, reason: string) => void): void;
8 | }
9 | export interface IWebSocketConnection extends IConnection {
10 | readonly socket: IWebSocket;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/terre2/terre-custom-libs/vscode-ws-jsonrpc-webgal/build/socket/writer.d.ts:
--------------------------------------------------------------------------------
1 | import { Message } from 'vscode-jsonrpc/lib/common/messages.js';
2 | import { AbstractMessageWriter, MessageWriter } from 'vscode-jsonrpc/lib/common/messageWriter.js';
3 | import { IWebSocket } from './socket.js';
4 | export declare class WebSocketMessageWriter extends AbstractMessageWriter implements MessageWriter {
5 | protected errorCount: number;
6 | protected readonly socket: IWebSocket;
7 | constructor(socket: IWebSocket);
8 | end(): void;
9 | write(msg: Message): Promise;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/terre2/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import * as request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/packages/terre2/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/terre2/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/terre2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pr-check-linux.yml:
--------------------------------------------------------------------------------
1 | name: Build Check Linux
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - synchronize
8 | jobs:
9 | build:
10 | name: Build Binary
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 | - name: Install Node.js
16 | uses: actions/setup-node@v2
17 | with:
18 | node-version: '16.x'
19 | - name: Build
20 | env:
21 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
22 | run: sh release-linux.sh
23 | - name: Install 7-Zip
24 | run: sudo apt-get install p7zip-full
25 | - name: Compress
26 | run: |
27 | # Use "-mx9" option for extreme compression
28 | 7z a -t7z -mx9 WebGAL_Terre.7z release/*
29 | - name: Upload Artifact
30 | uses: actions/upload-artifact@v2
31 | with:
32 | name: WebGAL_Terre.7z
33 | path: WebGAL_Terre.7z
34 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | echo "Welcome to build WebGAL Terre, the editor of WebGAL platform."
2 | # 安装依赖
3 | yarn install --frozen-lockfile --network-timeout=300000
4 |
5 | # 清理
6 | rm -rf release
7 |
8 | mkdir release
9 |
10 | # 进入 Terre 目录
11 | cd packages/terre2
12 | yarn run build
13 | yarn run pkg
14 | cd dist
15 | cp -r WebGAL_Terre.exe ../../../release
16 | rm WebGAL_Terre.exe
17 | cd ../
18 | mkdir Exported_Games
19 | cp -r public assets Exported_Games ../../release
20 | cd ../../
21 |
22 | # 进入 Origine 目录
23 | cd packages/origine2
24 | yarn run build
25 | cp -rf dist/* ../../release/public/
26 | cd ../../
27 |
28 | # 进入 Electron 目录
29 | cd packages/WebGAL-electron
30 | yarn install --frozen-lockfile
31 | yarn run build
32 | mkdir ../../release/assets/templates/WebGAL_Electron_Template
33 | cp -rf build/win-unpacked/* ../../release/assets/templates/WebGAL_Electron_Template/
34 | cd ../../
35 |
36 | # 克隆 WebGAL Android 模板
37 | cd release/assets/templates/
38 | git clone https://github.com/nini22P/WebGAL-Android.git
39 | mv WebGAL-Android WebGAL_Android_Template
40 | # MainActivity.kt 移动到主文件夹防止误删
41 | mv WebGAL_Android_Template/app/src/main/java/com/openwebgal/demo/MainActivity.kt WebGAL_Android_Template/app/src/main/java/MainActivity.kt
42 | cd ../../../
43 |
44 | cd release
45 |
46 | # 删除冗余文件
47 | rm -rf Exported_Games/*
48 | rm -rf public/games/*
49 | rm -rf public/games/.gitkeep
50 | rm -rf assets/templates/WebGAL_Template/game/video/*
51 | rm -rf assets/templates/WebGAL_Template/game/video/.gitkeep
52 | rm -rf assets/templates/WebGAL_Android_Template/.github
53 | rm -rf assets/templates/WebGAL_Android_Template/.git
54 | rm -rf assets/templates/WebGAL_Android_Template/.gitattributes
55 | rm -rf assets/templates/WebGAL_Android_Template/app/src/main/assets/webgal/.gitkeep
56 | rm -rf assets/templates/WebGAL_Android_Template/app/src/main/java/com
57 |
58 | echo "WebGAL Terre is now ready to be deployed."
59 |
--------------------------------------------------------------------------------