procon_bypass_man-web 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.babelrc +6 -0
- data/.circleci/config.yml +73 -0
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.rubocop.yml +26 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +97 -0
- data/LICENSE.txt +21 -0
- data/Procfile +2 -0
- data/README.md +43 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/pbm_web +7 -0
- data/bin/setup +8 -0
- data/jest.config.ts +194 -0
- data/lib/procon_bypass_man/web.rb +20 -0
- data/lib/procon_bypass_man/web/db.rb +33 -0
- data/lib/procon_bypass_man/web/migration/001_create_settings_table.sql +4 -0
- data/lib/procon_bypass_man/web/models/base_model.rb +47 -0
- data/lib/procon_bypass_man/web/models/setting.rb +22 -0
- data/lib/procon_bypass_man/web/public/bundle.js +2 -0
- data/lib/procon_bypass_man/web/public/bundle.js.LICENSE.txt +57 -0
- data/lib/procon_bypass_man/web/public/index.html +1 -0
- data/lib/procon_bypass_man/web/server.rb +139 -0
- data/lib/procon_bypass_man/web/setting_parser.rb +190 -0
- data/lib/procon_bypass_man/web/storage.rb +25 -0
- data/lib/procon_bypass_man/web/version.rb +7 -0
- data/package.json +48 -0
- data/procon_bypass_man-web.gemspec +36 -0
- data/src/app.tsx +5 -0
- data/src/components/button_setting.tsx +142 -0
- data/src/components/buttons_modal.tsx +110 -0
- data/src/components/buttons_setting.tsx +67 -0
- data/src/components/installable_macros.tsx +58 -0
- data/src/components/installable_modes.tsx +57 -0
- data/src/components/macro_settings.tsx +85 -0
- data/src/components/mode_settings.tsx +62 -0
- data/src/contexts/buttons_setting.ts +2 -0
- data/src/index.html +11 -0
- data/src/lib/button_state.test.ts +110 -0
- data/src/lib/button_state.ts +52 -0
- data/src/lib/button_state_diff.test.ts +123 -0
- data/src/lib/button_state_diff.ts +63 -0
- data/src/lib/buttons_setting_converter.test.ts +185 -0
- data/src/lib/buttons_setting_converter.ts +107 -0
- data/src/lib/http_client.ts +93 -0
- data/src/pages/bpm_page.tsx +92 -0
- data/src/pages/buttons_setting_page.tsx +281 -0
- data/src/pages/global_setting_page.tsx +83 -0
- data/src/pages/home.tsx +17 -0
- data/src/pages/recoding_mode_page.tsx +15 -0
- data/src/pages/top.tsx +107 -0
- data/src/reducers/layer_reducer.ts +120 -0
- data/src/types/button.ts +2 -0
- data/src/types/buttons_setting_type.ts +63 -0
- data/src/types/layer_key.ts +2 -0
- data/src/types/pbm_stats.ts +1 -0
- data/src/types/plugin.ts +43 -0
- data/tmp/.keep +0 -0
- data/tsconfig.json +75 -0
- data/webpack.config.js +56 -0
- data/yarn.lock +6815 -0
- metadata +150 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
import { PbmStats } from "../types/pbm_stats";
|
3
|
+
import { Button } from "../types/button";
|
4
|
+
import { LayerKey } from "../types/layer_key";
|
5
|
+
import { ButtonsSettingType } from "../types/buttons_setting_type";
|
6
|
+
|
7
|
+
interface DirPathApiResponse {
|
8
|
+
result: string,
|
9
|
+
root_path: string,
|
10
|
+
}
|
11
|
+
|
12
|
+
interface SettingPathApiResponse {
|
13
|
+
result: string,
|
14
|
+
setting_path: string,
|
15
|
+
}
|
16
|
+
|
17
|
+
interface PostApiResponse {
|
18
|
+
result: string,
|
19
|
+
}
|
20
|
+
|
21
|
+
interface StatsApiResponse {
|
22
|
+
stats: PbmStats,
|
23
|
+
result: string,
|
24
|
+
pid: number | null,
|
25
|
+
}
|
26
|
+
|
27
|
+
export interface SettingApiResponse {
|
28
|
+
result: string,
|
29
|
+
setting: ButtonsSettingType,
|
30
|
+
setting_group_by_button: any,
|
31
|
+
installed_macros: Array<string>,
|
32
|
+
installed_modes: Array<string>,
|
33
|
+
}
|
34
|
+
|
35
|
+
interface SettingDigestApiResponse {
|
36
|
+
result: string,
|
37
|
+
digest: string,
|
38
|
+
}
|
39
|
+
|
40
|
+
export class HttpClient {
|
41
|
+
constructor() {
|
42
|
+
};
|
43
|
+
|
44
|
+
getDirPath() {
|
45
|
+
const path = "/api/pbm_root__path";
|
46
|
+
return axios.get<DirPathApiResponse>(`/api/pbm_root_path`);
|
47
|
+
}
|
48
|
+
|
49
|
+
postDirPath(dirPath: string) {
|
50
|
+
return axios.post<PostApiResponse>(`/api/pbm_root_path`, { root_path: dirPath });
|
51
|
+
}
|
52
|
+
|
53
|
+
getSettingPath() {
|
54
|
+
return axios.get<SettingPathApiResponse>( "/api/pbm_setting_path");
|
55
|
+
}
|
56
|
+
|
57
|
+
postSettingPath(settingPath: string) {
|
58
|
+
const path = "/api/pbm_setting_path"
|
59
|
+
return axios.post<PostApiResponse>(`${path}`, { setting_path: settingPath });
|
60
|
+
}
|
61
|
+
|
62
|
+
getPbmStats() {
|
63
|
+
const path = "/api/pbm_stats";
|
64
|
+
return axios.get<StatsApiResponse>(`${path}`);
|
65
|
+
}
|
66
|
+
|
67
|
+
startPbm() {
|
68
|
+
const path = "/api/start_pbm";
|
69
|
+
return axios.post<PostApiResponse>(`${path}`);
|
70
|
+
}
|
71
|
+
|
72
|
+
stopPbm() {
|
73
|
+
const path = "/api/stop_pbm";
|
74
|
+
return axios.post<PostApiResponse>(`${path}`);
|
75
|
+
}
|
76
|
+
|
77
|
+
reloadPbmSetting() {
|
78
|
+
const path = "/api/reload_pbm_setting";
|
79
|
+
return axios.post<PostApiResponse>(`${path}`);
|
80
|
+
}
|
81
|
+
|
82
|
+
getSetting() {
|
83
|
+
return axios.get<SettingApiResponse>("/api/pbm_setting");
|
84
|
+
}
|
85
|
+
|
86
|
+
postSetting(settingYaml: string) {
|
87
|
+
return axios.post<PostApiResponse>("/api/pbm_setting", { setting_yaml: settingYaml });
|
88
|
+
}
|
89
|
+
|
90
|
+
getSettingDigest() {
|
91
|
+
return axios.get<SettingDigestApiResponse>("/api/pbm_setting_digest");
|
92
|
+
}
|
93
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
/** @jsx jsx */
|
2
|
+
|
3
|
+
import { jsx, css } from '@emotion/react'
|
4
|
+
import React, { useState, useEffect } from "react";
|
5
|
+
import { HttpClient } from "../lib/http_client";
|
6
|
+
import { PbmStats } from "../types/pbm_stats";
|
7
|
+
|
8
|
+
type Prop = {
|
9
|
+
};
|
10
|
+
|
11
|
+
const httpClient = new HttpClient();
|
12
|
+
|
13
|
+
export const BpmPage= ({}:Prop) => {
|
14
|
+
const [pbmStats, setPbmStats] = useState("");
|
15
|
+
|
16
|
+
const handlePbmStats = (e: React.MouseEvent<HTMLElement>) => {
|
17
|
+
httpClient.getPbmStats()
|
18
|
+
.then(function (response) {
|
19
|
+
setPbmStats(`${response.data.stats}(${response.data.pid})`);
|
20
|
+
})
|
21
|
+
}
|
22
|
+
const handleStartPbm = (e: React.MouseEvent<HTMLElement>) => {
|
23
|
+
httpClient.startPbm()
|
24
|
+
.then(function (response) {
|
25
|
+
if(response.data.result === "ok") {
|
26
|
+
setPbmStats("waiting" as PbmStats);
|
27
|
+
} else {
|
28
|
+
setPbmStats("error" as PbmStats);
|
29
|
+
}
|
30
|
+
})
|
31
|
+
.catch(function (error) {
|
32
|
+
console.log(error);
|
33
|
+
})
|
34
|
+
}
|
35
|
+
|
36
|
+
const handleStopPbm = (e: React.MouseEvent<HTMLElement>) => {
|
37
|
+
httpClient.stopPbm()
|
38
|
+
.then(function (response) {
|
39
|
+
if(response.data.result === "ok") {
|
40
|
+
setPbmStats("waiting" as PbmStats);
|
41
|
+
} else {
|
42
|
+
setPbmStats("error" as PbmStats);
|
43
|
+
}
|
44
|
+
})
|
45
|
+
.catch(function (error) {
|
46
|
+
console.log(error);
|
47
|
+
})
|
48
|
+
}
|
49
|
+
|
50
|
+
const handleReloadPbmSetting = (e: React.MouseEvent<HTMLElement>) => {
|
51
|
+
httpClient.reloadPbmSetting()
|
52
|
+
.then(function (response) {
|
53
|
+
if(response.data.result === "ok") {
|
54
|
+
setPbmStats("stopped" as PbmStats);
|
55
|
+
} else {
|
56
|
+
setPbmStats("error" as PbmStats);
|
57
|
+
}
|
58
|
+
})
|
59
|
+
.catch(function (error) {
|
60
|
+
console.log(error);
|
61
|
+
})
|
62
|
+
}
|
63
|
+
|
64
|
+
const isShowRestartButton = () => {
|
65
|
+
return pbmStats == "running";
|
66
|
+
}
|
67
|
+
|
68
|
+
const isShowStartButton = () => {
|
69
|
+
return pbmStats == "stopped";
|
70
|
+
}
|
71
|
+
|
72
|
+
const isShowStopButton = () => {
|
73
|
+
return pbmStats == "running";
|
74
|
+
}
|
75
|
+
|
76
|
+
useEffect(() => {
|
77
|
+
httpClient.getPbmStats()
|
78
|
+
.then(function (response) {
|
79
|
+
setPbmStats(`${response.data.stats}(${response.data.pid})`);
|
80
|
+
})
|
81
|
+
}, [])
|
82
|
+
|
83
|
+
return (
|
84
|
+
<>
|
85
|
+
<h2>PBMのステータス: {pbmStats}</h2>
|
86
|
+
<input type="button" onClick={handlePbmStats} value="現在のステータスを取得する" />
|
87
|
+
{isShowStopButton() && <input type="button" onClick={handleStopPbm} value="停止する" />}
|
88
|
+
{isShowStartButton() && <input type="button" onClick={handleStartPbm} value="開始する" />}
|
89
|
+
{pbmStats === "running" && <input type="button" onClick={handleReloadPbmSetting} value="設定を再読み込みする" />}
|
90
|
+
</>
|
91
|
+
)
|
92
|
+
}
|
@@ -0,0 +1,281 @@
|
|
1
|
+
/** @jsx jsx */
|
2
|
+
|
3
|
+
import { jsx, css } from '@emotion/react'
|
4
|
+
import React, { useState, useEffect, useContext, useRef } from "react";
|
5
|
+
import { ButtonsSetting } from "../components/buttons_setting";
|
6
|
+
import { Button, buttons } from "../types/button";
|
7
|
+
import { LayerKey, layerKeys } from "../types/layer_key";
|
8
|
+
import { ButtonInLayer, ButtonsInLayer, ButtonsSettingType, Layers, Flip, Macro, StructMacro, ModeTable, StructMode } from "../types/buttons_setting_type";
|
9
|
+
import { HttpClient, SettingApiResponse } from "../lib/http_client";
|
10
|
+
import { ButtonState } from "./../lib/button_state";
|
11
|
+
import { ButtonStateDiff } from "./../lib/button_state_diff";
|
12
|
+
import { ButtonsSettingContext, } from "./../contexts/buttons_setting";
|
13
|
+
import { ButtonsSettingConverter } from "./../lib/buttons_setting_converter";
|
14
|
+
import { disableFlipType, alwaysFlipType, flipIfPressedSelfType, flipIfPressedSomeButtonsType, ignoreButtonsInFlipingType, remapType, closeMenuType, applyMacroType, installMacroType, installModeType, applyModeType } from "../reducers/layer_reducer";
|
15
|
+
import { ButtonsModal } from "../components/buttons_modal";
|
16
|
+
import { InstallableMacros } from "../components/installable_macros";
|
17
|
+
import { InstallableModes } from "../components/installable_modes";
|
18
|
+
import _ from 'lodash';
|
19
|
+
import md5 from 'md5';
|
20
|
+
|
21
|
+
const httpClient = new HttpClient();
|
22
|
+
|
23
|
+
interface LayerRef {
|
24
|
+
setVisibility(status: string): string;
|
25
|
+
};
|
26
|
+
|
27
|
+
export const ButtonsSettingPage = () => {
|
28
|
+
const { loaded, setLoaded, layers, layersDispatch, prefixKeys, setPrefixKeys } = useContext(ButtonsSettingContext);
|
29
|
+
const [selectedLayer, setSelectedLayer] = useState<LayerKey>("up");
|
30
|
+
const layerRefs = layerKeys.map((l) => ({} as LayerRef));
|
31
|
+
const [initializedSetting, setInitializedSetting] = useState({} as ButtonsSettingType)
|
32
|
+
const [infoMessage, setInfoMessage] = useState(undefined as undefined | string)
|
33
|
+
|
34
|
+
const switchLayer = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
35
|
+
if (event.target instanceof HTMLElement) {
|
36
|
+
setSelectedLayer(event.target.dataset.layerKey as LayerKey);
|
37
|
+
layerRefs.forEach(r => r.setVisibility("hidden"));
|
38
|
+
layerRefs[
|
39
|
+
Number(event.target.dataset.layerKeyIndex)
|
40
|
+
].setVisibility("shown");
|
41
|
+
}
|
42
|
+
}
|
43
|
+
const exportSetting = () => {
|
44
|
+
const body = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
|
45
|
+
var data = new Blob([body], { type: 'text/yaml' })
|
46
|
+
var csvURL = window.URL.createObjectURL(data);
|
47
|
+
const tempLink = document.createElement('a');
|
48
|
+
tempLink.href = csvURL;
|
49
|
+
tempLink.setAttribute('download', 'setting.yml');
|
50
|
+
tempLink.click();
|
51
|
+
}
|
52
|
+
const applySetting = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
53
|
+
e.preventDefault();
|
54
|
+
httpClient.postSetting(
|
55
|
+
ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
|
56
|
+
).then(
|
57
|
+
function (response) {
|
58
|
+
alert("設定ファイルのパスへ書き込みが完了しました");
|
59
|
+
reset();
|
60
|
+
});
|
61
|
+
}
|
62
|
+
const changes = (): Array<string> => {
|
63
|
+
return ButtonStateDiff({
|
64
|
+
before: initializedSetting,
|
65
|
+
after: { prefix_keys_for_changing_layer: prefixKeys, layers: layers },
|
66
|
+
})
|
67
|
+
}
|
68
|
+
const reset = () => {
|
69
|
+
setInitializedSetting({
|
70
|
+
prefix_keys_for_changing_layer: prefixKeys,
|
71
|
+
layers: _.cloneDeep(layers),
|
72
|
+
});
|
73
|
+
isUptodate()
|
74
|
+
}
|
75
|
+
const isUptodate = () => {
|
76
|
+
httpClient.getSettingDigest().then(function (response) {
|
77
|
+
const digest = md5(
|
78
|
+
ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
|
79
|
+
)
|
80
|
+
console.log("digest:", digest);
|
81
|
+
if(digest === response.data.digest) {
|
82
|
+
console.log("最新です")
|
83
|
+
setInfoMessage("設定が反映済みです。")
|
84
|
+
} else {
|
85
|
+
setInfoMessage("設定が未反映です. PBMの再起動が必要です。")
|
86
|
+
}
|
87
|
+
}).catch(function (error) {
|
88
|
+
if (error.response.status === 404) {
|
89
|
+
setInfoMessage("PBMを起動していません。")
|
90
|
+
} else {
|
91
|
+
console.log("想定外のエラーです");
|
92
|
+
}
|
93
|
+
})
|
94
|
+
}
|
95
|
+
|
96
|
+
useEffect(() => {
|
97
|
+
if (loaded) {
|
98
|
+
reset();
|
99
|
+
|
100
|
+
layerRefs[0].setVisibility("shown");
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
httpClient.getSetting()
|
105
|
+
.then(function (response) {
|
106
|
+
const body = response.data.setting_group_by_button as SettingApiResponse;
|
107
|
+
|
108
|
+
if(body.installed_macros) {
|
109
|
+
body.installed_macros.forEach((installed_macro: string) => {
|
110
|
+
layersDispatch({ type: installMacroType, payload: { installed_macro: installed_macro }});
|
111
|
+
})
|
112
|
+
}
|
113
|
+
|
114
|
+
if(body.installed_modes) {
|
115
|
+
body.installed_modes.forEach((modeName: string) => {
|
116
|
+
layersDispatch({ type: installModeType, payload: { installed_mode: modeName } });
|
117
|
+
})
|
118
|
+
}
|
119
|
+
|
120
|
+
setPrefixKeys(response.data.setting.prefix_keys_for_changing_layer);
|
121
|
+
const layers = layerKeys.reduce((a, key) => {
|
122
|
+
a[key] = response.data.setting_group_by_button.layers[key];
|
123
|
+
return a;
|
124
|
+
}, {} as Layers)
|
125
|
+
|
126
|
+
layerKeys.forEach((layerKey) => {
|
127
|
+
const macroTable = layers[layerKey].macro as Macro
|
128
|
+
if(macroTable) {
|
129
|
+
Object.entries(macroTable).forEach((val, i) => {
|
130
|
+
const name = val[0];
|
131
|
+
const ifPressed = val[1];
|
132
|
+
const macro = { name: name, if_pressed: ifPressed } as StructMacro
|
133
|
+
layersDispatch({ type: applyMacroType, payload: { layerKey: layerKey, macro: macro }});
|
134
|
+
})
|
135
|
+
}
|
136
|
+
const modeTable = layers[layerKey].mode as ModeTable;
|
137
|
+
if(modeTable) {
|
138
|
+
Object.entries(modeTable).forEach((val, i) => {
|
139
|
+
const name = val[0];
|
140
|
+
const mode = { name: name } as StructMode;
|
141
|
+
layersDispatch({ type: applyModeType, payload: { layerKey: layerKey, mode: mode }});
|
142
|
+
})
|
143
|
+
} else {
|
144
|
+
layersDispatch({ type: applyModeType, payload: { layerKey: layerKey, mode: { name: "disable" } }});
|
145
|
+
}
|
146
|
+
|
147
|
+
buttons.forEach((button) => {
|
148
|
+
const buttonState = new ButtonState(
|
149
|
+
button,
|
150
|
+
layers[layerKey][button]?.flip, layers[layerKey][button]?.remap,
|
151
|
+
);
|
152
|
+
const flip = layers[layerKey][button]?.flip || {} as Flip
|
153
|
+
|
154
|
+
if(layers[layerKey][button] === undefined) {
|
155
|
+
layersDispatch({ type: closeMenuType, payload: { layerKey: layerKey, button: button }});
|
156
|
+
} else if (layers[layerKey][button].remap?.to) {
|
157
|
+
layersDispatch({ type: remapType, payload: { layerKey: layerKey, button: button, targetButtons: layers[layerKey][button].remap?.to }});
|
158
|
+
} else if (buttonState.hasFlipSetting()) {
|
159
|
+
layersDispatch({ type: ignoreButtonsInFlipingType, payload: { layerKey: layerKey, button: button, targetButtons: flip.force_neutral }});
|
160
|
+
|
161
|
+
if(buttonState.isDisabledFlip()) {
|
162
|
+
layersDispatch({ type: disableFlipType, payload: { layerKey: layerKey, button: button }});
|
163
|
+
} else if (buttonState.isAlwaysFlip()) {
|
164
|
+
layersDispatch({ type: alwaysFlipType, payload: { layerKey: layerKey, button: button }});
|
165
|
+
} else if (buttonState.isFlipIfPressedSelf()) {
|
166
|
+
layersDispatch({ type: flipIfPressedSelfType, payload: { layerKey: layerKey, button: button }});
|
167
|
+
} else if (buttonState.isFlipIfPressedSomeButtons()) {
|
168
|
+
layersDispatch({ type: flipIfPressedSomeButtonsType, payload: { layerKey: layerKey, button: button, targetButtons: layers[layerKey][button].flip?.if_pressed }});
|
169
|
+
}
|
170
|
+
} else {
|
171
|
+
console.log("unexpectですです!!!!!!!!!!!!!");
|
172
|
+
}
|
173
|
+
});
|
174
|
+
});
|
175
|
+
|
176
|
+
setLoaded(true);
|
177
|
+
}).catch(function (error) {
|
178
|
+
if (error.response.status === 404) {
|
179
|
+
setInfoMessage("設定ファイルのパスが未設定です")
|
180
|
+
} else {
|
181
|
+
console.log("想定外のエラーです");
|
182
|
+
}
|
183
|
+
})
|
184
|
+
|
185
|
+
}, [loaded]);
|
186
|
+
|
187
|
+
const layersTabStyle = () => {
|
188
|
+
return css`
|
189
|
+
list-style: none;
|
190
|
+
display:flex;
|
191
|
+
margin: 0;
|
192
|
+
padding: 0;
|
193
|
+
border-left: 1px solid #aaa;
|
194
|
+
margin-bottom: 30px;
|
195
|
+
li {
|
196
|
+
border-top: 1px solid #aaa;
|
197
|
+
border-right: 1px solid #aaa;
|
198
|
+
&.active {
|
199
|
+
border-bottom: 1px solid #white;
|
200
|
+
}
|
201
|
+
&.inactive {
|
202
|
+
border-bottom: 1px solid #aaa;
|
203
|
+
}
|
204
|
+
a {
|
205
|
+
padding: 20px;
|
206
|
+
display: block;
|
207
|
+
&:hover {
|
208
|
+
cursor:pointer;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
`;
|
213
|
+
}
|
214
|
+
const liClassName = (layer: LayerKey) => {
|
215
|
+
if(layer === selectedLayer) {
|
216
|
+
return "active"
|
217
|
+
} else {
|
218
|
+
return "inactive"
|
219
|
+
};
|
220
|
+
}
|
221
|
+
const handlePrefixKeysField = () => {
|
222
|
+
setOpenModal(true)
|
223
|
+
setModalTitle("キープレフィックスの変更")
|
224
|
+
setModalPrefillButtons(prefixKeys);
|
225
|
+
setModalCallbackOnSubmit(() => setPrefixKeys);
|
226
|
+
setModalCloseCallback(() => setOpenModal);
|
227
|
+
}
|
228
|
+
|
229
|
+
// for modal
|
230
|
+
const [openModal, setOpenModal] = useState(false)
|
231
|
+
const [modalCallbackOnSubmit, setModalCallbackOnSubmit] = useState(undefined as any)
|
232
|
+
const [modalCloseCallback, setModalCloseCallback] = useState(undefined as any)
|
233
|
+
const [modalTitle, setModalTitle] = useState("")
|
234
|
+
const [modalPrefillButtons, setModalPrefillButtons] = useState<Array<Button>>([])
|
235
|
+
|
236
|
+
return(
|
237
|
+
<>
|
238
|
+
<div css={css`display: table`}>
|
239
|
+
<div css={css`display: table-cell; width: 400px;`}>
|
240
|
+
<h2>設定ファイルの変更</h2>
|
241
|
+
<div>
|
242
|
+
<a href="#" onClick={exportSetting}>エクスポートする</a>
|
243
|
+
</div>
|
244
|
+
|
245
|
+
<h3>インストール可能なマクロ</h3>
|
246
|
+
{loaded && <InstallableMacros />}
|
247
|
+
|
248
|
+
<h3>インストール可能なモード</h3>
|
249
|
+
{loaded && <InstallableModes />}
|
250
|
+
|
251
|
+
<h3>設定中のプレフィックスキー</h3>
|
252
|
+
<div css={css`position: relative; margin-bottom: 20px;`}>
|
253
|
+
<input type="text" value={prefixKeys.join(", ")} readOnly={true} onClick={handlePrefixKeysField} />
|
254
|
+
{openModal && <ButtonsModal callbackOnSubmit={modalCallbackOnSubmit} callbackOnClose={modalCloseCallback} title={modalTitle} prefill={modalPrefillButtons} positionOnShown={"stay"} />}
|
255
|
+
</div>
|
256
|
+
</div>
|
257
|
+
<div css={css`display: table-cell`}>
|
258
|
+
<h2></h2>
|
259
|
+
<div>
|
260
|
+
<a href="#" onClick={applySetting}>変更した設定でsetting.ymlへ上書きする</a>
|
261
|
+
<div>{infoMessage}</div>
|
262
|
+
<ul>
|
263
|
+
{loaded && changes().map((c, i) => <li key={i}>{c}</li>)}
|
264
|
+
</ul>
|
265
|
+
</div>
|
266
|
+
</div>
|
267
|
+
</div>
|
268
|
+
|
269
|
+
<ul css={layersTabStyle()}>
|
270
|
+
{layerKeys.map((l, index) => (
|
271
|
+
<li key={l} className={`${liClassName(l)}`}>
|
272
|
+
<a data-layer-key-index={index} data-layer-key={l} onClick={switchLayer}>{l}</a>
|
273
|
+
</li>
|
274
|
+
))}
|
275
|
+
</ul>
|
276
|
+
|
277
|
+
{loaded && layerKeys.map((l, index) => (<ButtonsSetting key={index} layerKey={l} layerRef={layerRefs[index]} />))}
|
278
|
+
{!loaded && "loading..."}
|
279
|
+
</>
|
280
|
+
)
|
281
|
+
}
|