@kubohiroya/turbowarp-plus-editor-git 0.11.0 → 0.12.0
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/dist/index.js +1 -1
- package/dist/project-source-diff.d.ts +2 -0
- package/dist/project-source-diff.js +2 -2
- package/dist/project-source-review-model.d.ts +35 -0
- package/dist/review-words.d.ts +6 -0
- package/dist/{scratch-block-diff-renderer-CgAGfmAc.js → scratch-block-diff-renderer-e_drVlHE.js} +533 -254
- package/dist/style.css +14 -0
- package/package.json +1 -1
- package/src/project-source-diff.css +14 -0
- package/src/project-source-diff.tsx +2 -0
- package/src/project-source-review-model.ts +154 -8
- package/src/project-source-review.tsx +79 -13
- package/src/review-words.ts +66 -0
package/dist/{scratch-block-diff-renderer-CgAGfmAc.js → scratch-block-diff-renderer-e_drVlHE.js}
RENAMED
|
@@ -912,7 +912,9 @@ function groupScripts(before, after, changes) {
|
|
|
912
912
|
const group = (_groups$get = groups.get(id)) !== null && _groups$get !== void 0 ? _groups$get : {
|
|
913
913
|
id,
|
|
914
914
|
before: [],
|
|
915
|
-
after: []
|
|
915
|
+
after: [],
|
|
916
|
+
kind: "updated",
|
|
917
|
+
summary: []
|
|
916
918
|
};
|
|
917
919
|
const side = {
|
|
918
920
|
target,
|
|
@@ -924,20 +926,151 @@ function groupScripts(before, after, changes) {
|
|
|
924
926
|
sides.set(key, side);
|
|
925
927
|
}
|
|
926
928
|
}
|
|
927
|
-
const affected = /* @__PURE__ */ new
|
|
929
|
+
const affected = /* @__PURE__ */ new Map();
|
|
928
930
|
for (const change of changes) for (const version of ["before", "after"]) {
|
|
929
|
-
var _sides$get;
|
|
931
|
+
var _sides$get, _affected$get;
|
|
930
932
|
const block = change[version];
|
|
931
933
|
if (!block) continue;
|
|
932
934
|
const key = version + ":" + roots[version].get(block.id);
|
|
933
|
-
(_sides$get = sides.get(key)) === null || _sides$get === void 0 || _sides$get.marks.push({
|
|
935
|
+
if (!isLayoutOnly(change)) (_sides$get = sides.get(key)) === null || _sides$get === void 0 || _sides$get.marks.push({
|
|
934
936
|
id: block.id,
|
|
935
937
|
kind: change.kind,
|
|
936
938
|
parts: change.parts
|
|
937
939
|
});
|
|
938
|
-
|
|
940
|
+
const id = groupOf(key);
|
|
941
|
+
const list = (_affected$get = affected.get(id)) !== null && _affected$get !== void 0 ? _affected$get : [];
|
|
942
|
+
if (!list.includes(change)) list.push(change);
|
|
943
|
+
affected.set(id, list);
|
|
939
944
|
}
|
|
940
|
-
return [...affected].map((id) =>
|
|
945
|
+
return [...affected].map(([id, list]) => {
|
|
946
|
+
const group = groups.get(id);
|
|
947
|
+
group.kind = scriptKind(group, list);
|
|
948
|
+
group.summary = summarize(group, list, before, after);
|
|
949
|
+
return group;
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
var connectionFields = /* @__PURE__ */ new Set([
|
|
953
|
+
"next",
|
|
954
|
+
"parent",
|
|
955
|
+
"topLevel"
|
|
956
|
+
]);
|
|
957
|
+
var positionFields = /* @__PURE__ */ new Set(["x", "y"]);
|
|
958
|
+
var compactPositionOnly = (a, b) => Array.isArray(a) && Array.isArray(b) && equal(a.slice(0, 3), b.slice(0, 3)) && !equal(a, b);
|
|
959
|
+
function blockOf(side) {
|
|
960
|
+
return side ? side.target.blocks[side.id] : void 0;
|
|
961
|
+
}
|
|
962
|
+
/** True for an updated block whose content is the same: only its connections or its position changed. */
|
|
963
|
+
function isLayoutOnly(change) {
|
|
964
|
+
if (change.kind !== "updated") return false;
|
|
965
|
+
if (compactPositionOnly(blockOf(change.before), blockOf(change.after))) return true;
|
|
966
|
+
return change.fields.length > 0 && change.fields.every((field) => connectionFields.has(field) || positionFields.has(field));
|
|
967
|
+
}
|
|
968
|
+
function isPositionOnly(change) {
|
|
969
|
+
if (change.kind !== "updated") return false;
|
|
970
|
+
if (compactPositionOnly(blockOf(change.before), blockOf(change.after))) return true;
|
|
971
|
+
return change.fields.length > 0 && change.fields.every((field) => positionFields.has(field));
|
|
972
|
+
}
|
|
973
|
+
function scriptKind(group, changes) {
|
|
974
|
+
if (!group.before.length) return "added";
|
|
975
|
+
if (!group.after.length) return "removed";
|
|
976
|
+
if (group.before.length !== 1 || group.after.length !== 1) return "reorganized";
|
|
977
|
+
if (changes.every(isPositionOnly)) return "moved";
|
|
978
|
+
if (changes.every(isLayoutOnly)) return "reordered";
|
|
979
|
+
return "updated";
|
|
980
|
+
}
|
|
981
|
+
var opcodeOf = (block) => isRecord(block) && typeof block.opcode === "string" ? block.opcode : void 0;
|
|
982
|
+
var isShadow = (block) => !isRecord(block) || block.shadow === true;
|
|
983
|
+
var isHat = (opcode) => opcode !== void 0 && (opcode === "control_start_as_clone" || opcode === "procedures_definition" || /^[a-z0-9]+_when/i.test(opcode));
|
|
984
|
+
var substackInputs = ["SUBSTACK", "SUBSTACK2"];
|
|
985
|
+
function inputBlock(block, name) {
|
|
986
|
+
if (!isRecord(block) || !isRecord(block.inputs)) return void 0;
|
|
987
|
+
const input = block.inputs[name];
|
|
988
|
+
return Array.isArray(input) && typeof input[1] === "string" ? input[1] : void 0;
|
|
989
|
+
}
|
|
990
|
+
function summarize(group, changes, before, after) {
|
|
991
|
+
const items = [];
|
|
992
|
+
const seen = /* @__PURE__ */ new Set();
|
|
993
|
+
const add = (item) => {
|
|
994
|
+
const key = JSON.stringify(item);
|
|
995
|
+
if (seen.has(key)) return;
|
|
996
|
+
seen.add(key);
|
|
997
|
+
items.push(item);
|
|
998
|
+
};
|
|
999
|
+
if (group.kind === "moved" || group.kind === "reordered") {
|
|
1000
|
+
add({ kind: group.kind });
|
|
1001
|
+
return items;
|
|
1002
|
+
}
|
|
1003
|
+
const explained = /* @__PURE__ */ new Set();
|
|
1004
|
+
if (group.before.length === 1 && group.after.length === 1) {
|
|
1005
|
+
const oldRoot = group.before[0].id, newRoot = group.after[0].id;
|
|
1006
|
+
const a = opcodeOf(before === null || before === void 0 ? void 0 : before.blocks[oldRoot]), b = opcodeOf(after === null || after === void 0 ? void 0 : after.blocks[newRoot]);
|
|
1007
|
+
if (a !== b && isHat(a) && isHat(b)) {
|
|
1008
|
+
add({
|
|
1009
|
+
kind: "hatReplaced",
|
|
1010
|
+
before: a,
|
|
1011
|
+
after: b
|
|
1012
|
+
});
|
|
1013
|
+
for (const change of changes) {
|
|
1014
|
+
var _change$after, _change$before;
|
|
1015
|
+
if (change.kind === "added" && ((_change$after = change.after) === null || _change$after === void 0 ? void 0 : _change$after.id) === newRoot || change.kind === "removed" && ((_change$before = change.before) === null || _change$before === void 0 ? void 0 : _change$before.id) === oldRoot) explained.add(change);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
const branches = [];
|
|
1020
|
+
for (const change of changes) {
|
|
1021
|
+
if (change.kind !== "added" || !after) continue;
|
|
1022
|
+
const block = after.blocks[change.after.id];
|
|
1023
|
+
const opcode = opcodeOf(block);
|
|
1024
|
+
if (!opcode) continue;
|
|
1025
|
+
if (substackInputs.some((name) => {
|
|
1026
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1027
|
+
for (let id = inputBlock(block, name); id !== void 0 && !visited.has(id);) {
|
|
1028
|
+
visited.add(id);
|
|
1029
|
+
if (before && Object.hasOwn(before.blocks, id)) return true;
|
|
1030
|
+
const next = after.blocks[id];
|
|
1031
|
+
id = isRecord(next) && typeof next.next === "string" ? next.next : void 0;
|
|
1032
|
+
}
|
|
1033
|
+
return false;
|
|
1034
|
+
})) {
|
|
1035
|
+
add({
|
|
1036
|
+
kind: "wrapped",
|
|
1037
|
+
opcode
|
|
1038
|
+
});
|
|
1039
|
+
explained.add(change);
|
|
1040
|
+
} else if (opcode === "control_if" || opcode === "control_if_else") branches.push(change);
|
|
1041
|
+
}
|
|
1042
|
+
for (const change of branches) {
|
|
1043
|
+
add({
|
|
1044
|
+
kind: "branchAdded",
|
|
1045
|
+
opcode: opcodeOf(after.blocks[change.after.id])
|
|
1046
|
+
});
|
|
1047
|
+
explained.add(change);
|
|
1048
|
+
}
|
|
1049
|
+
for (const change of changes) {
|
|
1050
|
+
var _opcodeOf;
|
|
1051
|
+
if (change.kind !== "updated") continue;
|
|
1052
|
+
const opcode = (_opcodeOf = opcodeOf(blockOf(change.after))) !== null && _opcodeOf !== void 0 ? _opcodeOf : opcodeOf(blockOf(change.before));
|
|
1053
|
+
if (!opcode) continue;
|
|
1054
|
+
for (const part of change.parts) {
|
|
1055
|
+
if (part.kind === "input" && substackInputs.includes(part.name)) continue;
|
|
1056
|
+
add({
|
|
1057
|
+
kind: "valueChanged",
|
|
1058
|
+
opcode,
|
|
1059
|
+
name: part.name
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
const count = (kind) => changes.filter((change) => change.kind === kind && !explained.has(change) && !isShadow(blockOf(kind === "added" ? change.after : change.before))).length;
|
|
1064
|
+
const added = count("added"), removed = count("removed");
|
|
1065
|
+
if (added) add({
|
|
1066
|
+
kind: "blocksAdded",
|
|
1067
|
+
count: added
|
|
1068
|
+
});
|
|
1069
|
+
if (removed) add({
|
|
1070
|
+
kind: "blocksRemoved",
|
|
1071
|
+
count: removed
|
|
1072
|
+
});
|
|
1073
|
+
return items;
|
|
941
1074
|
}
|
|
942
1075
|
function changedProperties(before, after, except) {
|
|
943
1076
|
return [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].filter((key) => key !== except && !equal(before[key], after[key]));
|
|
@@ -946,8 +1079,8 @@ function compareProjectForReview(before, after) {
|
|
|
946
1079
|
const { changes } = compareProjectBlocks(before, after);
|
|
947
1080
|
const byTarget = /* @__PURE__ */ new Map();
|
|
948
1081
|
for (const change of changes) {
|
|
949
|
-
var _change$
|
|
950
|
-
const key = targetKey(((_change$
|
|
1082
|
+
var _change$after2;
|
|
1083
|
+
const key = targetKey(((_change$after2 = change.after) !== null && _change$after2 !== void 0 ? _change$after2 : change.before).target);
|
|
951
1084
|
const existing = byTarget.get(key);
|
|
952
1085
|
if (existing) existing.push(change);
|
|
953
1086
|
else byTarget.set(key, [change]);
|
|
@@ -959,12 +1092,14 @@ function compareProjectForReview(before, after) {
|
|
|
959
1092
|
const a = oldTargets.get(key), b = newTargets.get(key);
|
|
960
1093
|
const edits = byTarget.get(key);
|
|
961
1094
|
const scripts = edits ? groupScripts(a, b, edits) : [];
|
|
962
|
-
const
|
|
963
|
-
|
|
1095
|
+
const status = !a ? "added" : !b ? "removed" : "updated";
|
|
1096
|
+
const properties = status === "updated" ? changedProperties(_objectSpread2({}, a), _objectSpread2({}, b), "blocks") : [];
|
|
1097
|
+
if (scripts.length || properties.length || status !== "updated") {
|
|
964
1098
|
const { name, isStage } = b !== null && b !== void 0 ? b : a;
|
|
965
1099
|
targets.push({
|
|
966
1100
|
name,
|
|
967
1101
|
isStage,
|
|
1102
|
+
status,
|
|
968
1103
|
scripts,
|
|
969
1104
|
properties,
|
|
970
1105
|
before: a,
|
|
@@ -977,6 +1112,308 @@ function compareProjectForReview(before, after) {
|
|
|
977
1112
|
properties: changedProperties(before, after, "targets")
|
|
978
1113
|
};
|
|
979
1114
|
}
|
|
1115
|
+
// src/project-change-model.ts
|
|
1116
|
+
init_objectSpread2();
|
|
1117
|
+
var targetDataKeys = [
|
|
1118
|
+
"variables",
|
|
1119
|
+
"lists",
|
|
1120
|
+
"broadcasts"
|
|
1121
|
+
];
|
|
1122
|
+
var nonSettingKeys = /* @__PURE__ */ new Set([
|
|
1123
|
+
"blocks",
|
|
1124
|
+
"costumes",
|
|
1125
|
+
"sounds",
|
|
1126
|
+
...targetDataKeys
|
|
1127
|
+
]);
|
|
1128
|
+
var propertyLabels = /* @__PURE__ */ new Map([
|
|
1129
|
+
["variables", ["変数", "Variables"]],
|
|
1130
|
+
["lists", ["リスト", "Lists"]],
|
|
1131
|
+
["broadcasts", ["メッセージ", "Broadcast messages"]],
|
|
1132
|
+
["costumes", ["コスチューム・背景", "Costumes and backdrops"]],
|
|
1133
|
+
["sounds", ["音", "Sounds"]],
|
|
1134
|
+
["blocks", ["コード", "Code"]],
|
|
1135
|
+
["x", ["X 座標", "X position"]],
|
|
1136
|
+
["y", ["Y 座標", "Y position"]],
|
|
1137
|
+
["size", ["大きさ", "Size"]],
|
|
1138
|
+
["direction", ["向き", "Direction"]],
|
|
1139
|
+
["visible", ["表示状態", "Visibility"]],
|
|
1140
|
+
["currentCostume", ["表示するコスチューム", "Selected costume"]],
|
|
1141
|
+
["layerOrder", ["重なり順", "Layer order"]],
|
|
1142
|
+
["volume", ["音量", "Volume"]],
|
|
1143
|
+
["comments", ["コメント", "Comments"]],
|
|
1144
|
+
["draggable", ["ドラッグ可否", "Draggable"]],
|
|
1145
|
+
["rotationStyle", ["回転方法", "Rotation style"]],
|
|
1146
|
+
["tempo", ["テンポ", "Tempo"]],
|
|
1147
|
+
["videoTransparency", ["ビデオの透明度", "Video transparency"]],
|
|
1148
|
+
["videoState", ["ビデオの状態", "Video state"]],
|
|
1149
|
+
["textToSpeechLanguage", ["音声合成の言語", "Text to speech language"]],
|
|
1150
|
+
["monitors", ["モニター", "Monitors"]],
|
|
1151
|
+
["extensions", ["拡張機能", "Extensions"]],
|
|
1152
|
+
["meta", ["作品の情報", "Project metadata"]]
|
|
1153
|
+
]);
|
|
1154
|
+
/** The Japanese or English name of a project or target property key; unknown keys are returned as they are. */
|
|
1155
|
+
function projectPropertyLabel(key, locale = "en") {
|
|
1156
|
+
var _propertyLabels$get, _propertyLabels$get2;
|
|
1157
|
+
return (_propertyLabels$get = (_propertyLabels$get2 = propertyLabels.get(key)) === null || _propertyLabels$get2 === void 0 ? void 0 : _propertyLabels$get2[locale.startsWith("ja") ? 0 : 1]) !== null && _propertyLabels$get !== void 0 ? _propertyLabels$get : key;
|
|
1158
|
+
}
|
|
1159
|
+
/** The asset file name of a costume or sound entry: `md5ext`, or `assetId.dataFormat`; `''` when it has neither. */
|
|
1160
|
+
function assetFileName(entry) {
|
|
1161
|
+
if (typeof entry.md5ext === "string") return entry.md5ext;
|
|
1162
|
+
return typeof entry.assetId === "string" && typeof entry.dataFormat === "string" ? entry.assetId + "." + entry.dataFormat : "";
|
|
1163
|
+
}
|
|
1164
|
+
function joinPath(...parts) {
|
|
1165
|
+
return parts.flatMap((part) => part.split("/")).filter((part) => part !== "" && part !== ".").join("/");
|
|
1166
|
+
}
|
|
1167
|
+
function statusOf(before, after) {
|
|
1168
|
+
return before === void 0 ? "added" : after === void 0 ? "removed" : "updated";
|
|
1169
|
+
}
|
|
1170
|
+
function entriesOf(target, kind) {
|
|
1171
|
+
const list = target === null || target === void 0 ? void 0 : target[kind];
|
|
1172
|
+
return Array.isArray(list) ? list.filter(isRecord) : [];
|
|
1173
|
+
}
|
|
1174
|
+
function nameOf(entry) {
|
|
1175
|
+
return typeof entry.name === "string" ? entry.name : "";
|
|
1176
|
+
}
|
|
1177
|
+
/** Pairs the entries of two versions by name (repeated names pair in order): after's order first, then removed ones. */
|
|
1178
|
+
function pairEntries(before, after) {
|
|
1179
|
+
var _remaining$get;
|
|
1180
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
1181
|
+
for (const entry of before) {
|
|
1182
|
+
const queue = remaining.get(nameOf(entry));
|
|
1183
|
+
if (queue) queue.push(entry);
|
|
1184
|
+
else remaining.set(nameOf(entry), [entry]);
|
|
1185
|
+
}
|
|
1186
|
+
const pairs = [];
|
|
1187
|
+
for (const entry of after) pairs.push({
|
|
1188
|
+
name: nameOf(entry),
|
|
1189
|
+
before: (_remaining$get = remaining.get(nameOf(entry))) === null || _remaining$get === void 0 ? void 0 : _remaining$get.shift(),
|
|
1190
|
+
after: entry
|
|
1191
|
+
});
|
|
1192
|
+
for (const entry of before) {
|
|
1193
|
+
const queue = remaining.get(nameOf(entry));
|
|
1194
|
+
if ((queue === null || queue === void 0 ? void 0 : queue[0]) === entry) pairs.push({
|
|
1195
|
+
name: nameOf(entry),
|
|
1196
|
+
before: queue.shift()
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
return pairs;
|
|
1200
|
+
}
|
|
1201
|
+
function changedKeys(before, after, skip) {
|
|
1202
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(after), ...Object.keys(before)])].filter((key) => !skip(key) && !equal(before[key], after[key]));
|
|
1203
|
+
}
|
|
1204
|
+
/** Arranges the changed files of one project folder by stage, sprite and element. Pure; reads nothing. */
|
|
1205
|
+
function organizeProjectChanges(input) {
|
|
1206
|
+
const { folder, files, assetsDirectory } = input;
|
|
1207
|
+
const normalize = (project) => project === null ? { targets: [] } : _objectSpread2(_objectSpread2({}, project), {}, { targets: project.targets.map((target) => {
|
|
1208
|
+
var _target$blocks;
|
|
1209
|
+
return _objectSpread2(_objectSpread2({}, target), {}, { blocks: (_target$blocks = target.blocks) !== null && _target$blocks !== void 0 ? _target$blocks : {} });
|
|
1210
|
+
}) });
|
|
1211
|
+
const before = normalize(input.before), after = normalize(input.after);
|
|
1212
|
+
const projectPath = joinPath(folder, "project.source.json");
|
|
1213
|
+
const projectFiles = files.filter((file) => file.path === projectPath);
|
|
1214
|
+
const explained = /* @__PURE__ */ new Set();
|
|
1215
|
+
let projectUsed = false;
|
|
1216
|
+
const useProject = () => {
|
|
1217
|
+
projectUsed = true;
|
|
1218
|
+
return [...projectFiles];
|
|
1219
|
+
};
|
|
1220
|
+
const filesAt = (paths) => {
|
|
1221
|
+
const wanted = new Set(paths.filter((path) => path !== void 0));
|
|
1222
|
+
const found = files.filter((file) => wanted.has(file.path));
|
|
1223
|
+
for (const file of found) explained.add(file.path);
|
|
1224
|
+
return found;
|
|
1225
|
+
};
|
|
1226
|
+
const review = compareProjectForReview(before, after);
|
|
1227
|
+
const scripts = new Map(review.targets.map((target) => {
|
|
1228
|
+
var _target$after;
|
|
1229
|
+
return [targetKey((_target$after = target.after) !== null && _target$after !== void 0 ? _target$after : target.before), target.scripts.length];
|
|
1230
|
+
}));
|
|
1231
|
+
const oldTargets = new Map(before.targets.map((target) => [targetKey(target), target]));
|
|
1232
|
+
const newTargets = new Map(after.targets.map((target) => [targetKey(target), target]));
|
|
1233
|
+
const keys = [...new Set([...after.targets, ...before.targets].map(targetKey))];
|
|
1234
|
+
keys.sort((a, b) => Number(b === "stage") - Number(a === "stage"));
|
|
1235
|
+
const targets = [];
|
|
1236
|
+
for (const key of keys) {
|
|
1237
|
+
var _scripts$get;
|
|
1238
|
+
const a = oldTargets.get(key), b = newTargets.get(key);
|
|
1239
|
+
const status = statusOf(a, b);
|
|
1240
|
+
const elements = [];
|
|
1241
|
+
const count = (_scripts$get = scripts.get(key)) !== null && _scripts$get !== void 0 ? _scripts$get : 0;
|
|
1242
|
+
if (count) elements.push({
|
|
1243
|
+
key: JSON.stringify([
|
|
1244
|
+
key,
|
|
1245
|
+
"code",
|
|
1246
|
+
""
|
|
1247
|
+
]),
|
|
1248
|
+
kind: "code",
|
|
1249
|
+
status,
|
|
1250
|
+
scripts: count,
|
|
1251
|
+
files: useProject()
|
|
1252
|
+
});
|
|
1253
|
+
for (const kind of ["costumes", "sounds"]) {
|
|
1254
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1255
|
+
for (const pair of pairEntries(entriesOf(a, kind), entriesOf(b, kind))) {
|
|
1256
|
+
var _seen$get;
|
|
1257
|
+
const md5ext = {
|
|
1258
|
+
before: pair.before && assetFileName(pair.before) || void 0,
|
|
1259
|
+
after: pair.after && assetFileName(pair.after) || void 0
|
|
1260
|
+
};
|
|
1261
|
+
const differs = !equal(pair.before, pair.after);
|
|
1262
|
+
const assets = filesAt([md5ext.before && joinPath(folder, assetsDirectory.before, md5ext.before), md5ext.after && joinPath(folder, assetsDirectory.after, md5ext.after)]);
|
|
1263
|
+
if (!differs && !assets.length) continue;
|
|
1264
|
+
const repeat = (_seen$get = seen.get(pair.name)) !== null && _seen$get !== void 0 ? _seen$get : 0;
|
|
1265
|
+
seen.set(pair.name, repeat + 1);
|
|
1266
|
+
elements.push({
|
|
1267
|
+
key: JSON.stringify(repeat ? [
|
|
1268
|
+
key,
|
|
1269
|
+
kind,
|
|
1270
|
+
pair.name,
|
|
1271
|
+
repeat
|
|
1272
|
+
] : [
|
|
1273
|
+
key,
|
|
1274
|
+
kind,
|
|
1275
|
+
pair.name
|
|
1276
|
+
]),
|
|
1277
|
+
kind,
|
|
1278
|
+
status: statusOf(pair.before, pair.after),
|
|
1279
|
+
name: pair.name,
|
|
1280
|
+
before: pair.before,
|
|
1281
|
+
after: pair.after,
|
|
1282
|
+
md5ext,
|
|
1283
|
+
files: [...differs ? useProject() : [], ...assets]
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
if (a && b) {
|
|
1288
|
+
const oldFields = a, newFields = b;
|
|
1289
|
+
const data = changedKeys(oldFields, newFields, (property) => !targetDataKeys.includes(property));
|
|
1290
|
+
if (data.length) elements.push({
|
|
1291
|
+
key: JSON.stringify([
|
|
1292
|
+
key,
|
|
1293
|
+
"data",
|
|
1294
|
+
""
|
|
1295
|
+
]),
|
|
1296
|
+
kind: "data",
|
|
1297
|
+
status,
|
|
1298
|
+
properties: data,
|
|
1299
|
+
files: useProject()
|
|
1300
|
+
});
|
|
1301
|
+
const settings = changedKeys(oldFields, newFields, (property) => nonSettingKeys.has(property));
|
|
1302
|
+
if (settings.length) elements.push({
|
|
1303
|
+
key: JSON.stringify([
|
|
1304
|
+
key,
|
|
1305
|
+
"settings",
|
|
1306
|
+
""
|
|
1307
|
+
]),
|
|
1308
|
+
kind: "settings",
|
|
1309
|
+
status,
|
|
1310
|
+
properties: settings,
|
|
1311
|
+
files: useProject()
|
|
1312
|
+
});
|
|
1313
|
+
} else projectUsed = true;
|
|
1314
|
+
if (status !== "updated" || elements.length) {
|
|
1315
|
+
const { name, isStage } = b !== null && b !== void 0 ? b : a;
|
|
1316
|
+
targets.push({
|
|
1317
|
+
key,
|
|
1318
|
+
name,
|
|
1319
|
+
isStage,
|
|
1320
|
+
status,
|
|
1321
|
+
before: a,
|
|
1322
|
+
after: b,
|
|
1323
|
+
elements
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const project = changedKeys(before, after, (key) => key === "targets").map((property) => ({
|
|
1328
|
+
key: JSON.stringify([
|
|
1329
|
+
"project",
|
|
1330
|
+
"settings",
|
|
1331
|
+
property
|
|
1332
|
+
]),
|
|
1333
|
+
kind: "settings",
|
|
1334
|
+
status: statusOf(before[property], after[property]),
|
|
1335
|
+
properties: [property],
|
|
1336
|
+
files: useProject()
|
|
1337
|
+
}));
|
|
1338
|
+
if (projectUsed) explained.add(projectPath);
|
|
1339
|
+
return {
|
|
1340
|
+
targets,
|
|
1341
|
+
project,
|
|
1342
|
+
other: files.filter((file) => !explained.has(file.path))
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
// src/review-words.ts
|
|
1346
|
+
var pick = ([ja, en], locale) => locale.startsWith("ja") ? ja : en;
|
|
1347
|
+
var hats = /* @__PURE__ */ new Map([
|
|
1348
|
+
["event_whenflagclicked", ["緑の旗が押されたとき", "When green flag clicked"]],
|
|
1349
|
+
["event_whenthisspriteclicked", ["このスプライトが押されたとき", "When this sprite clicked"]],
|
|
1350
|
+
["event_whenstageclicked", ["ステージが押されたとき", "When stage clicked"]],
|
|
1351
|
+
["event_whenkeypressed", ["キーが押されたとき", "When key pressed"]],
|
|
1352
|
+
["event_whenbackdropswitchesto", ["背景が切り替わったとき", "When backdrop switches"]],
|
|
1353
|
+
["event_whengreaterthan", ["値が超えたとき", "When value exceeds"]],
|
|
1354
|
+
["event_whenbroadcastreceived", ["メッセージを受け取ったとき", "When message received"]],
|
|
1355
|
+
["control_start_as_clone", ["クローンされたとき", "When I start as a clone"]],
|
|
1356
|
+
["procedures_definition", ["定義", "Define"]]
|
|
1357
|
+
]);
|
|
1358
|
+
var blocks = /* @__PURE__ */ new Map([
|
|
1359
|
+
["control_repeat", ["〜回繰り返す", "repeat"]],
|
|
1360
|
+
["control_forever", ["ずっと", "forever"]],
|
|
1361
|
+
["control_repeat_until", ["〜まで繰り返す", "repeat until"]],
|
|
1362
|
+
["control_while", ["〜の間繰り返す", "while"]],
|
|
1363
|
+
["control_if", ["もし〜なら", "if"]],
|
|
1364
|
+
["control_if_else", ["もし〜なら、でなければ", "if else"]],
|
|
1365
|
+
["control_wait", ["〜秒待つ", "wait"]],
|
|
1366
|
+
["motion_movesteps", ["〜歩動かす", "move steps"]],
|
|
1367
|
+
["motion_turnright", ["〜度回す", "turn"]],
|
|
1368
|
+
["motion_turnleft", ["〜度回す", "turn"]],
|
|
1369
|
+
["motion_gotoxy", ["x座標を〜、y座標を〜にする", "go to x y"]],
|
|
1370
|
+
["motion_changeyby", ["y座標を〜ずつ変える", "change y by"]],
|
|
1371
|
+
["motion_changexby", ["x座標を〜ずつ変える", "change x by"]],
|
|
1372
|
+
["motion_glidesecstoxy", ["〜秒でx座標を〜に、y座標を〜に変える", "glide"]],
|
|
1373
|
+
["looks_sayforsecs", ["〜と〜秒言う", "say for secs"]],
|
|
1374
|
+
["looks_say", ["〜と言う", "say"]],
|
|
1375
|
+
["looks_switchcostumeto", ["コスチュームを〜にする", "switch costume"]],
|
|
1376
|
+
["looks_nextcostume", ["次のコスチュームにする", "next costume"]],
|
|
1377
|
+
["data_setvariableto", ["〜を〜にする", "set variable"]],
|
|
1378
|
+
["data_changevariableby", ["〜を〜ずつ変える", "change variable"]],
|
|
1379
|
+
["event_broadcast", ["〜を送る", "broadcast"]],
|
|
1380
|
+
["sound_play", ["〜の音を鳴らす", "play sound"]]
|
|
1381
|
+
]);
|
|
1382
|
+
function hatName(opcode, locale) {
|
|
1383
|
+
const pair = hats.get(opcode);
|
|
1384
|
+
return pair && pick(pair, locale);
|
|
1385
|
+
}
|
|
1386
|
+
var blockName = (opcode, locale) => {
|
|
1387
|
+
var _blocks$get;
|
|
1388
|
+
const pair = (_blocks$get = blocks.get(opcode)) !== null && _blocks$get !== void 0 ? _blocks$get : hats.get(opcode);
|
|
1389
|
+
return pair ? pick(pair, locale) : locale.startsWith("ja") ? "ブロック" : "a block";
|
|
1390
|
+
};
|
|
1391
|
+
/** One phrase per summary item, in the reader's language. */
|
|
1392
|
+
function summaryPhrase(item, locale) {
|
|
1393
|
+
const ja = locale.startsWith("ja");
|
|
1394
|
+
switch (item.kind) {
|
|
1395
|
+
case "hatReplaced": return ja ? `「${blockName(item.before, locale)}」を「${blockName(item.after, locale)}」に替えた` : `replaced “${blockName(item.before, locale)}” with “${blockName(item.after, locale)}”`;
|
|
1396
|
+
case "wrapped": return ja ? `「${blockName(item.opcode, locale)}」で包んだ` : `wrapped in “${blockName(item.opcode, locale)}”`;
|
|
1397
|
+
case "branchAdded": return ja ? `分岐「${blockName(item.opcode, locale)}」を追加` : `added “${blockName(item.opcode, locale)}”`;
|
|
1398
|
+
case "valueChanged": return ja ? `「${blockName(item.opcode, locale)}」の ${item.name} を変更` : `changed ${item.name} of “${blockName(item.opcode, locale)}”`;
|
|
1399
|
+
case "blocksAdded": return ja ? `ブロックを ${item.count} 個追加` : `added ${item.count} ${item.count === 1 ? "block" : "blocks"}`;
|
|
1400
|
+
case "blocksRemoved": return ja ? `ブロックを ${item.count} 個削除` : `removed ${item.count} ${item.count === 1 ? "block" : "blocks"}`;
|
|
1401
|
+
case "reordered": return ja ? "つなぎ方だけを変更" : "only reconnected";
|
|
1402
|
+
case "moved": return ja ? "位置だけを変更" : "only moved";
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
/** The colours of the previews, said once at the top of a review. */
|
|
1406
|
+
var legend = (locale) => locale.startsWith("ja") ? [
|
|
1407
|
+
["added", "緑の縁: 追加したブロック"],
|
|
1408
|
+
["removed", "赤の縁: 削除したブロック"],
|
|
1409
|
+
["updated", "紫の縁: 中身を変えたブロック"],
|
|
1410
|
+
["part", "オレンジの枠: 値を変えた欄"]
|
|
1411
|
+
] : [
|
|
1412
|
+
["added", "Green outline: added block"],
|
|
1413
|
+
["removed", "Red outline: removed block"],
|
|
1414
|
+
["updated", "Purple outline: block with changed contents"],
|
|
1415
|
+
["part", "Orange frame: changed value"]
|
|
1416
|
+
];
|
|
980
1417
|
// src/project-source-review.tsx
|
|
981
1418
|
init_objectSpread2();
|
|
982
1419
|
var ja = {
|
|
@@ -994,6 +1431,14 @@ var ja = {
|
|
|
994
1431
|
added: "追加",
|
|
995
1432
|
removed: "削除",
|
|
996
1433
|
reorganized: "再編",
|
|
1434
|
+
moved: "位置だけ",
|
|
1435
|
+
reordered: "並べ替え",
|
|
1436
|
+
stage: "ステージ",
|
|
1437
|
+
otherSettings: "そのほかの設定",
|
|
1438
|
+
legend: "色の見方",
|
|
1439
|
+
targetAdded: (name, stage) => stage ? "ステージを追加" : `スプライト『${name}』を追加`,
|
|
1440
|
+
targetRemoved: (name, stage) => stage ? "ステージを削除" : `スプライト『${name}』を削除`,
|
|
1441
|
+
assetOwner: (owner, kind, name) => `${owner} の${kind === "sound" ? "音" : kind === "backdrop" ? "背景" : "コスチューム"}『${name}』`,
|
|
997
1442
|
absent: "この版にはこのコードがありません",
|
|
998
1443
|
loading: "コードを描画中…",
|
|
999
1444
|
unavailable: "このコードを描画できません。",
|
|
@@ -1028,6 +1473,14 @@ var en = {
|
|
|
1028
1473
|
added: "Added",
|
|
1029
1474
|
removed: "Removed",
|
|
1030
1475
|
reorganized: "Reorganized",
|
|
1476
|
+
moved: "Moved only",
|
|
1477
|
+
reordered: "Reconnected",
|
|
1478
|
+
stage: "Stage",
|
|
1479
|
+
otherSettings: "Other settings",
|
|
1480
|
+
legend: "Colours",
|
|
1481
|
+
targetAdded: (name, stage) => stage ? "Added the stage" : `Added the sprite “${name}”`,
|
|
1482
|
+
targetRemoved: (name, stage) => stage ? "Removed the stage" : `Removed the sprite “${name}”`,
|
|
1483
|
+
assetOwner: (owner, kind, name) => `${kind === "sound" ? "Sound" : kind === "backdrop" ? "Backdrop" : "Costume"} “${name}” of ${owner}`,
|
|
1031
1484
|
absent: "No code in this version",
|
|
1032
1485
|
loading: "Rendering code…",
|
|
1033
1486
|
unavailable: "This code could not be rendered.",
|
|
@@ -1066,15 +1519,12 @@ var properties = /* @__PURE__ */ new Map([
|
|
|
1066
1519
|
function scriptTitle(side, labels) {
|
|
1067
1520
|
const block = side.target.blocks[side.id];
|
|
1068
1521
|
if (!isRecord(block)) return labels.script;
|
|
1069
|
-
if (block.opcode === "event_whenflagclicked") return labels.flag;
|
|
1070
|
-
if (block.opcode === "event_whenthisspriteclicked") return labels.click;
|
|
1071
|
-
if (block.opcode === "event_whenkeypressed") return labels.key;
|
|
1072
1522
|
if (block.opcode === "event_whenbroadcastreceived") {
|
|
1073
1523
|
const field = isRecord(block.fields) ? block.fields.BROADCAST_OPTION : void 0;
|
|
1074
1524
|
const message = Array.isArray(field) && typeof field[0] === "string" ? field[0] : "";
|
|
1075
1525
|
if (message) return labels === ja ? `「${message}」${labels.broadcast}` : `“${message}” ${labels.broadcast}`;
|
|
1076
1526
|
}
|
|
1077
|
-
return typeof block.opcode === "string"
|
|
1527
|
+
return typeof block.opcode === "string" && hatName(block.opcode, labels === ja ? "ja" : "en") || labels.script;
|
|
1078
1528
|
}
|
|
1079
1529
|
var sideKey = (side) => JSON.stringify([
|
|
1080
1530
|
side.target.isStage,
|
|
@@ -1121,14 +1571,16 @@ function useBlockLocation(comparison, locateBlock) {
|
|
|
1121
1571
|
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset, location }) {
|
|
1122
1572
|
const [open, setOpen] = useState(false);
|
|
1123
1573
|
const { sync, toggle } = usePreviewSync(labels === ja ? "ja" : "en");
|
|
1124
|
-
const status =
|
|
1574
|
+
const status = script.kind;
|
|
1575
|
+
const locale = labels === ja ? "ja" : "en";
|
|
1576
|
+
const summary = script.summary.map((item) => summaryPhrase(item, locale)).join(labels === ja ? "、" : ", ");
|
|
1125
1577
|
const beforeTitle = script.before[0] && scriptTitle(script.before[0], labels);
|
|
1126
1578
|
const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
|
|
1127
1579
|
const title = beforeTitle && afterTitle && beforeTitle !== afterTitle ? beforeTitle + " → " + afterTitle : afterTitle !== null && afterTitle !== void 0 ? afterTitle : beforeTitle;
|
|
1128
1580
|
return /* @__PURE__ */ React.createElement("details", {
|
|
1129
1581
|
className: "twgit-review-script",
|
|
1130
1582
|
onToggle: (event) => setOpen(event.currentTarget.open)
|
|
1131
|
-
}, /* @__PURE__ */ React.createElement("summary", null, /* @__PURE__ */ React.createElement("span", { className: "twgit-review-status" }, labels[status]), /* @__PURE__ */ React.createElement("strong", null, title), status === "reorganized" && /* @__PURE__ */ React.createElement("small", null, script.before.length, " → ", script.after.length), reviewed && /* @__PURE__ */ React.createElement("small", null, labels.reviewed)), open && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-sides" }, ["before", "after"].map((version) => /* @__PURE__ */ React.createElement("div", { key: version }, script[version].length ? script[version].map((side, index, sides) => /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
1583
|
+
}, /* @__PURE__ */ React.createElement("summary", null, /* @__PURE__ */ React.createElement("span", { className: "twgit-review-status" }, labels[status]), /* @__PURE__ */ React.createElement("strong", null, title), status === "reorganized" && /* @__PURE__ */ React.createElement("small", null, script.before.length, " → ", script.after.length), summary && /* @__PURE__ */ React.createElement("small", { className: "twgit-review-summary" }, summary), reviewed && /* @__PURE__ */ React.createElement("small", null, labels.reviewed)), open && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-sides" }, ["before", "after"].map((version) => /* @__PURE__ */ React.createElement("div", { key: version }, script[version].length ? script[version].map((side, index, sides) => /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
1132
1584
|
key: side.id,
|
|
1133
1585
|
side,
|
|
1134
1586
|
title: sides.length > 1 ? `${labels[version]} ${index + 1}` : labels[version],
|
|
@@ -1153,10 +1605,15 @@ function ScriptComparison({ script, labels, reviewed = false, setReviewed, rende
|
|
|
1153
1605
|
}), " ", labels.reviewed)));
|
|
1154
1606
|
}
|
|
1155
1607
|
function propertyNames(keys, labels) {
|
|
1156
|
-
|
|
1608
|
+
const locale = labels === ja ? "ja" : "en";
|
|
1609
|
+
const named = /* @__PURE__ */ new Set(), other = [];
|
|
1610
|
+
for (const key of keys) {
|
|
1157
1611
|
var _properties$get, _properties$get2;
|
|
1158
|
-
|
|
1159
|
-
|
|
1612
|
+
const label = (_properties$get = (_properties$get2 = properties.get(key)) === null || _properties$get2 === void 0 ? void 0 : _properties$get2[labels === ja ? 0 : 1]) !== null && _properties$get !== void 0 ? _properties$get : projectPropertyLabel(key, locale);
|
|
1613
|
+
if (label === key) other.push(key);
|
|
1614
|
+
else named.add(label);
|
|
1615
|
+
}
|
|
1616
|
+
return /* @__PURE__ */ React.createElement("span", { title: keys.join(", ") }, [...named, ...other.length ? [labels.otherSettings] : []].join(" · "));
|
|
1160
1617
|
}
|
|
1161
1618
|
/**
|
|
1162
1619
|
* The current costume (the backdrop for the stage) of each version of a target that has one. A changed costume shows
|
|
@@ -1193,7 +1650,7 @@ function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, locateB
|
|
|
1193
1650
|
target,
|
|
1194
1651
|
labels,
|
|
1195
1652
|
loadAsset
|
|
1196
|
-
}), target.name, target.scripts.length > 0 && /* @__PURE__ */ React.createElement("small", null, labels.scriptCount(target.scripts.length))), target.properties.length > 0 && /* @__PURE__ */ React.createElement("p", null, labels.properties, " · ", propertyNames(target.properties, labels)), target.scripts.map((script) => {
|
|
1653
|
+
}), target.isStage ? labels.stage : target.name, target.scripts.length > 0 && /* @__PURE__ */ React.createElement("small", null, labels.scriptCount(target.scripts.length))), target.status !== "updated" && /* @__PURE__ */ React.createElement("p", { className: "twgit-review-target-status" }, (target.status === "added" ? labels.targetAdded : labels.targetRemoved)(target.name, target.isStage)), target.status === "updated" && target.properties.length > 0 && /* @__PURE__ */ React.createElement("p", null, labels.properties, " · ", propertyNames(target.properties, labels)), target.scripts.map((script) => {
|
|
1197
1654
|
const key = JSON.stringify([
|
|
1198
1655
|
review === null || review === void 0 ? void 0 : review.file,
|
|
1199
1656
|
target.isStage,
|
|
@@ -1212,6 +1669,54 @@ function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, locateB
|
|
|
1212
1669
|
});
|
|
1213
1670
|
}))));
|
|
1214
1671
|
}
|
|
1672
|
+
function Legend({ labels }) {
|
|
1673
|
+
return /* @__PURE__ */ React.createElement("p", {
|
|
1674
|
+
className: "twgit-review-legend",
|
|
1675
|
+
"aria-label": labels.legend
|
|
1676
|
+
}, legend(labels === ja ? "ja" : "en").map(([kind, text]) => /* @__PURE__ */ React.createElement("span", {
|
|
1677
|
+
key: kind,
|
|
1678
|
+
"data-legend": kind
|
|
1679
|
+
}, text)));
|
|
1680
|
+
}
|
|
1681
|
+
/** Finds who uses an asset file of the review: the costume or sound whose file name it is, in the project beside it. */
|
|
1682
|
+
function assetOwner(path, status, entries) {
|
|
1683
|
+
const parts = path.split("/");
|
|
1684
|
+
const name = parts.pop();
|
|
1685
|
+
const folder = parts.slice(0, -1).join("/");
|
|
1686
|
+
const version = status === "removed" ? "before" : "after";
|
|
1687
|
+
for (const { file, comparison } of entries) {
|
|
1688
|
+
if (!comparison || !("patch" in file) || file.path.split("/").slice(0, -1).join("/") !== folder) continue;
|
|
1689
|
+
for (const target of comparison.targets) {
|
|
1690
|
+
const owner = target[version];
|
|
1691
|
+
if (!owner) continue;
|
|
1692
|
+
for (const kind of ["costumes", "sounds"]) {
|
|
1693
|
+
const list = owner[kind];
|
|
1694
|
+
if (!Array.isArray(list)) continue;
|
|
1695
|
+
const entry = list.find((item) => isRecord(item) && assetFileName(item) === name);
|
|
1696
|
+
if (isRecord(entry)) return {
|
|
1697
|
+
target,
|
|
1698
|
+
version,
|
|
1699
|
+
md5ext: name,
|
|
1700
|
+
loadAsset: file.loadAsset,
|
|
1701
|
+
kind: kind === "sounds" ? "sound" : target.isStage ? "backdrop" : "costume",
|
|
1702
|
+
name: typeof entry.name === "string" ? entry.name : name
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
function RelatedFile({ file, entries, labels }) {
|
|
1710
|
+
var _owner$version;
|
|
1711
|
+
const owner = assetOwner(file.path, file.status, entries);
|
|
1712
|
+
const image = useAssetImage(owner && owner.kind !== "sound" ? owner.md5ext : void 0, (_owner$version = owner === null || owner === void 0 ? void 0 : owner.version) !== null && _owner$version !== void 0 ? _owner$version : "after", owner === null || owner === void 0 ? void 0 : owner.loadAsset);
|
|
1713
|
+
if (!owner) return /* @__PURE__ */ React.createElement("p", null, labels.related, " · ", labels[file.status]);
|
|
1714
|
+
const ownerName = owner.target.isStage ? labels.stage : owner.target.name;
|
|
1715
|
+
return /* @__PURE__ */ React.createElement("p", { className: "twgit-review-asset" }, image && /* @__PURE__ */ React.createElement("img", {
|
|
1716
|
+
src: image,
|
|
1717
|
+
alt: ""
|
|
1718
|
+
}), /* @__PURE__ */ React.createElement("span", null, labels.assetOwner(ownerName, owner.kind, owner.name), " · ", labels[file.status]));
|
|
1719
|
+
}
|
|
1215
1720
|
var countScripts = (comparison) => comparison.targets.reduce((count, target) => count + target.scripts.length, 0);
|
|
1216
1721
|
/** Compares two versions of a project.source.json by stage, sprite and code, as the pull request review does. */
|
|
1217
1722
|
function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fallback, loadAsset, locateBlock, target }) {
|
|
@@ -1241,7 +1746,7 @@ function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fa
|
|
|
1241
1746
|
return /* @__PURE__ */ React.createElement("section", {
|
|
1242
1747
|
className: "twgit-block-diff twgit-review twgit-review-comparison",
|
|
1243
1748
|
"aria-label": labels.comparison
|
|
1244
|
-
}, !only && /* @__PURE__ */ React.createElement("header", { className: "twgit-review-overview" }, /* @__PURE__ */ React.createElement("dl", null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.targets), /* @__PURE__ */ React.createElement("dd", null, comparison.targets.length)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.scripts), /* @__PURE__ */ React.createElement("dd", null, countScripts(comparison))))), /* @__PURE__ */ React.createElement(ComparisonTargets, {
|
|
1749
|
+
}, !only && /* @__PURE__ */ React.createElement(Legend, { labels }), !only && /* @__PURE__ */ React.createElement("header", { className: "twgit-review-overview" }, /* @__PURE__ */ React.createElement("dl", null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.targets), /* @__PURE__ */ React.createElement("dd", null, comparison.targets.length)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.scripts), /* @__PURE__ */ React.createElement("dd", null, countScripts(comparison))))), /* @__PURE__ */ React.createElement(ComparisonTargets, {
|
|
1245
1750
|
comparison,
|
|
1246
1751
|
labels,
|
|
1247
1752
|
renderBlock,
|
|
@@ -1291,10 +1796,14 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1291
1796
|
return /* @__PURE__ */ React.createElement("section", {
|
|
1292
1797
|
className: "twgit-block-diff twgit-review",
|
|
1293
1798
|
"aria-label": labels.review
|
|
1294
|
-
}, /* @__PURE__ */ React.createElement("header", { className: "twgit-review-overview" }, /* @__PURE__ */ React.createElement("p", null, labels.review), /* @__PURE__ */ React.createElement("h2", null, title), excerpt && /* @__PURE__ */ React.createElement("p", { className: "twgit-review-description" }, excerpt), /* @__PURE__ */ React.createElement("dl", null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.files), /* @__PURE__ */ React.createElement("dd", null, entries.length)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.targets), /* @__PURE__ */ React.createElement("dd", null, targets)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.scripts), /* @__PURE__ */ React.createElement("dd", null, scripts)), scripts > 0 && /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.reviewed), /* @__PURE__ */ React.createElement("dd", null, reviewed.size, " / ", scripts))), longBody && /* @__PURE__ */ React.createElement("details", { className: "twgit-review-body" }, /* @__PURE__ */ React.createElement("summary", null, labels.fullDescription), /* @__PURE__ */ React.createElement("p", { className: "twgit-review-description" }, body)), !!(verification === null || verification === void 0 ? void 0 : verification.length) && /* @__PURE__ */ React.createElement("div", { className: "twgit-review-checklist" }, /* @__PURE__ */ React.createElement("h3", null, labels.checks), /* @__PURE__ */ React.createElement("ul", null, verification.map((item, index) => /* @__PURE__ */ React.createElement("li", { key: index }, item))))), /* @__PURE__ */ React.createElement("h3", { className: "twgit-review-files-title" }, labels.files), entries.map(({ file, comparison, invalid }) => /* @__PURE__ */ React.createElement("section", {
|
|
1799
|
+
}, /* @__PURE__ */ React.createElement("header", { className: "twgit-review-overview" }, /* @__PURE__ */ React.createElement("p", null, labels.review), /* @__PURE__ */ React.createElement("h2", null, title), excerpt && /* @__PURE__ */ React.createElement("p", { className: "twgit-review-description" }, excerpt), /* @__PURE__ */ React.createElement("dl", null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.files), /* @__PURE__ */ React.createElement("dd", null, entries.length)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.targets), /* @__PURE__ */ React.createElement("dd", null, targets)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.scripts), /* @__PURE__ */ React.createElement("dd", null, scripts)), scripts > 0 && /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, labels.reviewed), /* @__PURE__ */ React.createElement("dd", null, reviewed.size, " / ", scripts))), longBody && /* @__PURE__ */ React.createElement("details", { className: "twgit-review-body" }, /* @__PURE__ */ React.createElement("summary", null, labels.fullDescription), /* @__PURE__ */ React.createElement("p", { className: "twgit-review-description" }, body)), !!(verification === null || verification === void 0 ? void 0 : verification.length) && /* @__PURE__ */ React.createElement("div", { className: "twgit-review-checklist" }, /* @__PURE__ */ React.createElement("h3", null, labels.checks), /* @__PURE__ */ React.createElement("ul", null, verification.map((item, index) => /* @__PURE__ */ React.createElement("li", { key: index }, item))))), /* @__PURE__ */ React.createElement(Legend, { labels }), /* @__PURE__ */ React.createElement("h3", { className: "twgit-review-files-title" }, labels.files), entries.map(({ file, comparison, invalid }) => /* @__PURE__ */ React.createElement("section", {
|
|
1295
1800
|
key: file.path,
|
|
1296
1801
|
className: "twgit-review-file"
|
|
1297
|
-
}, /* @__PURE__ */ React.createElement("header", null, /* @__PURE__ */ React.createElement("h4", null, file.previousPath ? file.previousPath + " → " + file.path : file.path), file.summary && /* @__PURE__ */ React.createElement("p", null, file.summary)), "status" in file && /* @__PURE__ */ React.createElement(
|
|
1802
|
+
}, /* @__PURE__ */ React.createElement("header", null, /* @__PURE__ */ React.createElement("h4", null, file.previousPath ? file.previousPath + " → " + file.path : file.path), file.summary && /* @__PURE__ */ React.createElement("p", null, file.summary)), "status" in file && /* @__PURE__ */ React.createElement(RelatedFile, {
|
|
1803
|
+
file,
|
|
1804
|
+
entries,
|
|
1805
|
+
labels
|
|
1806
|
+
}), (invalid || file.unavailable) && /* @__PURE__ */ React.createElement("p", { role: "alert" }, labels.invalid), comparison && /* @__PURE__ */ React.createElement(ComparisonTargets, {
|
|
1298
1807
|
comparison,
|
|
1299
1808
|
labels,
|
|
1300
1809
|
renderBlock,
|
|
@@ -1307,236 +1816,6 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1307
1816
|
}
|
|
1308
1817
|
}))));
|
|
1309
1818
|
}
|
|
1310
|
-
// src/project-change-model.ts
|
|
1311
|
-
init_objectSpread2();
|
|
1312
|
-
var targetDataKeys = [
|
|
1313
|
-
"variables",
|
|
1314
|
-
"lists",
|
|
1315
|
-
"broadcasts"
|
|
1316
|
-
];
|
|
1317
|
-
var nonSettingKeys = /* @__PURE__ */ new Set([
|
|
1318
|
-
"blocks",
|
|
1319
|
-
"costumes",
|
|
1320
|
-
"sounds",
|
|
1321
|
-
...targetDataKeys
|
|
1322
|
-
]);
|
|
1323
|
-
var propertyLabels = /* @__PURE__ */ new Map([
|
|
1324
|
-
["variables", ["変数", "Variables"]],
|
|
1325
|
-
["lists", ["リスト", "Lists"]],
|
|
1326
|
-
["broadcasts", ["メッセージ", "Broadcast messages"]],
|
|
1327
|
-
["costumes", ["コスチューム・背景", "Costumes and backdrops"]],
|
|
1328
|
-
["sounds", ["音", "Sounds"]],
|
|
1329
|
-
["blocks", ["コード", "Code"]],
|
|
1330
|
-
["x", ["X 座標", "X position"]],
|
|
1331
|
-
["y", ["Y 座標", "Y position"]],
|
|
1332
|
-
["size", ["大きさ", "Size"]],
|
|
1333
|
-
["direction", ["向き", "Direction"]],
|
|
1334
|
-
["visible", ["表示状態", "Visibility"]],
|
|
1335
|
-
["currentCostume", ["表示するコスチューム", "Selected costume"]],
|
|
1336
|
-
["layerOrder", ["重なり順", "Layer order"]],
|
|
1337
|
-
["volume", ["音量", "Volume"]],
|
|
1338
|
-
["comments", ["コメント", "Comments"]],
|
|
1339
|
-
["draggable", ["ドラッグ可否", "Draggable"]],
|
|
1340
|
-
["rotationStyle", ["回転方法", "Rotation style"]],
|
|
1341
|
-
["tempo", ["テンポ", "Tempo"]],
|
|
1342
|
-
["videoTransparency", ["ビデオの透明度", "Video transparency"]],
|
|
1343
|
-
["videoState", ["ビデオの状態", "Video state"]],
|
|
1344
|
-
["textToSpeechLanguage", ["音声合成の言語", "Text to speech language"]],
|
|
1345
|
-
["monitors", ["モニター", "Monitors"]],
|
|
1346
|
-
["extensions", ["拡張機能", "Extensions"]],
|
|
1347
|
-
["meta", ["作品の情報", "Project metadata"]]
|
|
1348
|
-
]);
|
|
1349
|
-
/** The Japanese or English name of a project or target property key; unknown keys are returned as they are. */
|
|
1350
|
-
function projectPropertyLabel(key, locale = "en") {
|
|
1351
|
-
var _propertyLabels$get, _propertyLabels$get2;
|
|
1352
|
-
return (_propertyLabels$get = (_propertyLabels$get2 = propertyLabels.get(key)) === null || _propertyLabels$get2 === void 0 ? void 0 : _propertyLabels$get2[locale.startsWith("ja") ? 0 : 1]) !== null && _propertyLabels$get !== void 0 ? _propertyLabels$get : key;
|
|
1353
|
-
}
|
|
1354
|
-
/** The asset file name of a costume or sound entry: `md5ext`, or `assetId.dataFormat`; `''` when it has neither. */
|
|
1355
|
-
function assetFileName(entry) {
|
|
1356
|
-
if (typeof entry.md5ext === "string") return entry.md5ext;
|
|
1357
|
-
return typeof entry.assetId === "string" && typeof entry.dataFormat === "string" ? entry.assetId + "." + entry.dataFormat : "";
|
|
1358
|
-
}
|
|
1359
|
-
function joinPath(...parts) {
|
|
1360
|
-
return parts.flatMap((part) => part.split("/")).filter((part) => part !== "" && part !== ".").join("/");
|
|
1361
|
-
}
|
|
1362
|
-
function statusOf(before, after) {
|
|
1363
|
-
return before === void 0 ? "added" : after === void 0 ? "removed" : "updated";
|
|
1364
|
-
}
|
|
1365
|
-
function entriesOf(target, kind) {
|
|
1366
|
-
const list = target === null || target === void 0 ? void 0 : target[kind];
|
|
1367
|
-
return Array.isArray(list) ? list.filter(isRecord) : [];
|
|
1368
|
-
}
|
|
1369
|
-
function nameOf(entry) {
|
|
1370
|
-
return typeof entry.name === "string" ? entry.name : "";
|
|
1371
|
-
}
|
|
1372
|
-
/** Pairs the entries of two versions by name (repeated names pair in order): after's order first, then removed ones. */
|
|
1373
|
-
function pairEntries(before, after) {
|
|
1374
|
-
var _remaining$get;
|
|
1375
|
-
const remaining = /* @__PURE__ */ new Map();
|
|
1376
|
-
for (const entry of before) {
|
|
1377
|
-
const queue = remaining.get(nameOf(entry));
|
|
1378
|
-
if (queue) queue.push(entry);
|
|
1379
|
-
else remaining.set(nameOf(entry), [entry]);
|
|
1380
|
-
}
|
|
1381
|
-
const pairs = [];
|
|
1382
|
-
for (const entry of after) pairs.push({
|
|
1383
|
-
name: nameOf(entry),
|
|
1384
|
-
before: (_remaining$get = remaining.get(nameOf(entry))) === null || _remaining$get === void 0 ? void 0 : _remaining$get.shift(),
|
|
1385
|
-
after: entry
|
|
1386
|
-
});
|
|
1387
|
-
for (const entry of before) {
|
|
1388
|
-
const queue = remaining.get(nameOf(entry));
|
|
1389
|
-
if ((queue === null || queue === void 0 ? void 0 : queue[0]) === entry) pairs.push({
|
|
1390
|
-
name: nameOf(entry),
|
|
1391
|
-
before: queue.shift()
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
return pairs;
|
|
1395
|
-
}
|
|
1396
|
-
function changedKeys(before, after, skip) {
|
|
1397
|
-
return [.../* @__PURE__ */ new Set([...Object.keys(after), ...Object.keys(before)])].filter((key) => !skip(key) && !equal(before[key], after[key]));
|
|
1398
|
-
}
|
|
1399
|
-
/** Arranges the changed files of one project folder by stage, sprite and element. Pure; reads nothing. */
|
|
1400
|
-
function organizeProjectChanges(input) {
|
|
1401
|
-
const { folder, files, assetsDirectory } = input;
|
|
1402
|
-
const normalize = (project) => project === null ? { targets: [] } : _objectSpread2(_objectSpread2({}, project), {}, { targets: project.targets.map((target) => {
|
|
1403
|
-
var _target$blocks;
|
|
1404
|
-
return _objectSpread2(_objectSpread2({}, target), {}, { blocks: (_target$blocks = target.blocks) !== null && _target$blocks !== void 0 ? _target$blocks : {} });
|
|
1405
|
-
}) });
|
|
1406
|
-
const before = normalize(input.before), after = normalize(input.after);
|
|
1407
|
-
const projectPath = joinPath(folder, "project.source.json");
|
|
1408
|
-
const projectFiles = files.filter((file) => file.path === projectPath);
|
|
1409
|
-
const explained = /* @__PURE__ */ new Set();
|
|
1410
|
-
let projectUsed = false;
|
|
1411
|
-
const useProject = () => {
|
|
1412
|
-
projectUsed = true;
|
|
1413
|
-
return [...projectFiles];
|
|
1414
|
-
};
|
|
1415
|
-
const filesAt = (paths) => {
|
|
1416
|
-
const wanted = new Set(paths.filter((path) => path !== void 0));
|
|
1417
|
-
const found = files.filter((file) => wanted.has(file.path));
|
|
1418
|
-
for (const file of found) explained.add(file.path);
|
|
1419
|
-
return found;
|
|
1420
|
-
};
|
|
1421
|
-
const review = compareProjectForReview(before, after);
|
|
1422
|
-
const scripts = new Map(review.targets.map((target) => {
|
|
1423
|
-
var _target$after;
|
|
1424
|
-
return [targetKey((_target$after = target.after) !== null && _target$after !== void 0 ? _target$after : target.before), target.scripts.length];
|
|
1425
|
-
}));
|
|
1426
|
-
const oldTargets = new Map(before.targets.map((target) => [targetKey(target), target]));
|
|
1427
|
-
const newTargets = new Map(after.targets.map((target) => [targetKey(target), target]));
|
|
1428
|
-
const keys = [...new Set([...after.targets, ...before.targets].map(targetKey))];
|
|
1429
|
-
keys.sort((a, b) => Number(b === "stage") - Number(a === "stage"));
|
|
1430
|
-
const targets = [];
|
|
1431
|
-
for (const key of keys) {
|
|
1432
|
-
var _scripts$get;
|
|
1433
|
-
const a = oldTargets.get(key), b = newTargets.get(key);
|
|
1434
|
-
const status = statusOf(a, b);
|
|
1435
|
-
const elements = [];
|
|
1436
|
-
const count = (_scripts$get = scripts.get(key)) !== null && _scripts$get !== void 0 ? _scripts$get : 0;
|
|
1437
|
-
if (count) elements.push({
|
|
1438
|
-
key: JSON.stringify([
|
|
1439
|
-
key,
|
|
1440
|
-
"code",
|
|
1441
|
-
""
|
|
1442
|
-
]),
|
|
1443
|
-
kind: "code",
|
|
1444
|
-
status,
|
|
1445
|
-
scripts: count,
|
|
1446
|
-
files: useProject()
|
|
1447
|
-
});
|
|
1448
|
-
for (const kind of ["costumes", "sounds"]) {
|
|
1449
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1450
|
-
for (const pair of pairEntries(entriesOf(a, kind), entriesOf(b, kind))) {
|
|
1451
|
-
var _seen$get;
|
|
1452
|
-
const md5ext = {
|
|
1453
|
-
before: pair.before && assetFileName(pair.before) || void 0,
|
|
1454
|
-
after: pair.after && assetFileName(pair.after) || void 0
|
|
1455
|
-
};
|
|
1456
|
-
const differs = !equal(pair.before, pair.after);
|
|
1457
|
-
const assets = filesAt([md5ext.before && joinPath(folder, assetsDirectory.before, md5ext.before), md5ext.after && joinPath(folder, assetsDirectory.after, md5ext.after)]);
|
|
1458
|
-
if (!differs && !assets.length) continue;
|
|
1459
|
-
const repeat = (_seen$get = seen.get(pair.name)) !== null && _seen$get !== void 0 ? _seen$get : 0;
|
|
1460
|
-
seen.set(pair.name, repeat + 1);
|
|
1461
|
-
elements.push({
|
|
1462
|
-
key: JSON.stringify(repeat ? [
|
|
1463
|
-
key,
|
|
1464
|
-
kind,
|
|
1465
|
-
pair.name,
|
|
1466
|
-
repeat
|
|
1467
|
-
] : [
|
|
1468
|
-
key,
|
|
1469
|
-
kind,
|
|
1470
|
-
pair.name
|
|
1471
|
-
]),
|
|
1472
|
-
kind,
|
|
1473
|
-
status: statusOf(pair.before, pair.after),
|
|
1474
|
-
name: pair.name,
|
|
1475
|
-
before: pair.before,
|
|
1476
|
-
after: pair.after,
|
|
1477
|
-
md5ext,
|
|
1478
|
-
files: [...differs ? useProject() : [], ...assets]
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
if (a && b) {
|
|
1483
|
-
const oldFields = a, newFields = b;
|
|
1484
|
-
const data = changedKeys(oldFields, newFields, (property) => !targetDataKeys.includes(property));
|
|
1485
|
-
if (data.length) elements.push({
|
|
1486
|
-
key: JSON.stringify([
|
|
1487
|
-
key,
|
|
1488
|
-
"data",
|
|
1489
|
-
""
|
|
1490
|
-
]),
|
|
1491
|
-
kind: "data",
|
|
1492
|
-
status,
|
|
1493
|
-
properties: data,
|
|
1494
|
-
files: useProject()
|
|
1495
|
-
});
|
|
1496
|
-
const settings = changedKeys(oldFields, newFields, (property) => nonSettingKeys.has(property));
|
|
1497
|
-
if (settings.length) elements.push({
|
|
1498
|
-
key: JSON.stringify([
|
|
1499
|
-
key,
|
|
1500
|
-
"settings",
|
|
1501
|
-
""
|
|
1502
|
-
]),
|
|
1503
|
-
kind: "settings",
|
|
1504
|
-
status,
|
|
1505
|
-
properties: settings,
|
|
1506
|
-
files: useProject()
|
|
1507
|
-
});
|
|
1508
|
-
} else projectUsed = true;
|
|
1509
|
-
if (status !== "updated" || elements.length) {
|
|
1510
|
-
const { name, isStage } = b !== null && b !== void 0 ? b : a;
|
|
1511
|
-
targets.push({
|
|
1512
|
-
key,
|
|
1513
|
-
name,
|
|
1514
|
-
isStage,
|
|
1515
|
-
status,
|
|
1516
|
-
before: a,
|
|
1517
|
-
after: b,
|
|
1518
|
-
elements
|
|
1519
|
-
});
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
const project = changedKeys(before, after, (key) => key === "targets").map((property) => ({
|
|
1523
|
-
key: JSON.stringify([
|
|
1524
|
-
"project",
|
|
1525
|
-
"settings",
|
|
1526
|
-
property
|
|
1527
|
-
]),
|
|
1528
|
-
kind: "settings",
|
|
1529
|
-
status: statusOf(before[property], after[property]),
|
|
1530
|
-
properties: [property],
|
|
1531
|
-
files: useProject()
|
|
1532
|
-
}));
|
|
1533
|
-
if (projectUsed) explained.add(projectPath);
|
|
1534
|
-
return {
|
|
1535
|
-
targets,
|
|
1536
|
-
project,
|
|
1537
|
-
other: files.filter((file) => !explained.has(file.path))
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
1819
|
// src/github-pull-requests.ts
|
|
1541
1820
|
init_objectSpread2();
|
|
1542
1821
|
var PullRequestError = class extends Error {
|
|
@@ -2135,4 +2414,4 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
2135
2414
|
};
|
|
2136
2415
|
};
|
|
2137
2416
|
}
|
|
2138
|
-
export {
|
|
2417
|
+
export { useCostumeImage as _, gitHubRepositoryFromOrigin as a, parseProjectSource as b, assetFileName as c, compareProjectForReview as d, BlockPreview as f, useAssetImage as g, currentCostume as h, createGitHubPullRequests as i, organizeProjectChanges as l, usePreviewSync as m, createScratchBlocksDiffLocator as n, ProjectSourceComparison as o, createPreviewSync as p, PullRequestError as r, ProjectSourceReview as s, createScratchBlocksDiffRenderer as t, projectPropertyLabel as u, compareProjectBlocks as v, readProjectSourceDiff as x, createProjectSourceDiff as y };
|