@kubohiroya/turbowarp-plus-editor-git 0.7.0 → 0.9.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/README.ja.md +22 -6
- package/README.md +9 -1
- package/dist/block-diff-preview.d.ts +18 -2
- package/dist/file-diff-view.d.ts +3 -1
- package/dist/index.js +359 -15
- package/dist/{libesm-OWRWzmu_.js → project-asset-paths-KV715791.js} +23 -1
- package/dist/project-asset-paths.d.ts +12 -0
- package/dist/project-assets.d.ts +23 -0
- package/dist/project-change-list.d.ts +81 -0
- package/dist/project-change-model.d.ts +73 -0
- package/dist/project-source-diff.d.ts +7 -1
- package/dist/project-source-diff.js +10 -7
- package/dist/project-source-review-model.d.ts +4 -1
- package/dist/project-source-review.d.ts +11 -2
- package/dist/{repository-DplMQiuo.js → repository-VBp-UNWc.js} +134 -3
- package/dist/repository.d.ts +34 -0
- package/dist/repository.js +1 -1
- package/dist/{scratch-block-diff-renderer-CLYoOIB2.js → scratch-block-diff-renderer-DZR8ep-T.js} +708 -90
- package/dist/scratch-block-diff-renderer.d.ts +69 -6
- package/dist/style.css +48 -2
- package/package.json +7 -2
- package/src/block-diff-preview.tsx +89 -8
- package/src/file-diff-view.tsx +4 -3
- package/src/github-pull-requests.ts +73 -6
- package/src/index.tsx +42 -12
- package/src/project-asset-paths.ts +33 -0
- package/src/project-assets.ts +78 -0
- package/src/project-change-list.tsx +210 -0
- package/src/project-change-model.ts +212 -0
- package/src/project-source-diff.css +31 -2
- package/src/project-source-diff.tsx +17 -7
- package/src/project-source-review-model.ts +4 -1
- package/src/project-source-review.tsx +67 -27
- package/src/repository.ts +107 -2
- package/src/scratch-block-diff-renderer.ts +129 -11
- package/src/style.css +17 -0
package/dist/{scratch-block-diff-renderer-CLYoOIB2.js → scratch-block-diff-renderer-DZR8ep-T.js}
RENAMED
|
@@ -1,52 +1,6 @@
|
|
|
1
1
|
import { n as init_objectSpread2, t as _objectSpread2 } from "./objectSpread2-Dkd68nJH.js";
|
|
2
|
-
import { n as hasOnlyUnixLineEndings, r as hasOnlyWinLineEndings, t as
|
|
2
|
+
import { a as createTwoFilesPatch, i as isAssetName, n as assetsDirectoryParts, o as hasOnlyUnixLineEndings, r as folderSettingsLimit, s as hasOnlyWinLineEndings, t as assetLimit } from "./project-asset-paths-KV715791.js";
|
|
3
3
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
// src/block-diff-preview.tsx
|
|
5
|
-
init_objectSpread2();
|
|
6
|
-
function BlockPreview({ side, title, parts, marks, renderBlock, withContext, labels, onLocate, locationStatus }) {
|
|
7
|
-
const host = useRef(null);
|
|
8
|
-
const [status, setStatus] = useState("loading");
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
if (!side || !host.current) return;
|
|
11
|
-
const container = host.current;
|
|
12
|
-
const abort = new AbortController();
|
|
13
|
-
let dispose;
|
|
14
|
-
setStatus("loading");
|
|
15
|
-
Promise.resolve().then(() => renderBlock(_objectSpread2(_objectSpread2({}, side), {}, {
|
|
16
|
-
container,
|
|
17
|
-
withContext,
|
|
18
|
-
parts,
|
|
19
|
-
marks,
|
|
20
|
-
signal: abort.signal
|
|
21
|
-
}))).then((cleanup) => {
|
|
22
|
-
if (abort.signal.aborted) cleanup === null || cleanup === void 0 || cleanup();
|
|
23
|
-
else {
|
|
24
|
-
dispose = cleanup;
|
|
25
|
-
setStatus("ready");
|
|
26
|
-
}
|
|
27
|
-
}, () => {
|
|
28
|
-
if (!abort.signal.aborted) setStatus("failed");
|
|
29
|
-
});
|
|
30
|
-
return () => {
|
|
31
|
-
abort.abort();
|
|
32
|
-
dispose === null || dispose === void 0 || dispose();
|
|
33
|
-
};
|
|
34
|
-
}, [
|
|
35
|
-
side,
|
|
36
|
-
renderBlock,
|
|
37
|
-
withContext,
|
|
38
|
-
parts,
|
|
39
|
-
marks
|
|
40
|
-
]);
|
|
41
|
-
return /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-side" }, /* @__PURE__ */ React.createElement("h4", null, title), !side && /* @__PURE__ */ React.createElement("p", null, labels.absent), side && status === "loading" && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.loading), side && status === "failed" && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.unavailable), /* @__PURE__ */ React.createElement("div", {
|
|
42
|
-
ref: host,
|
|
43
|
-
className: "twgit-block-diff-preview"
|
|
44
|
-
}), side && onLocate && /* @__PURE__ */ React.createElement("button", {
|
|
45
|
-
type: "button",
|
|
46
|
-
"aria-label": `${title} · ${labels.locate}`,
|
|
47
|
-
onClick: onLocate
|
|
48
|
-
}, labels.locate), locationStatus && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels[locationStatus]));
|
|
49
|
-
}
|
|
50
4
|
// node_modules/diff/libesm/patch/line-endings.js
|
|
51
5
|
function unixToWin(patch) {
|
|
52
6
|
if (Array.isArray(patch)) return patch.map((p) => unixToWin(p));
|
|
@@ -560,6 +514,7 @@ function applyStructuredPatch(source, patch, options = {}) {
|
|
|
560
514
|
return resultLines.join("\n");
|
|
561
515
|
}
|
|
562
516
|
// \0@oxc-project+runtime@0.156.0/helpers/esm/objectWithoutPropertiesLoose.js
|
|
517
|
+
init_objectSpread2();
|
|
563
518
|
function _objectWithoutPropertiesLoose(r, e) {
|
|
564
519
|
if (null == r) return {};
|
|
565
520
|
var t = {};
|
|
@@ -580,7 +535,6 @@ function _objectWithoutProperties(e, t) {
|
|
|
580
535
|
return i;
|
|
581
536
|
}
|
|
582
537
|
// src/project-source-patch.ts
|
|
583
|
-
init_objectSpread2();
|
|
584
538
|
var _excluded = ["blocks"];
|
|
585
539
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
586
540
|
function readProject(source) {
|
|
@@ -698,6 +652,211 @@ function compareProjectBlocks(before, after) {
|
|
|
698
652
|
otherChanges: !equal(withoutBlocks(before), withoutBlocks(after))
|
|
699
653
|
};
|
|
700
654
|
}
|
|
655
|
+
// src/project-assets.ts
|
|
656
|
+
var imageTypes = {
|
|
657
|
+
svg: "image/svg+xml",
|
|
658
|
+
png: "image/png",
|
|
659
|
+
jpg: "image/jpeg",
|
|
660
|
+
jpeg: "image/jpeg",
|
|
661
|
+
gif: "image/gif",
|
|
662
|
+
bmp: "image/bmp",
|
|
663
|
+
webp: "image/webp"
|
|
664
|
+
};
|
|
665
|
+
/** The costume a sprite shows, or the backdrop the stage shows, when the project data names an image file for it. */
|
|
666
|
+
function currentCostume(target) {
|
|
667
|
+
const { costumes, currentCostume: index } = target;
|
|
668
|
+
if (!Array.isArray(costumes)) return null;
|
|
669
|
+
const costume = costumes[typeof index === "number" && Number.isInteger(index) && index >= 0 ? index : 0];
|
|
670
|
+
if (!isRecord(costume)) return null;
|
|
671
|
+
const md5ext = typeof costume.md5ext === "string" ? costume.md5ext : typeof costume.assetId === "string" && typeof costume.dataFormat === "string" ? costume.assetId + "." + costume.dataFormat : "";
|
|
672
|
+
const match = /^[0-9A-Za-z_-]{1,128}\.([0-9A-Za-z]{1,8})$/.exec(md5ext);
|
|
673
|
+
const type = match ? imageTypes[match[1].toLowerCase()] : void 0;
|
|
674
|
+
if (!type) return null;
|
|
675
|
+
return {
|
|
676
|
+
name: typeof costume.name === "string" ? costume.name : "",
|
|
677
|
+
md5ext,
|
|
678
|
+
type
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
var reads = /* @__PURE__ */ new WeakMap();
|
|
682
|
+
function readAsset(loadAsset, version, md5ext) {
|
|
683
|
+
let cache = reads.get(loadAsset);
|
|
684
|
+
if (!cache) reads.set(loadAsset, cache = /* @__PURE__ */ new Map());
|
|
685
|
+
const key = version + ":" + md5ext;
|
|
686
|
+
let read = cache.get(key);
|
|
687
|
+
if (!read) {
|
|
688
|
+
read = loadAsset({
|
|
689
|
+
version,
|
|
690
|
+
md5ext,
|
|
691
|
+
signal: new AbortController().signal
|
|
692
|
+
}).catch(() => null);
|
|
693
|
+
cache.set(key, read);
|
|
694
|
+
}
|
|
695
|
+
return read;
|
|
696
|
+
}
|
|
697
|
+
/** The image type of an asset file name, or `undefined` for sounds and anything that is not an image. */
|
|
698
|
+
function assetImageType(md5ext) {
|
|
699
|
+
const match = /^[0-9A-Za-z_-]{1,128}\.([0-9A-Za-z]{1,8})$/.exec(md5ext);
|
|
700
|
+
return match ? imageTypes[match[1].toLowerCase()] : void 0;
|
|
701
|
+
}
|
|
702
|
+
/** Loads one image asset of one version as an object URL, or `null` while it is unavailable or not an image. */
|
|
703
|
+
function useAssetImage(md5ext, version, loadAsset) {
|
|
704
|
+
const type = md5ext ? assetImageType(md5ext) : void 0;
|
|
705
|
+
const key = md5ext && type && loadAsset ? version + ":" + md5ext : "";
|
|
706
|
+
const [image, setImage] = useState();
|
|
707
|
+
useEffect(() => {
|
|
708
|
+
if (!key || !loadAsset) return;
|
|
709
|
+
let active = true;
|
|
710
|
+
let url;
|
|
711
|
+
readAsset(loadAsset, version, md5ext).then((blob) => {
|
|
712
|
+
if (!blob || !active) return;
|
|
713
|
+
url = URL.createObjectURL(new Blob([blob], { type }));
|
|
714
|
+
setImage({
|
|
715
|
+
key,
|
|
716
|
+
url
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
return () => {
|
|
720
|
+
active = false;
|
|
721
|
+
if (url) URL.revokeObjectURL(url);
|
|
722
|
+
};
|
|
723
|
+
}, [key, loadAsset]);
|
|
724
|
+
return (image === null || image === void 0 ? void 0 : image.key) === key ? image.url : null;
|
|
725
|
+
}
|
|
726
|
+
/** Loads the current costume of one version of a target as an object URL for an image, or `null` while it is unavailable. */
|
|
727
|
+
function useCostumeImage(target, version, loadAsset) {
|
|
728
|
+
const costume = target && loadAsset ? currentCostume(target) : null;
|
|
729
|
+
return useAssetImage(costume === null || costume === void 0 ? void 0 : costume.md5ext, version, loadAsset);
|
|
730
|
+
}
|
|
731
|
+
// src/block-diff-preview.tsx
|
|
732
|
+
init_objectSpread2();
|
|
733
|
+
function createPreviewSync() {
|
|
734
|
+
const members = /* @__PURE__ */ new Map();
|
|
735
|
+
let linked = true;
|
|
736
|
+
let moving = false;
|
|
737
|
+
const align = () => {
|
|
738
|
+
if (!linked || !members.size) return;
|
|
739
|
+
const views = [...members.keys()];
|
|
740
|
+
const fit = {
|
|
741
|
+
scale: Math.min(...views.map((view) => view.fit.scale)),
|
|
742
|
+
height: Math.max(...views.map((view) => view.fit.height))
|
|
743
|
+
};
|
|
744
|
+
for (const view of views) view.layout(fit);
|
|
745
|
+
};
|
|
746
|
+
return {
|
|
747
|
+
add(view) {
|
|
748
|
+
const stop = view.onChange(() => {
|
|
749
|
+
if (!linked || moving) return;
|
|
750
|
+
moving = true;
|
|
751
|
+
try {
|
|
752
|
+
const viewport = view.viewport();
|
|
753
|
+
for (const other of members.keys()) if (other !== view) other.setViewport(viewport);
|
|
754
|
+
} finally {
|
|
755
|
+
moving = false;
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
members.set(view, stop);
|
|
759
|
+
align();
|
|
760
|
+
return () => {
|
|
761
|
+
stop();
|
|
762
|
+
members.delete(view);
|
|
763
|
+
};
|
|
764
|
+
},
|
|
765
|
+
setLinked(value) {
|
|
766
|
+
if (linked === value) return;
|
|
767
|
+
linked = value;
|
|
768
|
+
align();
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
var linkLabels = {
|
|
773
|
+
en: "Move both versions together",
|
|
774
|
+
ja: "変更前と変更後を連動して動かす"
|
|
775
|
+
};
|
|
776
|
+
/** One sync group per comparison, with the checkbox that links or unlinks its previews. */
|
|
777
|
+
function usePreviewSync(locale) {
|
|
778
|
+
const sync = useMemo(createPreviewSync, []);
|
|
779
|
+
const [linked, setLinked] = useState(true);
|
|
780
|
+
useEffect(() => sync.setLinked(linked), [sync, linked]);
|
|
781
|
+
return {
|
|
782
|
+
sync,
|
|
783
|
+
toggle: /* @__PURE__ */ React.createElement("label", { className: "twgit-block-diff-link" }, /* @__PURE__ */ React.createElement("input", {
|
|
784
|
+
type: "checkbox",
|
|
785
|
+
checked: linked,
|
|
786
|
+
onChange: (event) => setLinked(event.currentTarget.checked)
|
|
787
|
+
}), " ", locale.startsWith("ja") ? linkLabels.ja : linkLabels.en)
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
var rendering = (result) => typeof result === "function" ? { release: result } : {
|
|
791
|
+
release: result && (() => result.dispose()),
|
|
792
|
+
view: result === null || result === void 0 ? void 0 : result.view
|
|
793
|
+
};
|
|
794
|
+
function BlockPreview({ side, title, parts, marks, renderBlock, withContext, labels, onLocate, locationStatus, version, loadAsset, sync }) {
|
|
795
|
+
const host = useRef(null);
|
|
796
|
+
const watermark = useRef(null);
|
|
797
|
+
const [status, setStatus] = useState("loading");
|
|
798
|
+
const thumbnail = !!(side && version && loadAsset && currentCostume(side.target));
|
|
799
|
+
const image = useCostumeImage(thumbnail ? side.target : void 0, version !== null && version !== void 0 ? version : "after", loadAsset);
|
|
800
|
+
useEffect(() => {
|
|
801
|
+
if (!side || !host.current) return;
|
|
802
|
+
const container = host.current;
|
|
803
|
+
const abort = new AbortController();
|
|
804
|
+
let dispose;
|
|
805
|
+
setStatus("loading");
|
|
806
|
+
Promise.resolve().then(() => {
|
|
807
|
+
var _watermark$current;
|
|
808
|
+
return renderBlock(_objectSpread2(_objectSpread2({}, side), {}, {
|
|
809
|
+
container,
|
|
810
|
+
withContext,
|
|
811
|
+
parts,
|
|
812
|
+
marks,
|
|
813
|
+
signal: abort.signal,
|
|
814
|
+
watermark: thumbnail ? (_watermark$current = watermark.current) !== null && _watermark$current !== void 0 ? _watermark$current : void 0 : void 0
|
|
815
|
+
}));
|
|
816
|
+
}).then((result) => {
|
|
817
|
+
const { release, view } = rendering(result);
|
|
818
|
+
if (abort.signal.aborted) {
|
|
819
|
+
release === null || release === void 0 || release();
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const leave = view && (sync === null || sync === void 0 ? void 0 : sync.add(view));
|
|
823
|
+
dispose = () => {
|
|
824
|
+
leave === null || leave === void 0 || leave();
|
|
825
|
+
release === null || release === void 0 || release();
|
|
826
|
+
};
|
|
827
|
+
setStatus("ready");
|
|
828
|
+
}, () => {
|
|
829
|
+
if (!abort.signal.aborted) setStatus("failed");
|
|
830
|
+
});
|
|
831
|
+
return () => {
|
|
832
|
+
abort.abort();
|
|
833
|
+
dispose === null || dispose === void 0 || dispose();
|
|
834
|
+
};
|
|
835
|
+
}, [
|
|
836
|
+
side,
|
|
837
|
+
renderBlock,
|
|
838
|
+
withContext,
|
|
839
|
+
parts,
|
|
840
|
+
marks,
|
|
841
|
+
sync,
|
|
842
|
+
thumbnail
|
|
843
|
+
]);
|
|
844
|
+
return /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-side" }, /* @__PURE__ */ React.createElement("h4", null, title), !side && /* @__PURE__ */ React.createElement("p", null, labels.absent), side && status === "loading" && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.loading), side && status === "failed" && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.unavailable), side && /* @__PURE__ */ React.createElement("div", {
|
|
845
|
+
ref: host,
|
|
846
|
+
className: "twgit-block-diff-preview"
|
|
847
|
+
}, thumbnail && /* @__PURE__ */ React.createElement("div", {
|
|
848
|
+
ref: watermark,
|
|
849
|
+
className: "twgit-block-diff-watermark",
|
|
850
|
+
"data-dockview-costume-thumbnail": ""
|
|
851
|
+
}, image && /* @__PURE__ */ React.createElement("img", {
|
|
852
|
+
src: image,
|
|
853
|
+
alt: ""
|
|
854
|
+
}))), side && onLocate && /* @__PURE__ */ React.createElement("button", {
|
|
855
|
+
type: "button",
|
|
856
|
+
"aria-label": `${title} · ${labels.locate}`,
|
|
857
|
+
onClick: onLocate
|
|
858
|
+
}, labels.locate), locationStatus && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels[locationStatus]));
|
|
859
|
+
}
|
|
701
860
|
// src/project-source-review-model.ts
|
|
702
861
|
init_objectSpread2();
|
|
703
862
|
function scriptRoots(target) {
|
|
@@ -807,7 +966,9 @@ function compareProjectForReview(before, after) {
|
|
|
807
966
|
name,
|
|
808
967
|
isStage,
|
|
809
968
|
scripts,
|
|
810
|
-
properties
|
|
969
|
+
properties,
|
|
970
|
+
before: a,
|
|
971
|
+
after: b
|
|
811
972
|
});
|
|
812
973
|
}
|
|
813
974
|
}
|
|
@@ -817,24 +978,25 @@ function compareProjectForReview(before, after) {
|
|
|
817
978
|
};
|
|
818
979
|
}
|
|
819
980
|
// src/project-source-review.tsx
|
|
981
|
+
init_objectSpread2();
|
|
820
982
|
var ja = {
|
|
821
983
|
review: "変更レビュー",
|
|
822
984
|
fullDescription: "PR 本文を表示",
|
|
823
985
|
checks: "確認してほしいこと",
|
|
824
986
|
files: "変更ファイル",
|
|
825
987
|
targets: "影響するステージ・スプライト",
|
|
826
|
-
scripts: "
|
|
988
|
+
scripts: "コードの変更",
|
|
989
|
+
scriptCount: (count) => `コードの変更 · ${count} か所`,
|
|
827
990
|
reviewed: "確認済み",
|
|
828
|
-
reviewedScripts: "スクリプト確認済み",
|
|
829
991
|
before: "変更前",
|
|
830
992
|
after: "変更後",
|
|
831
993
|
updated: "変更",
|
|
832
994
|
added: "追加",
|
|
833
995
|
removed: "削除",
|
|
834
996
|
reorganized: "再編",
|
|
835
|
-
absent: "
|
|
836
|
-
loading: "
|
|
837
|
-
unavailable: "
|
|
997
|
+
absent: "この版にはこのコードがありません",
|
|
998
|
+
loading: "コードを描画中…",
|
|
999
|
+
unavailable: "このコードを描画できません。",
|
|
838
1000
|
related: "関連ファイル",
|
|
839
1001
|
properties: "コード以外の変更",
|
|
840
1002
|
invalid: "この作品の差分を読み込めません。",
|
|
@@ -842,7 +1004,7 @@ var ja = {
|
|
|
842
1004
|
broadcast: "を受け取ったとき",
|
|
843
1005
|
key: "キーが押されたとき",
|
|
844
1006
|
click: "このスプライトが押されたとき",
|
|
845
|
-
script: "
|
|
1007
|
+
script: "コード",
|
|
846
1008
|
noChanges: "作品に内容の変更はありません。",
|
|
847
1009
|
comparison: "作品の変更",
|
|
848
1010
|
unreadable: "作品データとして読み込めないため、テキストの差分を表示します。"
|
|
@@ -853,18 +1015,18 @@ var en = {
|
|
|
853
1015
|
checks: "Review checklist",
|
|
854
1016
|
files: "Changed files",
|
|
855
1017
|
targets: "Affected stages and sprites",
|
|
856
|
-
scripts: "
|
|
1018
|
+
scripts: "Code changes",
|
|
1019
|
+
scriptCount: (count) => `Code changes · ${count}`,
|
|
857
1020
|
reviewed: "Reviewed",
|
|
858
|
-
reviewedScripts: "Reviewed scripts",
|
|
859
1021
|
before: "Before",
|
|
860
1022
|
after: "After",
|
|
861
1023
|
updated: "Changed",
|
|
862
1024
|
added: "Added",
|
|
863
1025
|
removed: "Removed",
|
|
864
1026
|
reorganized: "Reorganized",
|
|
865
|
-
absent: "No
|
|
866
|
-
loading: "Rendering
|
|
867
|
-
unavailable: "This
|
|
1027
|
+
absent: "No code in this version",
|
|
1028
|
+
loading: "Rendering code…",
|
|
1029
|
+
unavailable: "This code could not be rendered.",
|
|
868
1030
|
related: "Related file",
|
|
869
1031
|
properties: "Changes outside the code",
|
|
870
1032
|
invalid: "This project diff could not be read.",
|
|
@@ -872,7 +1034,7 @@ var en = {
|
|
|
872
1034
|
broadcast: "received",
|
|
873
1035
|
key: "When key pressed",
|
|
874
1036
|
click: "When this sprite clicked",
|
|
875
|
-
script: "
|
|
1037
|
+
script: "Code",
|
|
876
1038
|
noChanges: "No changes to the project contents.",
|
|
877
1039
|
comparison: "Project changes",
|
|
878
1040
|
unreadable: "This file could not be read as project data, so its text diff is shown."
|
|
@@ -906,8 +1068,9 @@ function scriptTitle(side, labels) {
|
|
|
906
1068
|
}
|
|
907
1069
|
return typeof block.opcode === "string" ? block.opcode : labels.script;
|
|
908
1070
|
}
|
|
909
|
-
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock }) {
|
|
1071
|
+
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset }) {
|
|
910
1072
|
const [open, setOpen] = useState(false);
|
|
1073
|
+
const { sync, toggle } = usePreviewSync(labels === ja ? "ja" : "en");
|
|
911
1074
|
const status = !script.before.length ? "added" : !script.after.length ? "removed" : script.before.length !== 1 || script.after.length !== 1 ? "reorganized" : "updated";
|
|
912
1075
|
const beforeTitle = script.before[0] && scriptTitle(script.before[0], labels);
|
|
913
1076
|
const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
|
|
@@ -922,13 +1085,16 @@ function ScriptComparison({ script, labels, reviewed = false, setReviewed, rende
|
|
|
922
1085
|
marks: side.marks,
|
|
923
1086
|
renderBlock,
|
|
924
1087
|
withContext: true,
|
|
925
|
-
labels
|
|
1088
|
+
labels,
|
|
1089
|
+
version,
|
|
1090
|
+
loadAsset,
|
|
1091
|
+
sync
|
|
926
1092
|
})) : /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
927
1093
|
title: labels[version],
|
|
928
1094
|
renderBlock,
|
|
929
1095
|
withContext: true,
|
|
930
1096
|
labels
|
|
931
|
-
})))), setReviewed && /* @__PURE__ */ React.createElement("label", { className: "twgit-review-checked" }, /* @__PURE__ */ React.createElement("input", {
|
|
1097
|
+
})))), toggle, setReviewed && /* @__PURE__ */ React.createElement("label", { className: "twgit-review-checked" }, /* @__PURE__ */ React.createElement("input", {
|
|
932
1098
|
type: "checkbox",
|
|
933
1099
|
checked: reviewed,
|
|
934
1100
|
onChange: (event) => setReviewed(event.currentTarget.checked)
|
|
@@ -940,12 +1106,41 @@ function propertyNames(keys, labels) {
|
|
|
940
1106
|
return (_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 : key;
|
|
941
1107
|
}).join(" · ");
|
|
942
1108
|
}
|
|
943
|
-
/**
|
|
944
|
-
|
|
1109
|
+
/**
|
|
1110
|
+
* The current costume (the backdrop for the stage) of each version of a target that has one. A changed costume shows
|
|
1111
|
+
* before → after. Nothing is shown until an image is read, so the heading falls back to the name alone.
|
|
1112
|
+
*/
|
|
1113
|
+
function TargetThumbnails({ target, labels, loadAsset }) {
|
|
1114
|
+
const images = {
|
|
1115
|
+
before: useCostumeImage(target.before, "before", loadAsset),
|
|
1116
|
+
after: useCostumeImage(target.after, "after", loadAsset)
|
|
1117
|
+
};
|
|
1118
|
+
const costumes = {
|
|
1119
|
+
before: target.before && currentCostume(target.before),
|
|
1120
|
+
after: target.after && currentCostume(target.after)
|
|
1121
|
+
};
|
|
1122
|
+
const changed = !!costumes.before && !!costumes.after && costumes.before.md5ext !== costumes.after.md5ext;
|
|
1123
|
+
const shown = (changed ? ["before", "after"] : [images.after ? "after" : "before"]).filter((version) => images[version]);
|
|
1124
|
+
if (!shown.length) return null;
|
|
1125
|
+
return /* @__PURE__ */ React.createElement("span", { className: "twgit-review-thumbnails" }, shown.map((version, index) => {
|
|
1126
|
+
var _costumes$version, _costumes$version$nam, _costumes$version2;
|
|
1127
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: version }, index > 0 && /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "→"), /* @__PURE__ */ React.createElement("img", {
|
|
1128
|
+
src: images[version],
|
|
1129
|
+
title: ((_costumes$version = costumes[version]) === null || _costumes$version === void 0 ? void 0 : _costumes$version.name) || void 0,
|
|
1130
|
+
alt: changed ? `${labels[version]}: ${(_costumes$version$nam = (_costumes$version2 = costumes[version]) === null || _costumes$version2 === void 0 ? void 0 : _costumes$version2.name) !== null && _costumes$version$nam !== void 0 ? _costumes$version$nam : ""}` : ""
|
|
1131
|
+
}));
|
|
1132
|
+
}));
|
|
1133
|
+
}
|
|
1134
|
+
/** Lists the changed stages and sprites of one project file, each with its code comparisons. */
|
|
1135
|
+
function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, review }) {
|
|
945
1136
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, comparison.properties.length > 0 && /* @__PURE__ */ React.createElement("p", null, labels.properties, " · ", propertyNames(comparison.properties, labels)), !comparison.targets.length && !comparison.properties.length && /* @__PURE__ */ React.createElement("p", null, labels.noChanges), comparison.targets.map((target) => /* @__PURE__ */ React.createElement("section", {
|
|
946
1137
|
className: "twgit-review-target",
|
|
947
1138
|
key: JSON.stringify([target.isStage, target.name])
|
|
948
|
-
}, /* @__PURE__ */ React.createElement("h5", null,
|
|
1139
|
+
}, /* @__PURE__ */ React.createElement("h5", null, /* @__PURE__ */ React.createElement(TargetThumbnails, {
|
|
1140
|
+
target,
|
|
1141
|
+
labels,
|
|
1142
|
+
loadAsset
|
|
1143
|
+
}), 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) => {
|
|
949
1144
|
const key = JSON.stringify([
|
|
950
1145
|
review === null || review === void 0 ? void 0 : review.file,
|
|
951
1146
|
target.isStage,
|
|
@@ -957,22 +1152,34 @@ function ComparisonTargets({ comparison, labels, renderBlock, review }) {
|
|
|
957
1152
|
script,
|
|
958
1153
|
labels,
|
|
959
1154
|
renderBlock,
|
|
1155
|
+
loadAsset,
|
|
960
1156
|
reviewed: review === null || review === void 0 ? void 0 : review.keys.has(key),
|
|
961
1157
|
setReviewed: review && ((value) => review.set(key, value))
|
|
962
1158
|
});
|
|
963
1159
|
}))));
|
|
964
1160
|
}
|
|
965
1161
|
var countScripts = (comparison) => comparison.targets.reduce((count, target) => count + target.scripts.length, 0);
|
|
966
|
-
/** Compares two versions of a project.source.json by stage, sprite and
|
|
967
|
-
function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fallback }) {
|
|
1162
|
+
/** Compares two versions of a project.source.json by stage, sprite and code, as the pull request review does. */
|
|
1163
|
+
function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fallback, loadAsset, target }) {
|
|
968
1164
|
const labels = locale.startsWith("ja") ? ja : en;
|
|
1165
|
+
const only = target && JSON.stringify([target.isStage, target.isStage || target.name]);
|
|
969
1166
|
const comparison = useMemo(() => {
|
|
1167
|
+
let result;
|
|
970
1168
|
try {
|
|
971
|
-
|
|
1169
|
+
result = compareProjectForReview(parseProjectSource(before), parseProjectSource(after));
|
|
972
1170
|
} catch (_unused) {
|
|
973
1171
|
return null;
|
|
974
1172
|
}
|
|
975
|
-
|
|
1173
|
+
if (!only) return result;
|
|
1174
|
+
return {
|
|
1175
|
+
properties: [],
|
|
1176
|
+
targets: result.targets.filter((item) => JSON.stringify([item.isStage, item.isStage || item.name]) === only).map((item) => _objectSpread2(_objectSpread2({}, item), {}, { properties: [] }))
|
|
1177
|
+
};
|
|
1178
|
+
}, [
|
|
1179
|
+
before,
|
|
1180
|
+
after,
|
|
1181
|
+
only
|
|
1182
|
+
]);
|
|
976
1183
|
if (!comparison) return fallback === void 0 ? /* @__PURE__ */ React.createElement("p", { role: "alert" }, labels.invalid) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", {
|
|
977
1184
|
role: "alert",
|
|
978
1185
|
className: "twgit-review-unreadable"
|
|
@@ -980,10 +1187,11 @@ function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fa
|
|
|
980
1187
|
return /* @__PURE__ */ React.createElement("section", {
|
|
981
1188
|
className: "twgit-block-diff twgit-review twgit-review-comparison",
|
|
982
1189
|
"aria-label": labels.comparison
|
|
983
|
-
}, /* @__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, {
|
|
1190
|
+
}, !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, {
|
|
984
1191
|
comparison,
|
|
985
1192
|
labels,
|
|
986
|
-
renderBlock
|
|
1193
|
+
renderBlock,
|
|
1194
|
+
loadAsset
|
|
987
1195
|
}));
|
|
988
1196
|
}
|
|
989
1197
|
function ProjectSourceReview({ title, description, verification, files, renderBlock, locale = "en" }) {
|
|
@@ -1028,13 +1236,14 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1028
1236
|
return /* @__PURE__ */ React.createElement("section", {
|
|
1029
1237
|
className: "twgit-block-diff twgit-review",
|
|
1030
1238
|
"aria-label": labels.review
|
|
1031
|
-
}, /* @__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.
|
|
1239
|
+
}, /* @__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", {
|
|
1032
1240
|
key: file.path,
|
|
1033
1241
|
className: "twgit-review-file"
|
|
1034
1242
|
}, /* @__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("p", null, labels.related, " · ", labels[file.status]), (invalid || file.unavailable) && /* @__PURE__ */ React.createElement("p", { role: "alert" }, labels.invalid), comparison && /* @__PURE__ */ React.createElement(ComparisonTargets, {
|
|
1035
1243
|
comparison,
|
|
1036
1244
|
labels,
|
|
1037
1245
|
renderBlock,
|
|
1246
|
+
loadAsset: "loadAsset" in file ? file.loadAsset : void 0,
|
|
1038
1247
|
review: {
|
|
1039
1248
|
file: file.path,
|
|
1040
1249
|
keys: reviewed,
|
|
@@ -1042,6 +1251,236 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1042
1251
|
}
|
|
1043
1252
|
}))));
|
|
1044
1253
|
}
|
|
1254
|
+
// src/project-change-model.ts
|
|
1255
|
+
init_objectSpread2();
|
|
1256
|
+
var targetDataKeys = [
|
|
1257
|
+
"variables",
|
|
1258
|
+
"lists",
|
|
1259
|
+
"broadcasts"
|
|
1260
|
+
];
|
|
1261
|
+
var nonSettingKeys = /* @__PURE__ */ new Set([
|
|
1262
|
+
"blocks",
|
|
1263
|
+
"costumes",
|
|
1264
|
+
"sounds",
|
|
1265
|
+
...targetDataKeys
|
|
1266
|
+
]);
|
|
1267
|
+
var propertyLabels = /* @__PURE__ */ new Map([
|
|
1268
|
+
["variables", ["変数", "Variables"]],
|
|
1269
|
+
["lists", ["リスト", "Lists"]],
|
|
1270
|
+
["broadcasts", ["メッセージ", "Broadcast messages"]],
|
|
1271
|
+
["costumes", ["コスチューム・背景", "Costumes and backdrops"]],
|
|
1272
|
+
["sounds", ["音", "Sounds"]],
|
|
1273
|
+
["blocks", ["コード", "Code"]],
|
|
1274
|
+
["x", ["X 座標", "X position"]],
|
|
1275
|
+
["y", ["Y 座標", "Y position"]],
|
|
1276
|
+
["size", ["大きさ", "Size"]],
|
|
1277
|
+
["direction", ["向き", "Direction"]],
|
|
1278
|
+
["visible", ["表示状態", "Visibility"]],
|
|
1279
|
+
["currentCostume", ["表示するコスチューム", "Selected costume"]],
|
|
1280
|
+
["layerOrder", ["重なり順", "Layer order"]],
|
|
1281
|
+
["volume", ["音量", "Volume"]],
|
|
1282
|
+
["comments", ["コメント", "Comments"]],
|
|
1283
|
+
["draggable", ["ドラッグ可否", "Draggable"]],
|
|
1284
|
+
["rotationStyle", ["回転方法", "Rotation style"]],
|
|
1285
|
+
["tempo", ["テンポ", "Tempo"]],
|
|
1286
|
+
["videoTransparency", ["ビデオの透明度", "Video transparency"]],
|
|
1287
|
+
["videoState", ["ビデオの状態", "Video state"]],
|
|
1288
|
+
["textToSpeechLanguage", ["音声合成の言語", "Text to speech language"]],
|
|
1289
|
+
["monitors", ["モニター", "Monitors"]],
|
|
1290
|
+
["extensions", ["拡張機能", "Extensions"]],
|
|
1291
|
+
["meta", ["作品の情報", "Project metadata"]]
|
|
1292
|
+
]);
|
|
1293
|
+
/** The Japanese or English name of a project or target property key; unknown keys are returned as they are. */
|
|
1294
|
+
function projectPropertyLabel(key, locale = "en") {
|
|
1295
|
+
var _propertyLabels$get, _propertyLabels$get2;
|
|
1296
|
+
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;
|
|
1297
|
+
}
|
|
1298
|
+
/** The asset file name of a costume or sound entry: `md5ext`, or `assetId.dataFormat`; `''` when it has neither. */
|
|
1299
|
+
function assetFileName(entry) {
|
|
1300
|
+
if (typeof entry.md5ext === "string") return entry.md5ext;
|
|
1301
|
+
return typeof entry.assetId === "string" && typeof entry.dataFormat === "string" ? entry.assetId + "." + entry.dataFormat : "";
|
|
1302
|
+
}
|
|
1303
|
+
function joinPath(...parts) {
|
|
1304
|
+
return parts.flatMap((part) => part.split("/")).filter((part) => part !== "" && part !== ".").join("/");
|
|
1305
|
+
}
|
|
1306
|
+
function statusOf(before, after) {
|
|
1307
|
+
return before === void 0 ? "added" : after === void 0 ? "removed" : "updated";
|
|
1308
|
+
}
|
|
1309
|
+
function entriesOf(target, kind) {
|
|
1310
|
+
const list = target === null || target === void 0 ? void 0 : target[kind];
|
|
1311
|
+
return Array.isArray(list) ? list.filter(isRecord) : [];
|
|
1312
|
+
}
|
|
1313
|
+
function nameOf(entry) {
|
|
1314
|
+
return typeof entry.name === "string" ? entry.name : "";
|
|
1315
|
+
}
|
|
1316
|
+
/** Pairs the entries of two versions by name (repeated names pair in order): after's order first, then removed ones. */
|
|
1317
|
+
function pairEntries(before, after) {
|
|
1318
|
+
var _remaining$get;
|
|
1319
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
1320
|
+
for (const entry of before) {
|
|
1321
|
+
const queue = remaining.get(nameOf(entry));
|
|
1322
|
+
if (queue) queue.push(entry);
|
|
1323
|
+
else remaining.set(nameOf(entry), [entry]);
|
|
1324
|
+
}
|
|
1325
|
+
const pairs = [];
|
|
1326
|
+
for (const entry of after) pairs.push({
|
|
1327
|
+
name: nameOf(entry),
|
|
1328
|
+
before: (_remaining$get = remaining.get(nameOf(entry))) === null || _remaining$get === void 0 ? void 0 : _remaining$get.shift(),
|
|
1329
|
+
after: entry
|
|
1330
|
+
});
|
|
1331
|
+
for (const entry of before) {
|
|
1332
|
+
const queue = remaining.get(nameOf(entry));
|
|
1333
|
+
if ((queue === null || queue === void 0 ? void 0 : queue[0]) === entry) pairs.push({
|
|
1334
|
+
name: nameOf(entry),
|
|
1335
|
+
before: queue.shift()
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
return pairs;
|
|
1339
|
+
}
|
|
1340
|
+
function changedKeys(before, after, skip) {
|
|
1341
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(after), ...Object.keys(before)])].filter((key) => !skip(key) && !equal(before[key], after[key]));
|
|
1342
|
+
}
|
|
1343
|
+
/** Arranges the changed files of one project folder by stage, sprite and element. Pure; reads nothing. */
|
|
1344
|
+
function organizeProjectChanges(input) {
|
|
1345
|
+
const { folder, files, assetsDirectory } = input;
|
|
1346
|
+
const normalize = (project) => project === null ? { targets: [] } : _objectSpread2(_objectSpread2({}, project), {}, { targets: project.targets.map((target) => {
|
|
1347
|
+
var _target$blocks;
|
|
1348
|
+
return _objectSpread2(_objectSpread2({}, target), {}, { blocks: (_target$blocks = target.blocks) !== null && _target$blocks !== void 0 ? _target$blocks : {} });
|
|
1349
|
+
}) });
|
|
1350
|
+
const before = normalize(input.before), after = normalize(input.after);
|
|
1351
|
+
const projectPath = joinPath(folder, "project.source.json");
|
|
1352
|
+
const projectFiles = files.filter((file) => file.path === projectPath);
|
|
1353
|
+
const explained = /* @__PURE__ */ new Set();
|
|
1354
|
+
let projectUsed = false;
|
|
1355
|
+
const useProject = () => {
|
|
1356
|
+
projectUsed = true;
|
|
1357
|
+
return [...projectFiles];
|
|
1358
|
+
};
|
|
1359
|
+
const filesAt = (paths) => {
|
|
1360
|
+
const wanted = new Set(paths.filter((path) => path !== void 0));
|
|
1361
|
+
const found = files.filter((file) => wanted.has(file.path));
|
|
1362
|
+
for (const file of found) explained.add(file.path);
|
|
1363
|
+
return found;
|
|
1364
|
+
};
|
|
1365
|
+
const review = compareProjectForReview(before, after);
|
|
1366
|
+
const scripts = new Map(review.targets.map((target) => {
|
|
1367
|
+
var _target$after;
|
|
1368
|
+
return [targetKey((_target$after = target.after) !== null && _target$after !== void 0 ? _target$after : target.before), target.scripts.length];
|
|
1369
|
+
}));
|
|
1370
|
+
const oldTargets = new Map(before.targets.map((target) => [targetKey(target), target]));
|
|
1371
|
+
const newTargets = new Map(after.targets.map((target) => [targetKey(target), target]));
|
|
1372
|
+
const keys = [...new Set([...after.targets, ...before.targets].map(targetKey))];
|
|
1373
|
+
keys.sort((a, b) => Number(b === "stage") - Number(a === "stage"));
|
|
1374
|
+
const targets = [];
|
|
1375
|
+
for (const key of keys) {
|
|
1376
|
+
var _scripts$get;
|
|
1377
|
+
const a = oldTargets.get(key), b = newTargets.get(key);
|
|
1378
|
+
const status = statusOf(a, b);
|
|
1379
|
+
const elements = [];
|
|
1380
|
+
const count = (_scripts$get = scripts.get(key)) !== null && _scripts$get !== void 0 ? _scripts$get : 0;
|
|
1381
|
+
if (count) elements.push({
|
|
1382
|
+
key: JSON.stringify([
|
|
1383
|
+
key,
|
|
1384
|
+
"code",
|
|
1385
|
+
""
|
|
1386
|
+
]),
|
|
1387
|
+
kind: "code",
|
|
1388
|
+
status,
|
|
1389
|
+
scripts: count,
|
|
1390
|
+
files: useProject()
|
|
1391
|
+
});
|
|
1392
|
+
for (const kind of ["costumes", "sounds"]) {
|
|
1393
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1394
|
+
for (const pair of pairEntries(entriesOf(a, kind), entriesOf(b, kind))) {
|
|
1395
|
+
var _seen$get;
|
|
1396
|
+
const md5ext = {
|
|
1397
|
+
before: pair.before && assetFileName(pair.before) || void 0,
|
|
1398
|
+
after: pair.after && assetFileName(pair.after) || void 0
|
|
1399
|
+
};
|
|
1400
|
+
const differs = !equal(pair.before, pair.after);
|
|
1401
|
+
const assets = filesAt([md5ext.before && joinPath(folder, assetsDirectory.before, md5ext.before), md5ext.after && joinPath(folder, assetsDirectory.after, md5ext.after)]);
|
|
1402
|
+
if (!differs && !assets.length) continue;
|
|
1403
|
+
const repeat = (_seen$get = seen.get(pair.name)) !== null && _seen$get !== void 0 ? _seen$get : 0;
|
|
1404
|
+
seen.set(pair.name, repeat + 1);
|
|
1405
|
+
elements.push({
|
|
1406
|
+
key: JSON.stringify(repeat ? [
|
|
1407
|
+
key,
|
|
1408
|
+
kind,
|
|
1409
|
+
pair.name,
|
|
1410
|
+
repeat
|
|
1411
|
+
] : [
|
|
1412
|
+
key,
|
|
1413
|
+
kind,
|
|
1414
|
+
pair.name
|
|
1415
|
+
]),
|
|
1416
|
+
kind,
|
|
1417
|
+
status: statusOf(pair.before, pair.after),
|
|
1418
|
+
name: pair.name,
|
|
1419
|
+
before: pair.before,
|
|
1420
|
+
after: pair.after,
|
|
1421
|
+
md5ext,
|
|
1422
|
+
files: [...differs ? useProject() : [], ...assets]
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (a && b) {
|
|
1427
|
+
const oldFields = a, newFields = b;
|
|
1428
|
+
const data = changedKeys(oldFields, newFields, (property) => !targetDataKeys.includes(property));
|
|
1429
|
+
if (data.length) elements.push({
|
|
1430
|
+
key: JSON.stringify([
|
|
1431
|
+
key,
|
|
1432
|
+
"data",
|
|
1433
|
+
""
|
|
1434
|
+
]),
|
|
1435
|
+
kind: "data",
|
|
1436
|
+
status,
|
|
1437
|
+
properties: data,
|
|
1438
|
+
files: useProject()
|
|
1439
|
+
});
|
|
1440
|
+
const settings = changedKeys(oldFields, newFields, (property) => nonSettingKeys.has(property));
|
|
1441
|
+
if (settings.length) elements.push({
|
|
1442
|
+
key: JSON.stringify([
|
|
1443
|
+
key,
|
|
1444
|
+
"settings",
|
|
1445
|
+
""
|
|
1446
|
+
]),
|
|
1447
|
+
kind: "settings",
|
|
1448
|
+
status,
|
|
1449
|
+
properties: settings,
|
|
1450
|
+
files: useProject()
|
|
1451
|
+
});
|
|
1452
|
+
} else projectUsed = true;
|
|
1453
|
+
if (status !== "updated" || elements.length) {
|
|
1454
|
+
const { name, isStage } = b !== null && b !== void 0 ? b : a;
|
|
1455
|
+
targets.push({
|
|
1456
|
+
key,
|
|
1457
|
+
name,
|
|
1458
|
+
isStage,
|
|
1459
|
+
status,
|
|
1460
|
+
before: a,
|
|
1461
|
+
after: b,
|
|
1462
|
+
elements
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const project = changedKeys(before, after, (key) => key === "targets").map((property) => ({
|
|
1467
|
+
key: JSON.stringify([
|
|
1468
|
+
"project",
|
|
1469
|
+
"settings",
|
|
1470
|
+
property
|
|
1471
|
+
]),
|
|
1472
|
+
kind: "settings",
|
|
1473
|
+
status: statusOf(before[property], after[property]),
|
|
1474
|
+
properties: [property],
|
|
1475
|
+
files: useProject()
|
|
1476
|
+
}));
|
|
1477
|
+
if (projectUsed) explained.add(projectPath);
|
|
1478
|
+
return {
|
|
1479
|
+
targets,
|
|
1480
|
+
project,
|
|
1481
|
+
other: files.filter((file) => !explained.has(file.path))
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1045
1484
|
// src/github-pull-requests.ts
|
|
1046
1485
|
init_objectSpread2();
|
|
1047
1486
|
var PullRequestError = class extends Error {
|
|
@@ -1065,25 +1504,29 @@ function repoPath(repository) {
|
|
|
1065
1504
|
var invalidResponse = () => new PullRequestError("failed");
|
|
1066
1505
|
var emptyProject = "{\"targets\":[]}\n";
|
|
1067
1506
|
var sourceLimit = 1048576;
|
|
1068
|
-
async function
|
|
1507
|
+
async function readBody(response, limit, chunk) {
|
|
1069
1508
|
if (Number(response.headers.get("Content-Length")) > limit || !response.body) throw invalidResponse();
|
|
1070
1509
|
const reader = response.body.getReader();
|
|
1071
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1072
|
-
const chunks = [];
|
|
1073
1510
|
let length = 0;
|
|
1074
1511
|
try {
|
|
1075
1512
|
for (;;) {
|
|
1076
1513
|
const { done, value } = await reader.read();
|
|
1077
|
-
if (done) return
|
|
1514
|
+
if (done) return;
|
|
1078
1515
|
length += value.byteLength;
|
|
1079
1516
|
if (length > limit) throw invalidResponse();
|
|
1080
|
-
|
|
1517
|
+
chunk(value);
|
|
1081
1518
|
}
|
|
1082
1519
|
} catch (error) {
|
|
1083
1520
|
reader.cancel().catch(() => {});
|
|
1084
1521
|
throw error;
|
|
1085
1522
|
}
|
|
1086
1523
|
}
|
|
1524
|
+
async function readText(response, limit) {
|
|
1525
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1526
|
+
const chunks = [];
|
|
1527
|
+
await readBody(response, limit, (value) => chunks.push(decoder.decode(value, { stream: true })));
|
|
1528
|
+
return chunks.join("") + decoder.decode();
|
|
1529
|
+
}
|
|
1087
1530
|
function readSummary(value) {
|
|
1088
1531
|
if (!isRecord(value) || !Number.isSafeInteger(value.number) || Number(value.number) < 1 || typeof value.title !== "string") throw invalidResponse();
|
|
1089
1532
|
return {
|
|
@@ -1129,6 +1572,63 @@ function contentPath(path) {
|
|
|
1129
1572
|
if (parts.some((part) => !part || part === "." || part === "..")) throw invalidResponse();
|
|
1130
1573
|
return parts.map(encodeURIComponent).join("/");
|
|
1131
1574
|
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Reads the assets of both versions of a project file from raw.githubusercontent.com, which does not count against the
|
|
1577
|
+
* API rate limit. Each version's sb3-source.json names its assets directory. Every read is capped, honors both its own
|
|
1578
|
+
* signal and the pull request's, and resolves `null` on any failure.
|
|
1579
|
+
*/
|
|
1580
|
+
function rawAssetLoader(versions, send, review) {
|
|
1581
|
+
const directories = /* @__PURE__ */ new Map();
|
|
1582
|
+
const get = (url, signal) => send(url, {
|
|
1583
|
+
credentials: "omit",
|
|
1584
|
+
redirect: "error",
|
|
1585
|
+
signal
|
|
1586
|
+
});
|
|
1587
|
+
const folderUrl = ({ repository, sha, path }) => "https://raw.githubusercontent.com/" + repository.split("/").map(encodeURIComponent).join("/") + "/" + sha + "/" + contentPath(path).split("/").slice(0, -1).map((part) => part + "/").join("");
|
|
1588
|
+
const readDirectory = async (folder, signal) => {
|
|
1589
|
+
let settings = null;
|
|
1590
|
+
try {
|
|
1591
|
+
var _response$body;
|
|
1592
|
+
const response = await get(folder + "sb3-source.json", signal);
|
|
1593
|
+
if (response.ok) settings = await readText(response, folderSettingsLimit);
|
|
1594
|
+
else (_response$body = response.body) === null || _response$body === void 0 || _response$body.cancel().catch(() => {});
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
if (signal.aborted) throw error;
|
|
1597
|
+
}
|
|
1598
|
+
return assetsDirectoryParts(settings);
|
|
1599
|
+
};
|
|
1600
|
+
return async ({ version, md5ext, signal: own }) => {
|
|
1601
|
+
const location = versions[version];
|
|
1602
|
+
if (!location || !isAssetName(md5ext)) return null;
|
|
1603
|
+
try {
|
|
1604
|
+
const signal = AbortSignal.any([own, review]);
|
|
1605
|
+
if (signal.aborted) return null;
|
|
1606
|
+
const folder = folderUrl(location);
|
|
1607
|
+
let directory = directories.get(version);
|
|
1608
|
+
if (!directory) {
|
|
1609
|
+
directory = readDirectory(folder, signal);
|
|
1610
|
+
directories.set(version, directory);
|
|
1611
|
+
const pending = directory;
|
|
1612
|
+
pending.catch(() => {
|
|
1613
|
+
if (directories.get(version) === pending) directories.delete(version);
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
const parts = await directory;
|
|
1617
|
+
if (!parts || signal.aborted) return null;
|
|
1618
|
+
const response = await get(folder + parts.map(encodeURIComponent).join("/") + "/" + md5ext, signal);
|
|
1619
|
+
if (!response.ok) {
|
|
1620
|
+
var _response$body2;
|
|
1621
|
+
(_response$body2 = response.body) === null || _response$body2 === void 0 || _response$body2.cancel().catch(() => {});
|
|
1622
|
+
return null;
|
|
1623
|
+
}
|
|
1624
|
+
const chunks = [];
|
|
1625
|
+
await readBody(response, assetLimit, (value) => chunks.push(value));
|
|
1626
|
+
return signal.aborted ? null : new Blob(chunks);
|
|
1627
|
+
} catch (_unused) {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1132
1632
|
function createGitHubPullRequests(repository, send = fetch) {
|
|
1133
1633
|
if (!validRepository(repository)) throw invalidResponse();
|
|
1134
1634
|
const api = "https://api.github.com";
|
|
@@ -1193,13 +1693,26 @@ function createGitHubPullRequests(repository, send = fetch) {
|
|
|
1193
1693
|
continue;
|
|
1194
1694
|
}
|
|
1195
1695
|
try {
|
|
1196
|
-
var _file$previous, _pr$head$repository;
|
|
1696
|
+
var _file$previous, _pr$head$repository, _file$previous2, _pr$head$repository2;
|
|
1197
1697
|
if (status !== "added" && !base) throw invalidResponse();
|
|
1198
1698
|
const [before, after] = await Promise.all([status === "added" ? emptyProject : source(repository, (_file$previous = file.previous) !== null && _file$previous !== void 0 ? _file$previous : file.path, base, signal), status === "removed" ? emptyProject : source((_pr$head$repository = pr.head.repository) !== null && _pr$head$repository !== void 0 ? _pr$head$repository : repository, file.path, pr.head.sha, signal)]);
|
|
1699
|
+
const loadAsset = rawAssetLoader({
|
|
1700
|
+
before: status === "added" ? null : {
|
|
1701
|
+
repository,
|
|
1702
|
+
sha: base,
|
|
1703
|
+
path: (_file$previous2 = file.previous) !== null && _file$previous2 !== void 0 ? _file$previous2 : file.path
|
|
1704
|
+
},
|
|
1705
|
+
after: status === "removed" ? null : {
|
|
1706
|
+
repository: (_pr$head$repository2 = pr.head.repository) !== null && _pr$head$repository2 !== void 0 ? _pr$head$repository2 : repository,
|
|
1707
|
+
sha: pr.head.sha,
|
|
1708
|
+
path: file.path
|
|
1709
|
+
}
|
|
1710
|
+
}, send, signal);
|
|
1199
1711
|
files.push({
|
|
1200
1712
|
path: file.path,
|
|
1201
1713
|
previousPath: file.previous,
|
|
1202
|
-
patch: createProjectSourceDiff(before, after)
|
|
1714
|
+
patch: createProjectSourceDiff(before, after),
|
|
1715
|
+
loadAsset
|
|
1203
1716
|
});
|
|
1204
1717
|
} catch (error) {
|
|
1205
1718
|
if (signal.aborted) throw error;
|
|
@@ -1372,9 +1885,93 @@ function placeholderDefinition(element, opcode, type, blockly, locale) {
|
|
|
1372
1885
|
}
|
|
1373
1886
|
return definition;
|
|
1374
1887
|
}
|
|
1888
|
+
var defaultScale = .675;
|
|
1889
|
+
var padding = 16;
|
|
1890
|
+
var minHeight = 160;
|
|
1891
|
+
var maxHeight = 420;
|
|
1892
|
+
/**
|
|
1893
|
+
* Sizes a preview frame to its blocks and reports scrolling and zooming, so both versions can move together.
|
|
1894
|
+
* The frame keeps a gutter on the right for the zoom buttons, and for the minimap and thumbnail when present.
|
|
1895
|
+
*/
|
|
1896
|
+
function createView(workspace, blockly, surface, frame, startScale, navigation) {
|
|
1897
|
+
var _bounds$x, _bounds$y, _workspace$translate, _surface$ownerDocumen;
|
|
1898
|
+
const bounds = workspace.getBlocksBoundingBox();
|
|
1899
|
+
const left = (_bounds$x = bounds.x) !== null && _bounds$x !== void 0 ? _bounds$x : 0, top = (_bounds$y = bounds.y) !== null && _bounds$y !== void 0 ? _bounds$y : 0;
|
|
1900
|
+
const gutter = navigation ? 150 : 56;
|
|
1901
|
+
const width = frame.clientWidth || 400;
|
|
1902
|
+
const scale = Math.max(.25, Math.min(startScale, (width - padding - gutter) / Math.max(1, bounds.width), 388 / Math.max(1, bounds.height)));
|
|
1903
|
+
const fit = {
|
|
1904
|
+
scale,
|
|
1905
|
+
height: Math.min(maxHeight, Math.max(minHeight, Math.ceil(bounds.height * scale + 32)))
|
|
1906
|
+
};
|
|
1907
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1908
|
+
const translate = (_workspace$translate = workspace.translate) === null || _workspace$translate === void 0 ? void 0 : _workspace$translate.bind(workspace);
|
|
1909
|
+
let quiet = false;
|
|
1910
|
+
if (translate) workspace.translate = (x, y) => {
|
|
1911
|
+
translate(x, y);
|
|
1912
|
+
if (!quiet) for (const listener of listeners) listener();
|
|
1913
|
+
};
|
|
1914
|
+
const setViewport = ({ scale, x, y }) => {
|
|
1915
|
+
if (!translate) return;
|
|
1916
|
+
quiet = true;
|
|
1917
|
+
try {
|
|
1918
|
+
var _workspace$getMetrics, _metrics$absoluteLeft, _metrics$absoluteTop, _workspace$scrollbar;
|
|
1919
|
+
workspace.scale = scale;
|
|
1920
|
+
workspace.scrollX = x;
|
|
1921
|
+
workspace.scrollY = y;
|
|
1922
|
+
const metrics = (_workspace$getMetrics = workspace.getMetrics) === null || _workspace$getMetrics === void 0 ? void 0 : _workspace$getMetrics.call(workspace);
|
|
1923
|
+
translate(x + ((_metrics$absoluteLeft = metrics === null || metrics === void 0 ? void 0 : metrics.absoluteLeft) !== null && _metrics$absoluteLeft !== void 0 ? _metrics$absoluteLeft : 0), y + ((_metrics$absoluteTop = metrics === null || metrics === void 0 ? void 0 : metrics.absoluteTop) !== null && _metrics$absoluteTop !== void 0 ? _metrics$absoluteTop : 0));
|
|
1924
|
+
(_workspace$scrollbar = workspace.scrollbar) === null || _workspace$scrollbar === void 0 || _workspace$scrollbar.resize();
|
|
1925
|
+
} finally {
|
|
1926
|
+
quiet = false;
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1929
|
+
const layout = ({ scale, height }) => {
|
|
1930
|
+
frame.style.height = height + "px";
|
|
1931
|
+
blockly.svgResize(workspace);
|
|
1932
|
+
setViewport({
|
|
1933
|
+
scale,
|
|
1934
|
+
x: padding - left * scale,
|
|
1935
|
+
y: padding - top * scale
|
|
1936
|
+
});
|
|
1937
|
+
};
|
|
1938
|
+
layout(fit);
|
|
1939
|
+
const resize = new ((_surface$ownerDocumen = surface.ownerDocument.defaultView) !== null && _surface$ownerDocumen !== void 0 ? _surface$ownerDocumen : globalThis).ResizeObserver(() => blockly.svgResize(workspace));
|
|
1940
|
+
resize.observe(surface);
|
|
1941
|
+
const wheel = (event) => {
|
|
1942
|
+
if (!event.ctrlKey) event.stopPropagation();
|
|
1943
|
+
};
|
|
1944
|
+
surface.addEventListener("wheel", wheel, { capture: true });
|
|
1945
|
+
return {
|
|
1946
|
+
api: {
|
|
1947
|
+
fit,
|
|
1948
|
+
layout,
|
|
1949
|
+
setViewport,
|
|
1950
|
+
viewport: () => {
|
|
1951
|
+
var _workspace$scale, _workspace$scrollX, _workspace$scrollY;
|
|
1952
|
+
return {
|
|
1953
|
+
scale: (_workspace$scale = workspace.scale) !== null && _workspace$scale !== void 0 ? _workspace$scale : fit.scale,
|
|
1954
|
+
x: (_workspace$scrollX = workspace.scrollX) !== null && _workspace$scrollX !== void 0 ? _workspace$scrollX : 0,
|
|
1955
|
+
y: (_workspace$scrollY = workspace.scrollY) !== null && _workspace$scrollY !== void 0 ? _workspace$scrollY : 0
|
|
1956
|
+
};
|
|
1957
|
+
},
|
|
1958
|
+
onChange(listener) {
|
|
1959
|
+
listeners.add(listener);
|
|
1960
|
+
return () => listeners.delete(listener);
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
dispose() {
|
|
1964
|
+
frame.style.removeProperty("height");
|
|
1965
|
+
listeners.clear();
|
|
1966
|
+
resize.disconnect();
|
|
1967
|
+
surface.removeEventListener("wheel", wheel, { capture: true });
|
|
1968
|
+
if (translate) workspace.translate = translate;
|
|
1969
|
+
}
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1375
1972
|
/** Use the host's Scratch VM serializer and Scratch Blocks definitions in an isolated workspace. */
|
|
1376
1973
|
function createScratchBlocksDiffRenderer(options) {
|
|
1377
|
-
return async ({ container, target, id, withContext, parts = [], marks, signal }) => {
|
|
1974
|
+
return async ({ container, target, id, withContext, parts = [], marks, signal, watermark }) => {
|
|
1378
1975
|
var _Object$values$find;
|
|
1379
1976
|
const blockly = await options.getBlockly();
|
|
1380
1977
|
if (signal.aborted) return;
|
|
@@ -1400,11 +1997,16 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1400
1997
|
surface.className = "twgit-block-diff-surface";
|
|
1401
1998
|
container.append(surface);
|
|
1402
1999
|
let workspace;
|
|
2000
|
+
let view;
|
|
2001
|
+
let navigation;
|
|
2002
|
+
const cleanups = [];
|
|
1403
2003
|
const placeholders = [];
|
|
1404
2004
|
const dispose = () => {
|
|
1405
2005
|
const active = blockly.getMainWorkspace();
|
|
2006
|
+
for (const cleanup of cleanups.splice(0)) cleanup();
|
|
1406
2007
|
blockly.Events.disable();
|
|
1407
2008
|
try {
|
|
2009
|
+
navigation === null || navigation === void 0 || navigation.dispose();
|
|
1408
2010
|
workspace === null || workspace === void 0 || workspace.dispose();
|
|
1409
2011
|
} finally {
|
|
1410
2012
|
if (active === workspace) blockly.mainWorkspace = main;
|
|
@@ -1415,7 +2017,7 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1415
2017
|
};
|
|
1416
2018
|
blockly.Events.disable();
|
|
1417
2019
|
try {
|
|
1418
|
-
var _main$options;
|
|
2020
|
+
var _main$options$zoomOpt, _main$options, _main$options2, _options$attachNaviga;
|
|
1419
2021
|
for (const element of Array.from(dom.querySelectorAll("block,shadow"))) {
|
|
1420
2022
|
var _element$getAttribute2, _options$locale;
|
|
1421
2023
|
const opcode = (_element$getAttribute2 = element.getAttribute("type")) !== null && _element$getAttribute2 !== void 0 ? _element$getAttribute2 : "";
|
|
@@ -1429,10 +2031,20 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1429
2031
|
blockly.defineBlocksWithJsonArray([placeholderDefinition(element, opcode, alias, blockly, (_options$locale = options.locale) !== null && _options$locale !== void 0 ? _options$locale : container.ownerDocument.documentElement.lang)]);
|
|
1430
2032
|
element.setAttribute("type", alias);
|
|
1431
2033
|
}
|
|
2034
|
+
const startScale = (_main$options$zoomOpt = main === null || main === void 0 || (_main$options = main.options) === null || _main$options === void 0 || (_main$options = _main$options.zoomOptions) === null || _main$options === void 0 ? void 0 : _main$options.startScale) !== null && _main$options$zoomOpt !== void 0 ? _main$options$zoomOpt : defaultScale;
|
|
1432
2035
|
workspace = blockly.inject(surface, {
|
|
1433
2036
|
readOnly: true,
|
|
1434
2037
|
sounds: false,
|
|
1435
|
-
media: main === null || main === void 0 || (_main$
|
|
2038
|
+
media: main === null || main === void 0 || (_main$options2 = main.options) === null || _main$options2 === void 0 ? void 0 : _main$options2.pathToMedia,
|
|
2039
|
+
scrollbars: true,
|
|
2040
|
+
zoom: {
|
|
2041
|
+
controls: true,
|
|
2042
|
+
wheel: true,
|
|
2043
|
+
startScale,
|
|
2044
|
+
maxScale: 4,
|
|
2045
|
+
minScale: .25,
|
|
2046
|
+
scaleSpeed: 1.2
|
|
2047
|
+
}
|
|
1436
2048
|
});
|
|
1437
2049
|
workspace.markFocused = () => {};
|
|
1438
2050
|
blockly.Xml.domToWorkspace(dom, workspace);
|
|
@@ -1447,10 +2059,13 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1447
2059
|
const changed = workspace.getBlockById(id === root ? serializedRoot : id);
|
|
1448
2060
|
if (changed) markBlockLocation(changed, parts);
|
|
1449
2061
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
2062
|
+
view = createView(workspace, blockly, surface, container, startScale, !!options.attachNavigation);
|
|
2063
|
+
cleanups.push(view.dispose);
|
|
2064
|
+
navigation = (_options$attachNaviga = options.attachNavigation) === null || _options$attachNaviga === void 0 ? void 0 : _options$attachNaviga.call(options, workspace, blockly, watermark);
|
|
2065
|
+
if (navigation && watermark) {
|
|
2066
|
+
watermark.dataset.navigation = "";
|
|
2067
|
+
cleanups.push(() => delete watermark.dataset.navigation);
|
|
2068
|
+
}
|
|
1454
2069
|
} catch (error) {
|
|
1455
2070
|
dispose();
|
|
1456
2071
|
throw error;
|
|
@@ -1458,7 +2073,10 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1458
2073
|
blockly.mainWorkspace = main;
|
|
1459
2074
|
blockly.Events.enable();
|
|
1460
2075
|
}
|
|
1461
|
-
return
|
|
2076
|
+
return {
|
|
2077
|
+
dispose,
|
|
2078
|
+
view: view.api
|
|
2079
|
+
};
|
|
1462
2080
|
};
|
|
1463
2081
|
}
|
|
1464
|
-
export { gitHubRepositoryFromOrigin as a,
|
|
2082
|
+
export { compareProjectBlocks as _, gitHubRepositoryFromOrigin as a, readProjectSourceDiff as b, projectPropertyLabel as c, BlockPreview as d, createPreviewSync as f, useCostumeImage as g, useAssetImage as h, createGitHubPullRequests as i, ProjectSourceComparison as l, currentCostume as m, createScratchBlocksDiffLocator as n, assetFileName as o, usePreviewSync as p, PullRequestError as r, organizeProjectChanges as s, createScratchBlocksDiffRenderer as t, ProjectSourceReview as u, createProjectSourceDiff as v, parseProjectSource as y };
|