danoniplus 47.6.0 → 47.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/danoni_main.js +134 -121
- package/js/lib/danoni_constants.js +3 -1
- package/package.json +1 -1
package/js/danoni_main.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Source by tickle
|
|
6
6
|
* Created : 2018/10/08
|
|
7
|
-
* Revised : 2026/05/
|
|
7
|
+
* Revised : 2026/05/14
|
|
8
8
|
*
|
|
9
9
|
* https://github.com/cwtickle/danoniplus
|
|
10
10
|
*/
|
|
11
|
-
const g_version = `Ver 47.6.
|
|
12
|
-
const g_revisedDate = `2026/05/
|
|
11
|
+
const g_version = `Ver 47.6.2`;
|
|
12
|
+
const g_revisedDate = `2026/05/14`;
|
|
13
13
|
|
|
14
14
|
// カスタム用バージョン (danoni_custom.js 等で指定可)
|
|
15
15
|
let g_localVersion = ``;
|
|
@@ -88,6 +88,7 @@ const g_remoteDomain = detectDomain(g_rootPath);
|
|
|
88
88
|
|
|
89
89
|
const g_randTime = Date.now();
|
|
90
90
|
const g_versionForUrl = g_version.slice(4); // URL用に先頭の"Ver "を削除
|
|
91
|
+
const g_dpr = window.devicePixelRatio || 1;
|
|
91
92
|
|
|
92
93
|
const g_isFile = location.href.match(/^file/);
|
|
93
94
|
const g_isLocal = location.href.match(/^file/) || location.href.indexOf(`localhost`) !== -1;
|
|
@@ -3380,7 +3381,6 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
3380
3381
|
_keyCtrlPtn,
|
|
3381
3382
|
config: {
|
|
3382
3383
|
scale: 1.5,
|
|
3383
|
-
dpr: window.devicePixelRatio || 1,
|
|
3384
3384
|
timeMargin: 35,
|
|
3385
3385
|
mmWidthBase: (g_sWidth - 500) / 2 + 290,
|
|
3386
3386
|
mmMarginY: 2,
|
|
@@ -3389,7 +3389,7 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
3389
3389
|
},
|
|
3390
3390
|
get logicalWidth() {
|
|
3391
3391
|
const logicalWidth = this.timeMargin + (this.laneWidth * keyNum);
|
|
3392
|
-
return Math.ceil(logicalWidth *
|
|
3392
|
+
return Math.ceil(logicalWidth * g_dpr) / g_dpr;
|
|
3393
3393
|
}
|
|
3394
3394
|
},
|
|
3395
3395
|
};
|
|
@@ -3403,16 +3403,15 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
3403
3403
|
* 指定された高さに基づいて分割されたCanvasリストを生成する
|
|
3404
3404
|
* @param {number} _width
|
|
3405
3405
|
* @param {number} _totalHeight
|
|
3406
|
-
* @param {number} _dpr
|
|
3407
3406
|
* @return {object[]} 分割されたCanvasとそのコンテキスト、オフセット情報を含むリスト
|
|
3408
3407
|
*/
|
|
3409
|
-
const createSplitCanvases = (_width, _totalHeight
|
|
3408
|
+
const createSplitCanvases = (_width, _totalHeight) => {
|
|
3410
3409
|
// バックバッファ(実際のピクセル数)の最大値を 8000 に設定(iOS Safari 8192px 対策)
|
|
3411
3410
|
const BACKING_STORE_LIMIT = 8000;
|
|
3412
3411
|
|
|
3413
3412
|
// 論理上の最大高さ(CSSピクセル)を計算
|
|
3414
|
-
//
|
|
3415
|
-
const maxLogicalHeight = Math.max(1, Math.floor(BACKING_STORE_LIMIT /
|
|
3413
|
+
// g_dpr=2なら4000px、g_dpr=3なら2666px が1枚の限界になる
|
|
3414
|
+
const maxLogicalHeight = Math.max(1, Math.floor(BACKING_STORE_LIMIT / g_dpr));
|
|
3416
3415
|
if (_totalHeight <= 0) return [];
|
|
3417
3416
|
|
|
3418
3417
|
const count = Math.ceil(_totalHeight / maxLogicalHeight);
|
|
@@ -3426,8 +3425,8 @@ const createSplitCanvases = (_width, _totalHeight, _dpr) => {
|
|
|
3426
3425
|
: maxLogicalHeight;
|
|
3427
3426
|
|
|
3428
3427
|
// 実際の描画解像度をセット
|
|
3429
|
-
cvs.width = _width *
|
|
3430
|
-
cvs.height = logicalH *
|
|
3428
|
+
cvs.width = _width * g_dpr;
|
|
3429
|
+
cvs.height = logicalH * g_dpr;
|
|
3431
3430
|
|
|
3432
3431
|
// ブラウザ上の表示サイズをセット
|
|
3433
3432
|
cvs.style.width = `${_width}px`;
|
|
@@ -3435,7 +3434,7 @@ const createSplitCanvases = (_width, _totalHeight, _dpr) => {
|
|
|
3435
3434
|
cvs.style.display = 'block';
|
|
3436
3435
|
|
|
3437
3436
|
const ctx = cvs.getContext('2d');
|
|
3438
|
-
ctx.scale(
|
|
3437
|
+
ctx.scale(g_dpr, g_dpr);
|
|
3439
3438
|
|
|
3440
3439
|
list.push({
|
|
3441
3440
|
canvas: cvs,
|
|
@@ -3474,7 +3473,6 @@ const distributeDrawing = (_canvases, _y, _h, _dpr, _drawFunc) => {
|
|
|
3474
3473
|
/**
|
|
3475
3474
|
* 譜面ミニマップ:キー名を表示するヘッダーキャンバスを作成する
|
|
3476
3475
|
* @param {object} _config ミニマップの基本設定
|
|
3477
|
-
* @param {number} _config.dpr デバイスピクセル比
|
|
3478
3476
|
* @param {number} _config.timeMargin 時間軸のマージン
|
|
3479
3477
|
* @param {number} _config.laneWidth レーンの幅
|
|
3480
3478
|
* @param {number} _config.logicalWidth キャンバスの論理幅
|
|
@@ -3483,20 +3481,20 @@ const distributeDrawing = (_canvases, _y, _h, _dpr, _drawFunc) => {
|
|
|
3483
3481
|
* @return {HTMLCanvasElement} ヘッダー用のキャンバス要素
|
|
3484
3482
|
*/
|
|
3485
3483
|
const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
3486
|
-
const {
|
|
3484
|
+
const { timeMargin, laneWidth, logicalWidth } = _config;
|
|
3487
3485
|
const headerHeight = 15; // ヘッダーの固定高
|
|
3488
3486
|
|
|
3489
3487
|
const canvas = document.createElement('canvas');
|
|
3490
3488
|
const ctx = canvas.getContext('2d');
|
|
3491
3489
|
|
|
3492
3490
|
// 解像度と表示サイズの設定
|
|
3493
|
-
canvas.width = logicalWidth *
|
|
3494
|
-
canvas.height = headerHeight *
|
|
3491
|
+
canvas.width = logicalWidth * g_dpr;
|
|
3492
|
+
canvas.height = headerHeight * g_dpr;
|
|
3495
3493
|
canvas.style.width = `${logicalWidth}px`;
|
|
3496
3494
|
canvas.style.height = `${headerHeight}px`;
|
|
3497
3495
|
canvas.style.display = 'block';
|
|
3498
3496
|
|
|
3499
|
-
ctx.scale(
|
|
3497
|
+
ctx.scale(g_dpr, g_dpr);
|
|
3500
3498
|
|
|
3501
3499
|
// テキストのスタイル設定
|
|
3502
3500
|
ctx.fillStyle = '#999';
|
|
@@ -3526,7 +3524,6 @@ const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
|
3526
3524
|
* @param {string} _params._keyCtrlPtn キーコントロールパターン
|
|
3527
3525
|
* @param {object} _params.config ミニマップの基本設定
|
|
3528
3526
|
* @param {number} _params.config.scale ミニマップの時間軸のスケール
|
|
3529
|
-
* @param {number} _params.config.dpr デバイスピクセル比
|
|
3530
3527
|
* @param {number} _params.config.timeMargin 時間軸のマージン
|
|
3531
3528
|
* @param {number} _params.config.laneWidth レーンの幅
|
|
3532
3529
|
* @param {number} _params.config.logicalWidth キャンバスの論理幅
|
|
@@ -3536,10 +3533,10 @@ const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
|
3536
3533
|
*/
|
|
3537
3534
|
const generateMinimapData = (_params, _isReverse) => {
|
|
3538
3535
|
const { _scoreObj, _keyNum, _playingFrame, _firstArrowFrame, _keyCtrlPtn, config } = _params;
|
|
3539
|
-
const { scale,
|
|
3536
|
+
const { scale, timeMargin, laneWidth, logicalWidth, mmMarginY } = config;
|
|
3540
3537
|
|
|
3541
3538
|
const mmHeightTotal = _playingFrame * scale + mmMarginY * 2;
|
|
3542
|
-
const canvases = createSplitCanvases(logicalWidth, mmHeightTotal
|
|
3539
|
+
const canvases = createSplitCanvases(logicalWidth, mmHeightTotal);
|
|
3543
3540
|
|
|
3544
3541
|
const getY = (frame) => {
|
|
3545
3542
|
const relativeFrame = frame - _firstArrowFrame;
|
|
@@ -3554,7 +3551,7 @@ const generateMinimapData = (_params, _isReverse) => {
|
|
|
3554
3551
|
const interval = g_fps;
|
|
3555
3552
|
for (let f = Math.ceil(_firstArrowFrame / interval) * interval; f <= _firstArrowFrame + _playingFrame; f += interval) {
|
|
3556
3553
|
const y = getY(f);
|
|
3557
|
-
distributeDrawing(canvases, y - 5, 10,
|
|
3554
|
+
distributeDrawing(canvases, y - 5, 10, g_dpr, (ctx) => {
|
|
3558
3555
|
ctx.strokeStyle = '#444';
|
|
3559
3556
|
ctx.fillStyle = '#999';
|
|
3560
3557
|
ctx.font = `10px ${getBasicFont()}`;
|
|
@@ -3583,7 +3580,7 @@ const generateMinimapData = (_params, _isReverse) => {
|
|
|
3583
3580
|
const top = Math.min(y1, y2);
|
|
3584
3581
|
const h = Math.abs(y2 - y1);
|
|
3585
3582
|
const x = timeMargin + j * laneWidth;
|
|
3586
|
-
distributeDrawing(canvases, top, h,
|
|
3583
|
+
distributeDrawing(canvases, top, h, g_dpr, (ctx) => {
|
|
3587
3584
|
ctx.fillStyle = 'rgba(0, 200, 255, 0.4)';
|
|
3588
3585
|
ctx.fillRect(x + 2, top, laneWidth - 3, h);
|
|
3589
3586
|
ctx.strokeStyle = 'rgba(0, 200, 255, 0.8)';
|
|
@@ -3597,7 +3594,7 @@ const generateMinimapData = (_params, _isReverse) => {
|
|
|
3597
3594
|
const color = g_dfColorObj.setColorType2[g_keyObj[`color${_keyCtrlPtn}_0`][j]] || '#ffffff';
|
|
3598
3595
|
_scoreObj.arrowData[j].forEach(note => {
|
|
3599
3596
|
const y = getY(parseFloat(note));
|
|
3600
|
-
distributeDrawing(canvases, y - 1.5, 3,
|
|
3597
|
+
distributeDrawing(canvases, y - 1.5, 3, g_dpr, (ctx) => {
|
|
3601
3598
|
ctx.fillStyle = color;
|
|
3602
3599
|
ctx.fillRect(timeMargin + j * laneWidth + 1, y - 1.5, laneWidth - 1, 3);
|
|
3603
3600
|
});
|
|
@@ -8239,9 +8236,8 @@ const createOptionWindow = _sprite => {
|
|
|
8239
8236
|
const bkColor = window.getComputedStyle(textBaseObj, ``).backgroundColor;
|
|
8240
8237
|
|
|
8241
8238
|
graphObj.id = `graph${_name}${j > 0 ? j + 1 : ``}`;
|
|
8242
|
-
|
|
8243
|
-
graphObj.
|
|
8244
|
-
graphObj.height = g_limitObj.graphHeight * dpr;
|
|
8239
|
+
graphObj.width = g_limitObj.graphWidth * g_dpr;
|
|
8240
|
+
graphObj.height = g_limitObj.graphHeight * g_dpr;
|
|
8245
8241
|
graphObj.style.width = wUnit(g_limitObj.graphWidth);
|
|
8246
8242
|
graphObj.style.height = wUnit(g_limitObj.graphHeight);
|
|
8247
8243
|
graphObj.style.left = wUnit(125);
|
|
@@ -8249,7 +8245,7 @@ const createOptionWindow = _sprite => {
|
|
|
8249
8245
|
graphObj.style.position = `absolute`;
|
|
8250
8246
|
graphObj.style.background = j === 0 ? bkColor : `#ffffff00`;
|
|
8251
8247
|
const ctx = graphObj.getContext(`2d`);
|
|
8252
|
-
ctx.scale(
|
|
8248
|
+
ctx.scale(g_dpr, g_dpr);
|
|
8253
8249
|
|
|
8254
8250
|
detailObj.appendChild(graphObj);
|
|
8255
8251
|
}
|
|
@@ -10396,8 +10392,11 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10396
10392
|
altFill: `#3e3e1a`, // 代替キー背景
|
|
10397
10393
|
altStroke: `#777755`, // 代替キー枠
|
|
10398
10394
|
altText: `#eeeecc`, // 代替キー文字
|
|
10395
|
+
shortcutFill: `#330011`, // ショートカットキー背景
|
|
10396
|
+
shortcutStroke: `#ff4466`, // ショートカットキー枠
|
|
10397
|
+
shortcutText: `#ffaacc`, // ショートカットキー文字
|
|
10399
10398
|
bgFill: `#0d0d1a`, // Canvas 背景
|
|
10400
|
-
legendText: `#
|
|
10399
|
+
legendText: `#cccccc`, // 凡例テキスト
|
|
10401
10400
|
};
|
|
10402
10401
|
|
|
10403
10402
|
// ボタン配置
|
|
@@ -10418,35 +10417,20 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10418
10417
|
// キーレイアウト定義
|
|
10419
10418
|
//
|
|
10420
10419
|
// 各行: { offsetX, keys }
|
|
10421
|
-
// offsetX : 行左端の水平オフセット(単位: BASE_KEY_W
|
|
10420
|
+
// offsetX : 行左端の水平オフセット(単位: BASE_KEY_W)。全行 0 で統一し、
|
|
10421
|
+
// L)Shift の幅で行頭位置を調整する。
|
|
10422
10422
|
// keys : キー定義の配列
|
|
10423
10423
|
//
|
|
10424
|
-
// 各キー: { kc, w
|
|
10424
|
+
// 各キー: { kc, w?, h?, label? }
|
|
10425
10425
|
// kc : keyCode(数値)。-1 はスペーサー(描画・キャッシュなし)。
|
|
10426
10426
|
// w : 幅倍率(BASE_KEY_W 基準。省略時 1)
|
|
10427
|
-
// h :
|
|
10427
|
+
// h : 高さ倍率(BASE_KEY_H 基準。省略時 1)
|
|
10428
10428
|
// label : 省略時は g_kCd[kc] を参照。g_kCd が空文字のキーや
|
|
10429
10429
|
// 左右を区別したいキーに指定する。
|
|
10430
10430
|
//
|
|
10431
10431
|
// 右Shift/Ctrl/Alt は danoniplus 独自コード 256〜258 を使用。
|
|
10432
|
+
// Appli キーは 93 を使用(g_kCd[93] = `Appli`)。
|
|
10432
10433
|
// -------------------------------------------------------------------------
|
|
10433
|
-
|
|
10434
|
-
// メインキーボード部(Fn行 + 数字行 + QWERTY + ASDF + ZXCV + スペース行)
|
|
10435
|
-
//
|
|
10436
|
-
// 各行の offsetX 設計(1u = BASE_KEY_W):
|
|
10437
|
-
// Fn行 0u Esc が左端
|
|
10438
|
-
// 数字行 0u 229(IME/`) が左端
|
|
10439
|
-
// QWERTY 0u Tab(1.5u) が左端、左端位置は数字行と揃う
|
|
10440
|
-
// ASDF 0u CapsLk を 2.25u にして L)Shift 左端と揃える
|
|
10441
|
-
// ZXCV 0.25u 標準キーボードの行オフセットを offsetX + L)Shift 拡大で再現
|
|
10442
|
-
// スペース行 0u
|
|
10443
|
-
//
|
|
10444
|
-
// JIS と US の主な違い:
|
|
10445
|
-
// 数字行: JIS は intlYen(220) あり、US はなし(BackSpace が広い)
|
|
10446
|
-
// QWERTY: JIS は [ の右にスペーサー、US はなし(Enter が横長)
|
|
10447
|
-
// ASDF : JIS は ¥(221) あり、US はなし(Enter が横長)
|
|
10448
|
-
// ZXCV : JIS は intlRo(226) あり、US はなし(R)Shift が広い)
|
|
10449
|
-
// Enter : JIS は縦長(h=2)、US は横長(h=1, w=2.25)
|
|
10450
10434
|
/**
|
|
10451
10435
|
* g_localeObj.val に応じた MAIN_ROWS を生成して返す。
|
|
10452
10436
|
* drawBase / calcScale の都度呼び出し、locale 変化を反映する。
|
|
@@ -10460,18 +10444,18 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10460
10444
|
{
|
|
10461
10445
|
offsetX: 0,
|
|
10462
10446
|
keys: [
|
|
10463
|
-
{ kc: 27 },
|
|
10464
|
-
{ kc: -1, w: 0.5 },
|
|
10447
|
+
{ kc: 27 }, // Esc
|
|
10448
|
+
{ kc: -1, w: 0.5 }, // スペーサー
|
|
10465
10449
|
{ kc: 112 }, { kc: 113 }, { kc: 114 }, { kc: 115 },
|
|
10466
|
-
{ kc: -1, w: 0.25 },
|
|
10450
|
+
{ kc: -1, w: 0.25 }, // スペーサー
|
|
10467
10451
|
{ kc: 116 }, { kc: 117 }, { kc: 118 }, { kc: 119 },
|
|
10468
|
-
{ kc: -1, w: 0.25 },
|
|
10452
|
+
{ kc: -1, w: 0.25 }, // スペーサー
|
|
10469
10453
|
{ kc: 120 }, { kc: 121 }, { kc: 122 }, { kc: 123 },
|
|
10470
10454
|
],
|
|
10471
10455
|
},
|
|
10472
10456
|
// Row1: 数字行
|
|
10473
|
-
// JIS: ...,
|
|
10474
|
-
// US : ...,
|
|
10457
|
+
// JIS: ..., 220(intlYen), BS
|
|
10458
|
+
// US : ..., BS
|
|
10475
10459
|
{
|
|
10476
10460
|
offsetX: 0,
|
|
10477
10461
|
keys: [
|
|
@@ -10481,13 +10465,13 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10481
10465
|
{ kc: 55 }, { kc: 56 }, { kc: 57 },
|
|
10482
10466
|
{ kc: 48 }, { kc: 189 }, { kc: 222 },
|
|
10483
10467
|
...(isJa
|
|
10484
|
-
? [{ kc: 220, w: 0.75 }, { kc: 8,
|
|
10485
|
-
: [{ kc: 8, w:
|
|
10468
|
+
? [{ kc: 220, w: 0.75 }, { kc: 8, label: `Back\nSpace` }] // JIS: intlYen + BS
|
|
10469
|
+
: [{ kc: 8, w: 1.7 }] // US : BS のみ(広い)
|
|
10486
10470
|
),
|
|
10487
10471
|
],
|
|
10488
10472
|
},
|
|
10489
10473
|
// Row2: QWERTY
|
|
10490
|
-
// JIS: ..., [,
|
|
10474
|
+
// JIS: ..., [, Enter(13)
|
|
10491
10475
|
// US : ..., [, ]
|
|
10492
10476
|
{
|
|
10493
10477
|
offsetX: 0,
|
|
@@ -10498,46 +10482,49 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10498
10482
|
{ kc: 85 }, { kc: 73 }, { kc: 79 },
|
|
10499
10483
|
{ kc: 80 }, { kc: 192 },
|
|
10500
10484
|
...(isJa
|
|
10501
|
-
? [{ kc: 219 }, { kc:
|
|
10502
|
-
: [{ kc: 219 }, { kc: 221 }]
|
|
10485
|
+
? [{ kc: 219 }, { kc: 13, w: 1.25, h: 2 }] // JIS: [, Enter縦長
|
|
10486
|
+
: [{ kc: 219 }, { kc: 221, w: 1.2 }] // US : [, ]
|
|
10503
10487
|
),
|
|
10504
10488
|
],
|
|
10505
10489
|
},
|
|
10506
10490
|
// Row3: ASDF
|
|
10507
10491
|
// JIS: ..., L, ;, ', ¥(221)
|
|
10508
|
-
// US : ..., L, ;, '
|
|
10492
|
+
// US : ..., L, ;, ', Enter(13)
|
|
10509
10493
|
{
|
|
10510
10494
|
offsetX: 0,
|
|
10511
10495
|
keys: [
|
|
10512
|
-
{ kc: 20, w:
|
|
10496
|
+
{ kc: 20, w: 1.75, label: `CapsLk` },
|
|
10513
10497
|
{ kc: 65 }, { kc: 83 }, { kc: 68 },
|
|
10514
10498
|
{ kc: 70 }, { kc: 71 }, { kc: 72 },
|
|
10515
10499
|
{ kc: 74 }, { kc: 75 }, { kc: 76 },
|
|
10516
10500
|
{ kc: 187 }, { kc: 186 },
|
|
10517
10501
|
...(isJa
|
|
10518
|
-
? [{ kc: 221 }]
|
|
10519
|
-
: [{ kc: 13, w:
|
|
10502
|
+
? [{ kc: 221 }] // JIS: ¥
|
|
10503
|
+
: [{ kc: 13, w: 1.9 }] // US : Enter横長
|
|
10520
10504
|
),
|
|
10521
10505
|
],
|
|
10522
10506
|
},
|
|
10523
|
-
// Row4: ZXCV
|
|
10524
|
-
//
|
|
10525
|
-
//
|
|
10507
|
+
// Row4: ZXCV
|
|
10508
|
+
// L)Shift の幅で行頭位置を揃える
|
|
10509
|
+
// JIS: L)Shift, ..., intlRo(226), R)Shift
|
|
10510
|
+
// US : L)Shift, ..., R)Shift
|
|
10526
10511
|
{
|
|
10527
|
-
offsetX: 0
|
|
10512
|
+
offsetX: 0,
|
|
10528
10513
|
keys: [
|
|
10529
|
-
{ kc: 16, w: 2.
|
|
10514
|
+
{ kc: 16, w: 2.25 },
|
|
10530
10515
|
{ kc: 90 }, { kc: 88 }, { kc: 67 },
|
|
10531
10516
|
{ kc: 86 }, { kc: 66 }, { kc: 78 },
|
|
10532
10517
|
{ kc: 77 }, { kc: 188 }, { kc: 190 },
|
|
10533
10518
|
{ kc: 191 },
|
|
10534
10519
|
...(isJa
|
|
10535
|
-
? [{ kc: 226 }, { kc: 256, w: 1.
|
|
10536
|
-
: [{ kc: 256, w: 2.
|
|
10520
|
+
? [{ kc: 226 }, { kc: 256, w: 1.5 }] // JIS: intlRo + R)Shift
|
|
10521
|
+
: [{ kc: 256, w: 2.4 }] // US : R)Shift のみ(広い)
|
|
10537
10522
|
),
|
|
10538
10523
|
],
|
|
10539
10524
|
},
|
|
10540
|
-
// Row5:
|
|
10525
|
+
// Row5: スペースバー行
|
|
10526
|
+
// JIS: ..., NoConv(29), Space, Conv(28), カタカナひらがな(242), ...
|
|
10527
|
+
// US : ..., Space, ...
|
|
10541
10528
|
{
|
|
10542
10529
|
offsetX: 0,
|
|
10543
10530
|
keys: [
|
|
@@ -10556,38 +10543,41 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10556
10543
|
]
|
|
10557
10544
|
),
|
|
10558
10545
|
{ kc: 258 },
|
|
10559
|
-
{ kc:
|
|
10560
|
-
|
|
10546
|
+
{ kc: 93 },
|
|
10547
|
+
...(isJa
|
|
10548
|
+
? [{ kc: 257, w: 1.2 }]
|
|
10549
|
+
: [{ kc: 257, w: 1.05 }]
|
|
10550
|
+
),
|
|
10561
10551
|
],
|
|
10562
10552
|
},
|
|
10563
10553
|
];
|
|
10564
10554
|
};
|
|
10565
|
-
// 編集キークラスター(Insert/Delete/Home/End/PgUp/PgDn + 矢印キー)
|
|
10555
|
+
// 編集キークラスター(PrintSc/ScrollLk/Pause/Insert/Delete/Home/End/PgUp/PgDn + 矢印キー)
|
|
10566
10556
|
// MAIN_ROWS と行インデックスを揃えて配置する。空行はスキップされる。
|
|
10567
10557
|
const NAV_ROWS = [
|
|
10568
10558
|
{ offsetX: 0, keys: [{ kc: 44, label: `PrintSc` }, { kc: 145, label: `ScrollLk` }, { kc: 19 }] }, // PrintSc ScrollLk Pause
|
|
10569
10559
|
{ offsetX: 0, keys: [{ kc: 45 }, { kc: 36 }, { kc: 33 }] }, // Insert Home PgUp
|
|
10570
10560
|
{ offsetX: 0, keys: [{ kc: 46 }, { kc: 35 }, { kc: 34 }] }, // Delete End PgDn
|
|
10571
|
-
{ offsetX: 0, keys: [] },
|
|
10561
|
+
{ offsetX: 0, keys: [] }, // ASDF行:空
|
|
10572
10562
|
{ offsetX: 0, keys: [{ kc: -1 }, { kc: 38 }, { kc: -1 }] }, // ↑
|
|
10573
10563
|
{ offsetX: 0, keys: [{ kc: 37 }, { kc: 40 }, { kc: 39 }] }, // ← ↓ →
|
|
10574
10564
|
];
|
|
10575
10565
|
|
|
10576
|
-
// テンキー(
|
|
10566
|
+
// テンキー(MAIN_ROWS と行インデックスを揃えて配置。1行目は空行で Fn行に揃える)
|
|
10577
10567
|
// kc は g_kCd 定義に従う: 96〜111=テンキー各種, 144=NumLk
|
|
10578
|
-
//
|
|
10568
|
+
// 標準テンキーレイアウト(2行目から):
|
|
10579
10569
|
// [NumLk] [T/] [T*] [T-]
|
|
10580
10570
|
// [T7][T8][T9] [T+]
|
|
10581
10571
|
// [T4][T5][T6] [T+] ← T+ は縦2u
|
|
10582
10572
|
// [T1][T2][T3] [TEnter]
|
|
10583
|
-
// [ T0 ][
|
|
10573
|
+
// [ T0 ][T.] [TEnter] ← T0 は横2u、TEnter は縦2u
|
|
10584
10574
|
const NUM_ROWS = [
|
|
10585
10575
|
{ offsetX: 0, keys: [] },
|
|
10586
|
-
{ offsetX: 0, keys: [{ kc: 144 }, { kc: 111 }, { kc: 106 }, { kc: 109 }] },
|
|
10576
|
+
{ offsetX: 0, keys: [{ kc: 144 }, { kc: 111 }, { kc: 106 }, { kc: 109 }] }, // NumLk T/ T* T-
|
|
10587
10577
|
{ offsetX: 0, keys: [{ kc: 103 }, { kc: 104 }, { kc: 105 }, { kc: 107, h: 2 }] }, // T7 T8 T9 T+(縦2u)
|
|
10588
|
-
{ offsetX: 0, keys: [{ kc: 100 }, { kc: 101 }, { kc: 102 }] },
|
|
10589
|
-
{ offsetX: 0, keys: [{ kc: 97 }, { kc: 98 }, { kc: 99 }, { kc: 108, h: 2 }] },
|
|
10590
|
-
{ offsetX: 0, keys: [{ kc: 96, w: 2 }, { kc: 110 }] },
|
|
10578
|
+
{ offsetX: 0, keys: [{ kc: 100 }, { kc: 101 }, { kc: 102 }] }, // T4 T5 T6
|
|
10579
|
+
{ offsetX: 0, keys: [{ kc: 97 }, { kc: 98 }, { kc: 99 }, { kc: 108, h: 2 }] }, // T1 T2 T3 TEnter(縦2u)
|
|
10580
|
+
{ offsetX: 0, keys: [{ kc: 96, w: 2 }, { kc: 110 }] }, // T0(横2u) T.
|
|
10591
10581
|
];
|
|
10592
10582
|
|
|
10593
10583
|
// -------------------------------------------------------------------------
|
|
@@ -10595,14 +10585,15 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10595
10585
|
// -------------------------------------------------------------------------
|
|
10596
10586
|
const _state = {
|
|
10597
10587
|
visible: false,
|
|
10598
|
-
mappedSet: new Set(),
|
|
10599
|
-
altSet: new Set(),
|
|
10588
|
+
mappedSet: new Set(), // メインキー(各矢印の index 0)
|
|
10589
|
+
altSet: new Set(), // 代替キー(各矢印の index 1 以降)
|
|
10590
|
+
shortcutSet: new Set(), // プレイ中ショートカット(keyRetry / keyTitleBack / PgDn / PgUp)
|
|
10600
10591
|
canvasBase: null,
|
|
10601
10592
|
canvasMap: null,
|
|
10602
|
-
keyRects: [],
|
|
10603
|
-
scale: 1,
|
|
10604
|
-
cvsW: 500,
|
|
10605
|
-
cvsH: 240,
|
|
10593
|
+
keyRects: [], // { kc, x, y, w, h, label } — drawMap で照合するキャッシュ
|
|
10594
|
+
scale: 1, // BASE_KEY_W/H に掛けるスケール係数
|
|
10595
|
+
cvsW: 500, // 実際の Canvas 幅(スケール計算後)
|
|
10596
|
+
cvsH: 240, // 実際の Canvas 高さ(スケール計算後)
|
|
10606
10597
|
};
|
|
10607
10598
|
|
|
10608
10599
|
// -------------------------------------------------------------------------
|
|
@@ -10629,7 +10620,7 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10629
10620
|
|
|
10630
10621
|
const BASE_NAV_W = 3 * BASE_KEY_W + 2 * BASE_KEY_GAP; // NAV は 3列固定
|
|
10631
10622
|
const BASE_ROW_H = MAIN_ROWS_LEN * (BASE_KEY_H + BASE_KEY_GAP) - BASE_KEY_GAP; // MAIN+NAV 分の高さ
|
|
10632
|
-
const NUM_ROWS_LEN = 6; //
|
|
10623
|
+
const NUM_ROWS_LEN = 6; // テンキーの行数(1行目は空行、2行目からテンキー配置)
|
|
10633
10624
|
const NUM_GAP_H = BASE_KEY_H * 0.4; // テンキー上部の余白(基準キー高の40%)
|
|
10634
10625
|
const BASE_NUM_ROW_H = NUM_ROWS_LEN * (BASE_KEY_H + BASE_KEY_GAP) - BASE_KEY_GAP; // テンキー部の高さ
|
|
10635
10626
|
const BASE_NUM_W = 4 * BASE_KEY_W + 3 * BASE_KEY_GAP; // テンキー横幅(4列固定)
|
|
@@ -10674,7 +10665,7 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10674
10665
|
if (forcedLabel !== undefined) return [forcedLabel, ``];
|
|
10675
10666
|
|
|
10676
10667
|
const raw = g_kCd[kc];
|
|
10677
|
-
if (raw && raw !==
|
|
10668
|
+
if (raw && raw !== g_kCd[0] && raw !== g_kCd[1]) {
|
|
10678
10669
|
const parts = raw.split(` `);
|
|
10679
10670
|
return [parts[0] || ``, parts[1] || ``];
|
|
10680
10671
|
}
|
|
@@ -10706,22 +10697,31 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10706
10697
|
};
|
|
10707
10698
|
|
|
10708
10699
|
const drawKeyLabel = (ctx, x, y, keyW, keyH, primary, sub, textColor, subColor) => {
|
|
10709
|
-
const fs =
|
|
10710
|
-
? Math.max(6, Math.floor(
|
|
10700
|
+
const fs = (_textLen) => _textLen >= 5 * keyW / BASE_KEY_W
|
|
10701
|
+
? Math.max(6, Math.floor(9 * _state.scale))
|
|
10711
10702
|
: Math.max(7, Math.floor(11 * _state.scale));
|
|
10712
10703
|
|
|
10713
10704
|
if (sub) {
|
|
10714
10705
|
ctx.fillStyle = subColor;
|
|
10715
|
-
ctx.font = `bold ${Math.max(6, Math.floor(
|
|
10706
|
+
ctx.font = `bold ${Math.max(6, Math.floor(9 * _state.scale))}px monospace`;
|
|
10716
10707
|
ctx.textAlign = `right`;
|
|
10717
10708
|
ctx.textBaseline = `top`;
|
|
10718
10709
|
ctx.fillText(sub, x + keyW - 2, y + 2);
|
|
10719
10710
|
}
|
|
10711
|
+
const [primary1, primary2] = primary.split(`\n`);
|
|
10720
10712
|
ctx.fillStyle = textColor;
|
|
10721
|
-
ctx.font = `bold ${fs}px monospace`;
|
|
10722
10713
|
ctx.textAlign = `center`;
|
|
10723
10714
|
ctx.textBaseline = `middle`;
|
|
10724
|
-
|
|
10715
|
+
const subDiff = sub ? 2 : 0;
|
|
10716
|
+
if (primary2) {
|
|
10717
|
+
const siz = fs(Math.max(primary1.length, primary2.length));
|
|
10718
|
+
ctx.font = `bold ${siz}px monospace`;
|
|
10719
|
+
ctx.fillText(primary1, x + keyW / 2, y + keyH / 2 - siz / 2 + subDiff);
|
|
10720
|
+
ctx.fillText(primary2, x + keyW / 2, y + keyH / 2 + siz / 2 + subDiff);
|
|
10721
|
+
} else {
|
|
10722
|
+
ctx.font = `bold ${fs(primary.length)}px monospace`;
|
|
10723
|
+
ctx.fillText(primary, x + keyW / 2, y + keyH / 2 + subDiff);
|
|
10724
|
+
}
|
|
10725
10725
|
};
|
|
10726
10726
|
|
|
10727
10727
|
const drawOneKey = (ctx, x, y, keyW, keyH, fill, stroke, lw, primary, sub, textColor, subColor) => {
|
|
@@ -10792,15 +10792,14 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10792
10792
|
const canvas = _state.canvasBase;
|
|
10793
10793
|
if (!canvas) return;
|
|
10794
10794
|
|
|
10795
|
-
const dpr = window.devicePixelRatio || 1;
|
|
10796
10795
|
canvas.style.top = wUnit(40);
|
|
10797
|
-
canvas.width = _state.cvsW *
|
|
10798
|
-
canvas.height = _state.cvsH *
|
|
10796
|
+
canvas.width = _state.cvsW * g_dpr;
|
|
10797
|
+
canvas.height = _state.cvsH * g_dpr;
|
|
10799
10798
|
canvas.style.width = wUnit(_state.cvsW);
|
|
10800
10799
|
canvas.style.height = wUnit(_state.cvsH);
|
|
10801
10800
|
|
|
10802
10801
|
const ctx = canvas.getContext(`2d`);
|
|
10803
|
-
ctx.scale(
|
|
10802
|
+
ctx.scale(g_dpr, g_dpr);
|
|
10804
10803
|
ctx.clearRect(0, 0, _state.cvsW, _state.cvsH);
|
|
10805
10804
|
ctx.fillStyle = C_COLOR.bgFill;
|
|
10806
10805
|
ctx.fillRect(0, 0, _state.cvsW, _state.cvsH);
|
|
@@ -10827,22 +10826,25 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10827
10826
|
|
|
10828
10827
|
// 凡例
|
|
10829
10828
|
const ly = _state.cvsH - 10;
|
|
10830
|
-
const fnt = `${Math.max(9, Math.floor(
|
|
10829
|
+
const fnt = `${Math.max(9, Math.floor(12 * _state.scale))}px ${getBasicFont()}`;
|
|
10831
10830
|
ctx.font = fnt;
|
|
10832
10831
|
ctx.textAlign = `left`;
|
|
10833
10832
|
ctx.textBaseline = `middle`;
|
|
10834
10833
|
|
|
10835
10834
|
const drawLegend = (x, fill, stroke, label) => {
|
|
10836
|
-
roundRect(ctx, x, ly, 10, 10, 2);
|
|
10835
|
+
roundRect(ctx, x, ly - 5, 10, 10, 2);
|
|
10837
10836
|
ctx.fillStyle = fill; ctx.fill();
|
|
10838
10837
|
ctx.strokeStyle = stroke; ctx.lineWidth = 1; ctx.stroke();
|
|
10839
10838
|
ctx.fillStyle = C_COLOR.legendText;
|
|
10840
|
-
ctx.fillText(label, x + 14, ly
|
|
10841
|
-
};
|
|
10839
|
+
ctx.fillText(label, x + 14, ly);
|
|
10842
10840
|
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10841
|
+
return 14 + ctx.measureText(label).width + 14;
|
|
10842
|
+
};
|
|
10843
|
+
let lx = 8;
|
|
10844
|
+
lx += drawLegend(lx, C_COLOR.keyFill, C_COLOR.keyStroke, g_lblNameObj.unallocated);
|
|
10845
|
+
lx += drawLegend(lx, C_COLOR.mappedFill, C_COLOR.mappedStroke, g_lblNameObj.allocated);
|
|
10846
|
+
lx += drawLegend(lx, C_COLOR.altFill, C_COLOR.altStroke, g_lblNameObj.altAllocated);
|
|
10847
|
+
lx += drawLegend(lx, C_COLOR.shortcutFill, C_COLOR.shortcutStroke, g_lblNameObj.shortcutKey);
|
|
10846
10848
|
};
|
|
10847
10849
|
|
|
10848
10850
|
/**
|
|
@@ -10854,18 +10856,17 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10854
10856
|
const canvas = _state.canvasMap;
|
|
10855
10857
|
if (!canvas) return;
|
|
10856
10858
|
|
|
10857
|
-
const dpr = window.devicePixelRatio || 1;
|
|
10858
10859
|
canvas.style.top = wUnit(40);
|
|
10859
|
-
canvas.width = _state.cvsW *
|
|
10860
|
-
canvas.height = _state.cvsH *
|
|
10860
|
+
canvas.width = _state.cvsW * g_dpr;
|
|
10861
|
+
canvas.height = _state.cvsH * g_dpr;
|
|
10861
10862
|
canvas.style.width = wUnit(_state.cvsW);
|
|
10862
10863
|
canvas.style.height = wUnit(_state.cvsH);
|
|
10863
10864
|
|
|
10864
10865
|
const ctx = canvas.getContext(`2d`);
|
|
10865
|
-
ctx.scale(
|
|
10866
|
+
ctx.scale(g_dpr, g_dpr);
|
|
10866
10867
|
ctx.clearRect(0, 0, _state.cvsW, _state.cvsH);
|
|
10867
10868
|
|
|
10868
|
-
//
|
|
10869
|
+
// 優先度: ショートカット > メイン > 代替(後から描くほど優先)
|
|
10869
10870
|
const drawKey = (fill, stroke, text) => rect => {
|
|
10870
10871
|
const [primary, sub] = getKeyLabels(rect.kc, rect.label);
|
|
10871
10872
|
roundRect(ctx, rect.x + 0.5, rect.y + 0.5, rect.w - 1, rect.h - 1, kr());
|
|
@@ -10877,13 +10878,22 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10877
10878
|
drawKeyLabel(ctx, rect.x, rect.y, rect.w, rect.h, primary, sub, text, text);
|
|
10878
10879
|
};
|
|
10879
10880
|
|
|
10881
|
+
// 1. 代替キー(メイン・ショートカットと重複しない場合のみ)
|
|
10880
10882
|
_state.keyRects
|
|
10881
|
-
.filter(rect => _state.altSet.has(rect.kc)
|
|
10883
|
+
.filter(rect => _state.altSet.has(rect.kc)
|
|
10884
|
+
&& !_state.mappedSet.has(rect.kc)
|
|
10885
|
+
&& !_state.shortcutSet.has(rect.kc))
|
|
10882
10886
|
.forEach(drawKey(C_COLOR.altFill, C_COLOR.altStroke, C_COLOR.altText));
|
|
10883
10887
|
|
|
10888
|
+
// 2. メインキー(ショートカットと重複しない場合のみ)
|
|
10884
10889
|
_state.keyRects
|
|
10885
|
-
.filter(rect => _state.mappedSet.has(rect.kc))
|
|
10890
|
+
.filter(rect => _state.mappedSet.has(rect.kc) && !_state.shortcutSet.has(rect.kc))
|
|
10886
10891
|
.forEach(drawKey(C_COLOR.mappedFill, C_COLOR.mappedStroke, C_COLOR.mappedText));
|
|
10892
|
+
|
|
10893
|
+
// 3. ショートカット(常に最前面)
|
|
10894
|
+
_state.keyRects
|
|
10895
|
+
.filter(rect => _state.shortcutSet.has(rect.kc))
|
|
10896
|
+
.forEach(drawKey(C_COLOR.shortcutFill, C_COLOR.shortcutStroke, C_COLOR.shortcutText));
|
|
10887
10897
|
};
|
|
10888
10898
|
|
|
10889
10899
|
// -------------------------------------------------------------------------
|
|
@@ -10971,6 +10981,10 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10971
10981
|
|
|
10972
10982
|
_state.mappedSet = new Set(ctrl.map(arr => arr[0]).filter(v => v > 0));
|
|
10973
10983
|
_state.altSet = new Set(ctrl.flatMap(arr => arr.slice(1)).filter(v => v > 0));
|
|
10984
|
+
// プレイ中ショートカット: keyRetry / keyTitleBack は g_headerObj から取得、PgDn(34) / PgUp(33) は固定
|
|
10985
|
+
_state.shortcutSet = new Set(
|
|
10986
|
+
[g_headerObj.keyRetry, g_headerObj.keyTitleBack, 34, 33].filter(v => v > 0)
|
|
10987
|
+
);
|
|
10974
10988
|
if (_state.visible) drawMap();
|
|
10975
10989
|
};
|
|
10976
10990
|
|
|
@@ -10982,6 +10996,7 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
10982
10996
|
_state.visible = false;
|
|
10983
10997
|
_state.mappedSet = new Set();
|
|
10984
10998
|
_state.altSet = new Set();
|
|
10999
|
+
_state.shortcutSet = new Set();
|
|
10985
11000
|
_state.keyRects = [];
|
|
10986
11001
|
_state.canvasBase = null;
|
|
10987
11002
|
_state.canvasMap = null;
|
|
@@ -16135,12 +16150,11 @@ const resultInit = () => {
|
|
|
16135
16150
|
for (let j = 0; j < 2; j++) {
|
|
16136
16151
|
const canvas = document.createElement(`canvas`);
|
|
16137
16152
|
canvas.id = `graphGaugeTransition${j > 0 ? j + 1 : ``}`;
|
|
16138
|
-
|
|
16139
|
-
canvas.
|
|
16140
|
-
canvas.height = g_limitObj.gaugeTransitionHeight * dpr;
|
|
16153
|
+
canvas.width = g_limitObj.gaugeTransitionWidth * g_dpr;
|
|
16154
|
+
canvas.height = g_limitObj.gaugeTransitionHeight * g_dpr;
|
|
16141
16155
|
canvas.style.width = wUnit(g_limitObj.gaugeTransitionWidth);
|
|
16142
16156
|
canvas.style.height = wUnit(g_limitObj.gaugeTransitionHeight);
|
|
16143
|
-
canvas.getContext(`2d`).scale(
|
|
16157
|
+
canvas.getContext(`2d`).scale(g_dpr, g_dpr);
|
|
16144
16158
|
canvas.style.left = wUnit(0);
|
|
16145
16159
|
canvas.style.top = wUnit(0);
|
|
16146
16160
|
canvas.style.position = `absolute`;
|
|
@@ -16451,13 +16465,12 @@ const resultInit = () => {
|
|
|
16451
16465
|
tmpDiv.style.background = `#000000cc`;
|
|
16452
16466
|
const canvas = document.createElement(`canvas`);
|
|
16453
16467
|
const artistName = g_headerObj.artistNames[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.artistName;
|
|
16454
|
-
const dpr = window.devicePixelRatio || 1;
|
|
16455
16468
|
const logicalWidth = 400;
|
|
16456
16469
|
const logicalHeight = g_sHeight - 90;
|
|
16457
16470
|
|
|
16458
16471
|
canvas.id = `resultImage`;
|
|
16459
|
-
canvas.width = logicalWidth *
|
|
16460
|
-
canvas.height = logicalHeight *
|
|
16472
|
+
canvas.width = logicalWidth * g_dpr;
|
|
16473
|
+
canvas.height = logicalHeight * g_dpr;
|
|
16461
16474
|
canvas.style.width = wUnit(logicalWidth);
|
|
16462
16475
|
canvas.style.height = wUnit(logicalHeight);
|
|
16463
16476
|
canvas.style.left = wUnit((g_sWidth - parseFloat(canvas.style.width)) / 2);
|
|
@@ -16465,7 +16478,7 @@ const resultInit = () => {
|
|
|
16465
16478
|
canvas.style.position = `absolute`;
|
|
16466
16479
|
|
|
16467
16480
|
const context = canvas.getContext(`2d`);
|
|
16468
|
-
context.scale(
|
|
16481
|
+
context.scale(g_dpr, g_dpr);
|
|
16469
16482
|
const drawText = (_text, { x = 30, dy = 0, hy, siz = 15, color = `#cccccc`, align = C_ALIGN_LEFT, font } = {}) => {
|
|
16470
16483
|
context.font = `${wUnit(siz)} ${getBasicFont(font)}`;
|
|
16471
16484
|
context.fillStyle = color;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source by tickle
|
|
7
7
|
* Created : 2019/11/19
|
|
8
|
-
* Revised : 2026/05/
|
|
8
|
+
* Revised : 2026/05/13 (v47.6.1)
|
|
9
9
|
*
|
|
10
10
|
* https://github.com/cwtickle/danoniplus
|
|
11
11
|
*/
|
|
@@ -4743,6 +4743,7 @@ const g_lang_lblNameObj = {
|
|
|
4743
4743
|
unallocated: `未割当`,
|
|
4744
4744
|
allocated: `割当済`,
|
|
4745
4745
|
altAllocated: `代替キー`,
|
|
4746
|
+
shortcutKey: `ショートカットキー`,
|
|
4746
4747
|
|
|
4747
4748
|
j_ii: "(・∀・)イイ!!",
|
|
4748
4749
|
j_shakin: "(`・ω・)シャキン",
|
|
@@ -4798,6 +4799,7 @@ const g_lang_lblNameObj = {
|
|
|
4798
4799
|
unallocated: `Unallocated`,
|
|
4799
4800
|
allocated: `Allocated`,
|
|
4800
4801
|
altAllocated: `Alternate Keys`,
|
|
4802
|
+
shortcutKey: `Shortcut Keys`,
|
|
4801
4803
|
|
|
4802
4804
|
j_ii: ":D Perfect!!",
|
|
4803
4805
|
j_shakin: ":) Great!",
|