@kubohiroya/turbowarp-plus-editor-git 0.7.0 → 0.8.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 +11 -4
- 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 +19 -0
- package/dist/project-source-diff.d.ts +5 -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 +6 -2
- package/dist/{repository-DplMQiuo.js → repository-BQkADJmK.js} +71 -3
- package/dist/repository.d.ts +10 -0
- package/dist/repository.js +1 -1
- package/dist/{scratch-block-diff-renderer-CLYoOIB2.js → scratch-block-diff-renderer-xS8OYsRl.js} +453 -87
- package/dist/scratch-block-diff-renderer.d.ts +69 -6
- package/dist/style.css +31 -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 +5 -2
- package/src/project-asset-paths.ts +33 -0
- package/src/project-assets.ts +66 -0
- package/src/project-source-diff.css +31 -2
- package/src/project-source-diff.tsx +14 -7
- package/src/project-source-review-model.ts +4 -1
- package/src/project-source-review.tsx +56 -23
- package/src/repository.ts +58 -2
- package/src/scratch-block-diff-renderer.ts +129 -11
package/dist/{scratch-block-diff-renderer-CLYoOIB2.js → scratch-block-diff-renderer-xS8OYsRl.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,201 @@ 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
|
+
/** Loads the current costume of one version of a target as an object URL for an image, or `null` while it is unavailable. */
|
|
698
|
+
function useCostumeImage(target, version, loadAsset) {
|
|
699
|
+
const costume = target && loadAsset ? currentCostume(target) : null;
|
|
700
|
+
const key = costume ? version + ":" + costume.md5ext : "";
|
|
701
|
+
const [image, setImage] = useState();
|
|
702
|
+
useEffect(() => {
|
|
703
|
+
if (!costume || !loadAsset) return;
|
|
704
|
+
let active = true;
|
|
705
|
+
let url;
|
|
706
|
+
readAsset(loadAsset, version, costume.md5ext).then((blob) => {
|
|
707
|
+
if (!blob || !active) return;
|
|
708
|
+
url = URL.createObjectURL(new Blob([blob], { type: costume.type }));
|
|
709
|
+
setImage({
|
|
710
|
+
key,
|
|
711
|
+
url
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
return () => {
|
|
715
|
+
active = false;
|
|
716
|
+
if (url) URL.revokeObjectURL(url);
|
|
717
|
+
};
|
|
718
|
+
}, [key, loadAsset]);
|
|
719
|
+
return (image === null || image === void 0 ? void 0 : image.key) === key ? image.url : null;
|
|
720
|
+
}
|
|
721
|
+
// src/block-diff-preview.tsx
|
|
722
|
+
init_objectSpread2();
|
|
723
|
+
function createPreviewSync() {
|
|
724
|
+
const members = /* @__PURE__ */ new Map();
|
|
725
|
+
let linked = true;
|
|
726
|
+
let moving = false;
|
|
727
|
+
const align = () => {
|
|
728
|
+
if (!linked || !members.size) return;
|
|
729
|
+
const views = [...members.keys()];
|
|
730
|
+
const fit = {
|
|
731
|
+
scale: Math.min(...views.map((view) => view.fit.scale)),
|
|
732
|
+
height: Math.max(...views.map((view) => view.fit.height))
|
|
733
|
+
};
|
|
734
|
+
for (const view of views) view.layout(fit);
|
|
735
|
+
};
|
|
736
|
+
return {
|
|
737
|
+
add(view) {
|
|
738
|
+
const stop = view.onChange(() => {
|
|
739
|
+
if (!linked || moving) return;
|
|
740
|
+
moving = true;
|
|
741
|
+
try {
|
|
742
|
+
const viewport = view.viewport();
|
|
743
|
+
for (const other of members.keys()) if (other !== view) other.setViewport(viewport);
|
|
744
|
+
} finally {
|
|
745
|
+
moving = false;
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
members.set(view, stop);
|
|
749
|
+
align();
|
|
750
|
+
return () => {
|
|
751
|
+
stop();
|
|
752
|
+
members.delete(view);
|
|
753
|
+
};
|
|
754
|
+
},
|
|
755
|
+
setLinked(value) {
|
|
756
|
+
if (linked === value) return;
|
|
757
|
+
linked = value;
|
|
758
|
+
align();
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
var linkLabels = {
|
|
763
|
+
en: "Move both versions together",
|
|
764
|
+
ja: "変更前と変更後を連動して動かす"
|
|
765
|
+
};
|
|
766
|
+
/** One sync group per comparison, with the checkbox that links or unlinks its previews. */
|
|
767
|
+
function usePreviewSync(locale) {
|
|
768
|
+
const sync = useMemo(createPreviewSync, []);
|
|
769
|
+
const [linked, setLinked] = useState(true);
|
|
770
|
+
useEffect(() => sync.setLinked(linked), [sync, linked]);
|
|
771
|
+
return {
|
|
772
|
+
sync,
|
|
773
|
+
toggle: /* @__PURE__ */ React.createElement("label", { className: "twgit-block-diff-link" }, /* @__PURE__ */ React.createElement("input", {
|
|
774
|
+
type: "checkbox",
|
|
775
|
+
checked: linked,
|
|
776
|
+
onChange: (event) => setLinked(event.currentTarget.checked)
|
|
777
|
+
}), " ", locale.startsWith("ja") ? linkLabels.ja : linkLabels.en)
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
var rendering = (result) => typeof result === "function" ? { release: result } : {
|
|
781
|
+
release: result && (() => result.dispose()),
|
|
782
|
+
view: result === null || result === void 0 ? void 0 : result.view
|
|
783
|
+
};
|
|
784
|
+
function BlockPreview({ side, title, parts, marks, renderBlock, withContext, labels, onLocate, locationStatus, version, loadAsset, sync }) {
|
|
785
|
+
const host = useRef(null);
|
|
786
|
+
const watermark = useRef(null);
|
|
787
|
+
const [status, setStatus] = useState("loading");
|
|
788
|
+
const thumbnail = !!(side && version && loadAsset && currentCostume(side.target));
|
|
789
|
+
const image = useCostumeImage(thumbnail ? side.target : void 0, version !== null && version !== void 0 ? version : "after", loadAsset);
|
|
790
|
+
useEffect(() => {
|
|
791
|
+
if (!side || !host.current) return;
|
|
792
|
+
const container = host.current;
|
|
793
|
+
const abort = new AbortController();
|
|
794
|
+
let dispose;
|
|
795
|
+
setStatus("loading");
|
|
796
|
+
Promise.resolve().then(() => {
|
|
797
|
+
var _watermark$current;
|
|
798
|
+
return renderBlock(_objectSpread2(_objectSpread2({}, side), {}, {
|
|
799
|
+
container,
|
|
800
|
+
withContext,
|
|
801
|
+
parts,
|
|
802
|
+
marks,
|
|
803
|
+
signal: abort.signal,
|
|
804
|
+
watermark: thumbnail ? (_watermark$current = watermark.current) !== null && _watermark$current !== void 0 ? _watermark$current : void 0 : void 0
|
|
805
|
+
}));
|
|
806
|
+
}).then((result) => {
|
|
807
|
+
const { release, view } = rendering(result);
|
|
808
|
+
if (abort.signal.aborted) {
|
|
809
|
+
release === null || release === void 0 || release();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
const leave = view && (sync === null || sync === void 0 ? void 0 : sync.add(view));
|
|
813
|
+
dispose = () => {
|
|
814
|
+
leave === null || leave === void 0 || leave();
|
|
815
|
+
release === null || release === void 0 || release();
|
|
816
|
+
};
|
|
817
|
+
setStatus("ready");
|
|
818
|
+
}, () => {
|
|
819
|
+
if (!abort.signal.aborted) setStatus("failed");
|
|
820
|
+
});
|
|
821
|
+
return () => {
|
|
822
|
+
abort.abort();
|
|
823
|
+
dispose === null || dispose === void 0 || dispose();
|
|
824
|
+
};
|
|
825
|
+
}, [
|
|
826
|
+
side,
|
|
827
|
+
renderBlock,
|
|
828
|
+
withContext,
|
|
829
|
+
parts,
|
|
830
|
+
marks,
|
|
831
|
+
sync,
|
|
832
|
+
thumbnail
|
|
833
|
+
]);
|
|
834
|
+
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", {
|
|
835
|
+
ref: host,
|
|
836
|
+
className: "twgit-block-diff-preview"
|
|
837
|
+
}, thumbnail && /* @__PURE__ */ React.createElement("div", {
|
|
838
|
+
ref: watermark,
|
|
839
|
+
className: "twgit-block-diff-watermark",
|
|
840
|
+
"data-dockview-costume-thumbnail": ""
|
|
841
|
+
}, image && /* @__PURE__ */ React.createElement("img", {
|
|
842
|
+
src: image,
|
|
843
|
+
alt: ""
|
|
844
|
+
}))), side && onLocate && /* @__PURE__ */ React.createElement("button", {
|
|
845
|
+
type: "button",
|
|
846
|
+
"aria-label": `${title} · ${labels.locate}`,
|
|
847
|
+
onClick: onLocate
|
|
848
|
+
}, labels.locate), locationStatus && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels[locationStatus]));
|
|
849
|
+
}
|
|
701
850
|
// src/project-source-review-model.ts
|
|
702
851
|
init_objectSpread2();
|
|
703
852
|
function scriptRoots(target) {
|
|
@@ -807,7 +956,9 @@ function compareProjectForReview(before, after) {
|
|
|
807
956
|
name,
|
|
808
957
|
isStage,
|
|
809
958
|
scripts,
|
|
810
|
-
properties
|
|
959
|
+
properties,
|
|
960
|
+
before: a,
|
|
961
|
+
after: b
|
|
811
962
|
});
|
|
812
963
|
}
|
|
813
964
|
}
|
|
@@ -823,18 +974,18 @@ var ja = {
|
|
|
823
974
|
checks: "確認してほしいこと",
|
|
824
975
|
files: "変更ファイル",
|
|
825
976
|
targets: "影響するステージ・スプライト",
|
|
826
|
-
scripts: "
|
|
977
|
+
scripts: "コードの変更",
|
|
978
|
+
scriptCount: (count) => `コードの変更 · ${count} か所`,
|
|
827
979
|
reviewed: "確認済み",
|
|
828
|
-
reviewedScripts: "スクリプト確認済み",
|
|
829
980
|
before: "変更前",
|
|
830
981
|
after: "変更後",
|
|
831
982
|
updated: "変更",
|
|
832
983
|
added: "追加",
|
|
833
984
|
removed: "削除",
|
|
834
985
|
reorganized: "再編",
|
|
835
|
-
absent: "
|
|
836
|
-
loading: "
|
|
837
|
-
unavailable: "
|
|
986
|
+
absent: "この版にはこのコードがありません",
|
|
987
|
+
loading: "コードを描画中…",
|
|
988
|
+
unavailable: "このコードを描画できません。",
|
|
838
989
|
related: "関連ファイル",
|
|
839
990
|
properties: "コード以外の変更",
|
|
840
991
|
invalid: "この作品の差分を読み込めません。",
|
|
@@ -842,7 +993,7 @@ var ja = {
|
|
|
842
993
|
broadcast: "を受け取ったとき",
|
|
843
994
|
key: "キーが押されたとき",
|
|
844
995
|
click: "このスプライトが押されたとき",
|
|
845
|
-
script: "
|
|
996
|
+
script: "コード",
|
|
846
997
|
noChanges: "作品に内容の変更はありません。",
|
|
847
998
|
comparison: "作品の変更",
|
|
848
999
|
unreadable: "作品データとして読み込めないため、テキストの差分を表示します。"
|
|
@@ -853,18 +1004,18 @@ var en = {
|
|
|
853
1004
|
checks: "Review checklist",
|
|
854
1005
|
files: "Changed files",
|
|
855
1006
|
targets: "Affected stages and sprites",
|
|
856
|
-
scripts: "
|
|
1007
|
+
scripts: "Code changes",
|
|
1008
|
+
scriptCount: (count) => `Code changes · ${count}`,
|
|
857
1009
|
reviewed: "Reviewed",
|
|
858
|
-
reviewedScripts: "Reviewed scripts",
|
|
859
1010
|
before: "Before",
|
|
860
1011
|
after: "After",
|
|
861
1012
|
updated: "Changed",
|
|
862
1013
|
added: "Added",
|
|
863
1014
|
removed: "Removed",
|
|
864
1015
|
reorganized: "Reorganized",
|
|
865
|
-
absent: "No
|
|
866
|
-
loading: "Rendering
|
|
867
|
-
unavailable: "This
|
|
1016
|
+
absent: "No code in this version",
|
|
1017
|
+
loading: "Rendering code…",
|
|
1018
|
+
unavailable: "This code could not be rendered.",
|
|
868
1019
|
related: "Related file",
|
|
869
1020
|
properties: "Changes outside the code",
|
|
870
1021
|
invalid: "This project diff could not be read.",
|
|
@@ -872,7 +1023,7 @@ var en = {
|
|
|
872
1023
|
broadcast: "received",
|
|
873
1024
|
key: "When key pressed",
|
|
874
1025
|
click: "When this sprite clicked",
|
|
875
|
-
script: "
|
|
1026
|
+
script: "Code",
|
|
876
1027
|
noChanges: "No changes to the project contents.",
|
|
877
1028
|
comparison: "Project changes",
|
|
878
1029
|
unreadable: "This file could not be read as project data, so its text diff is shown."
|
|
@@ -906,8 +1057,9 @@ function scriptTitle(side, labels) {
|
|
|
906
1057
|
}
|
|
907
1058
|
return typeof block.opcode === "string" ? block.opcode : labels.script;
|
|
908
1059
|
}
|
|
909
|
-
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock }) {
|
|
1060
|
+
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset }) {
|
|
910
1061
|
const [open, setOpen] = useState(false);
|
|
1062
|
+
const { sync, toggle } = usePreviewSync(labels === ja ? "ja" : "en");
|
|
911
1063
|
const status = !script.before.length ? "added" : !script.after.length ? "removed" : script.before.length !== 1 || script.after.length !== 1 ? "reorganized" : "updated";
|
|
912
1064
|
const beforeTitle = script.before[0] && scriptTitle(script.before[0], labels);
|
|
913
1065
|
const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
|
|
@@ -922,13 +1074,16 @@ function ScriptComparison({ script, labels, reviewed = false, setReviewed, rende
|
|
|
922
1074
|
marks: side.marks,
|
|
923
1075
|
renderBlock,
|
|
924
1076
|
withContext: true,
|
|
925
|
-
labels
|
|
1077
|
+
labels,
|
|
1078
|
+
version,
|
|
1079
|
+
loadAsset,
|
|
1080
|
+
sync
|
|
926
1081
|
})) : /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
927
1082
|
title: labels[version],
|
|
928
1083
|
renderBlock,
|
|
929
1084
|
withContext: true,
|
|
930
1085
|
labels
|
|
931
|
-
})))), setReviewed && /* @__PURE__ */ React.createElement("label", { className: "twgit-review-checked" }, /* @__PURE__ */ React.createElement("input", {
|
|
1086
|
+
})))), toggle, setReviewed && /* @__PURE__ */ React.createElement("label", { className: "twgit-review-checked" }, /* @__PURE__ */ React.createElement("input", {
|
|
932
1087
|
type: "checkbox",
|
|
933
1088
|
checked: reviewed,
|
|
934
1089
|
onChange: (event) => setReviewed(event.currentTarget.checked)
|
|
@@ -940,12 +1095,41 @@ function propertyNames(keys, labels) {
|
|
|
940
1095
|
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
1096
|
}).join(" · ");
|
|
942
1097
|
}
|
|
943
|
-
/**
|
|
944
|
-
|
|
1098
|
+
/**
|
|
1099
|
+
* The current costume (the backdrop for the stage) of each version of a target that has one. A changed costume shows
|
|
1100
|
+
* before → after. Nothing is shown until an image is read, so the heading falls back to the name alone.
|
|
1101
|
+
*/
|
|
1102
|
+
function TargetThumbnails({ target, labels, loadAsset }) {
|
|
1103
|
+
const images = {
|
|
1104
|
+
before: useCostumeImage(target.before, "before", loadAsset),
|
|
1105
|
+
after: useCostumeImage(target.after, "after", loadAsset)
|
|
1106
|
+
};
|
|
1107
|
+
const costumes = {
|
|
1108
|
+
before: target.before && currentCostume(target.before),
|
|
1109
|
+
after: target.after && currentCostume(target.after)
|
|
1110
|
+
};
|
|
1111
|
+
const changed = !!costumes.before && !!costumes.after && costumes.before.md5ext !== costumes.after.md5ext;
|
|
1112
|
+
const shown = (changed ? ["before", "after"] : [images.after ? "after" : "before"]).filter((version) => images[version]);
|
|
1113
|
+
if (!shown.length) return null;
|
|
1114
|
+
return /* @__PURE__ */ React.createElement("span", { className: "twgit-review-thumbnails" }, shown.map((version, index) => {
|
|
1115
|
+
var _costumes$version, _costumes$version$nam, _costumes$version2;
|
|
1116
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: version }, index > 0 && /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "→"), /* @__PURE__ */ React.createElement("img", {
|
|
1117
|
+
src: images[version],
|
|
1118
|
+
title: ((_costumes$version = costumes[version]) === null || _costumes$version === void 0 ? void 0 : _costumes$version.name) || void 0,
|
|
1119
|
+
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 : ""}` : ""
|
|
1120
|
+
}));
|
|
1121
|
+
}));
|
|
1122
|
+
}
|
|
1123
|
+
/** Lists the changed stages and sprites of one project file, each with its code comparisons. */
|
|
1124
|
+
function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, review }) {
|
|
945
1125
|
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
1126
|
className: "twgit-review-target",
|
|
947
1127
|
key: JSON.stringify([target.isStage, target.name])
|
|
948
|
-
}, /* @__PURE__ */ React.createElement("h5", null,
|
|
1128
|
+
}, /* @__PURE__ */ React.createElement("h5", null, /* @__PURE__ */ React.createElement(TargetThumbnails, {
|
|
1129
|
+
target,
|
|
1130
|
+
labels,
|
|
1131
|
+
loadAsset
|
|
1132
|
+
}), 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
1133
|
const key = JSON.stringify([
|
|
950
1134
|
review === null || review === void 0 ? void 0 : review.file,
|
|
951
1135
|
target.isStage,
|
|
@@ -957,14 +1141,15 @@ function ComparisonTargets({ comparison, labels, renderBlock, review }) {
|
|
|
957
1141
|
script,
|
|
958
1142
|
labels,
|
|
959
1143
|
renderBlock,
|
|
1144
|
+
loadAsset,
|
|
960
1145
|
reviewed: review === null || review === void 0 ? void 0 : review.keys.has(key),
|
|
961
1146
|
setReviewed: review && ((value) => review.set(key, value))
|
|
962
1147
|
});
|
|
963
1148
|
}))));
|
|
964
1149
|
}
|
|
965
1150
|
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 }) {
|
|
1151
|
+
/** Compares two versions of a project.source.json by stage, sprite and code, as the pull request review does. */
|
|
1152
|
+
function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fallback, loadAsset }) {
|
|
968
1153
|
const labels = locale.startsWith("ja") ? ja : en;
|
|
969
1154
|
const comparison = useMemo(() => {
|
|
970
1155
|
try {
|
|
@@ -983,7 +1168,8 @@ function ProjectSourceComparison({ before, after, renderBlock, locale = "en", fa
|
|
|
983
1168
|
}, /* @__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
1169
|
comparison,
|
|
985
1170
|
labels,
|
|
986
|
-
renderBlock
|
|
1171
|
+
renderBlock,
|
|
1172
|
+
loadAsset
|
|
987
1173
|
}));
|
|
988
1174
|
}
|
|
989
1175
|
function ProjectSourceReview({ title, description, verification, files, renderBlock, locale = "en" }) {
|
|
@@ -1028,13 +1214,14 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1028
1214
|
return /* @__PURE__ */ React.createElement("section", {
|
|
1029
1215
|
className: "twgit-block-diff twgit-review",
|
|
1030
1216
|
"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.
|
|
1217
|
+
}, /* @__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
1218
|
key: file.path,
|
|
1033
1219
|
className: "twgit-review-file"
|
|
1034
1220
|
}, /* @__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
1221
|
comparison,
|
|
1036
1222
|
labels,
|
|
1037
1223
|
renderBlock,
|
|
1224
|
+
loadAsset: "loadAsset" in file ? file.loadAsset : void 0,
|
|
1038
1225
|
review: {
|
|
1039
1226
|
file: file.path,
|
|
1040
1227
|
keys: reviewed,
|
|
@@ -1065,25 +1252,29 @@ function repoPath(repository) {
|
|
|
1065
1252
|
var invalidResponse = () => new PullRequestError("failed");
|
|
1066
1253
|
var emptyProject = "{\"targets\":[]}\n";
|
|
1067
1254
|
var sourceLimit = 1048576;
|
|
1068
|
-
async function
|
|
1255
|
+
async function readBody(response, limit, chunk) {
|
|
1069
1256
|
if (Number(response.headers.get("Content-Length")) > limit || !response.body) throw invalidResponse();
|
|
1070
1257
|
const reader = response.body.getReader();
|
|
1071
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1072
|
-
const chunks = [];
|
|
1073
1258
|
let length = 0;
|
|
1074
1259
|
try {
|
|
1075
1260
|
for (;;) {
|
|
1076
1261
|
const { done, value } = await reader.read();
|
|
1077
|
-
if (done) return
|
|
1262
|
+
if (done) return;
|
|
1078
1263
|
length += value.byteLength;
|
|
1079
1264
|
if (length > limit) throw invalidResponse();
|
|
1080
|
-
|
|
1265
|
+
chunk(value);
|
|
1081
1266
|
}
|
|
1082
1267
|
} catch (error) {
|
|
1083
1268
|
reader.cancel().catch(() => {});
|
|
1084
1269
|
throw error;
|
|
1085
1270
|
}
|
|
1086
1271
|
}
|
|
1272
|
+
async function readText(response, limit) {
|
|
1273
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1274
|
+
const chunks = [];
|
|
1275
|
+
await readBody(response, limit, (value) => chunks.push(decoder.decode(value, { stream: true })));
|
|
1276
|
+
return chunks.join("") + decoder.decode();
|
|
1277
|
+
}
|
|
1087
1278
|
function readSummary(value) {
|
|
1088
1279
|
if (!isRecord(value) || !Number.isSafeInteger(value.number) || Number(value.number) < 1 || typeof value.title !== "string") throw invalidResponse();
|
|
1089
1280
|
return {
|
|
@@ -1129,6 +1320,63 @@ function contentPath(path) {
|
|
|
1129
1320
|
if (parts.some((part) => !part || part === "." || part === "..")) throw invalidResponse();
|
|
1130
1321
|
return parts.map(encodeURIComponent).join("/");
|
|
1131
1322
|
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Reads the assets of both versions of a project file from raw.githubusercontent.com, which does not count against the
|
|
1325
|
+
* API rate limit. Each version's sb3-source.json names its assets directory. Every read is capped, honors both its own
|
|
1326
|
+
* signal and the pull request's, and resolves `null` on any failure.
|
|
1327
|
+
*/
|
|
1328
|
+
function rawAssetLoader(versions, send, review) {
|
|
1329
|
+
const directories = /* @__PURE__ */ new Map();
|
|
1330
|
+
const get = (url, signal) => send(url, {
|
|
1331
|
+
credentials: "omit",
|
|
1332
|
+
redirect: "error",
|
|
1333
|
+
signal
|
|
1334
|
+
});
|
|
1335
|
+
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("");
|
|
1336
|
+
const readDirectory = async (folder, signal) => {
|
|
1337
|
+
let settings = null;
|
|
1338
|
+
try {
|
|
1339
|
+
var _response$body;
|
|
1340
|
+
const response = await get(folder + "sb3-source.json", signal);
|
|
1341
|
+
if (response.ok) settings = await readText(response, folderSettingsLimit);
|
|
1342
|
+
else (_response$body = response.body) === null || _response$body === void 0 || _response$body.cancel().catch(() => {});
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if (signal.aborted) throw error;
|
|
1345
|
+
}
|
|
1346
|
+
return assetsDirectoryParts(settings);
|
|
1347
|
+
};
|
|
1348
|
+
return async ({ version, md5ext, signal: own }) => {
|
|
1349
|
+
const location = versions[version];
|
|
1350
|
+
if (!location || !isAssetName(md5ext)) return null;
|
|
1351
|
+
try {
|
|
1352
|
+
const signal = AbortSignal.any([own, review]);
|
|
1353
|
+
if (signal.aborted) return null;
|
|
1354
|
+
const folder = folderUrl(location);
|
|
1355
|
+
let directory = directories.get(version);
|
|
1356
|
+
if (!directory) {
|
|
1357
|
+
directory = readDirectory(folder, signal);
|
|
1358
|
+
directories.set(version, directory);
|
|
1359
|
+
const pending = directory;
|
|
1360
|
+
pending.catch(() => {
|
|
1361
|
+
if (directories.get(version) === pending) directories.delete(version);
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
const parts = await directory;
|
|
1365
|
+
if (!parts || signal.aborted) return null;
|
|
1366
|
+
const response = await get(folder + parts.map(encodeURIComponent).join("/") + "/" + md5ext, signal);
|
|
1367
|
+
if (!response.ok) {
|
|
1368
|
+
var _response$body2;
|
|
1369
|
+
(_response$body2 = response.body) === null || _response$body2 === void 0 || _response$body2.cancel().catch(() => {});
|
|
1370
|
+
return null;
|
|
1371
|
+
}
|
|
1372
|
+
const chunks = [];
|
|
1373
|
+
await readBody(response, assetLimit, (value) => chunks.push(value));
|
|
1374
|
+
return signal.aborted ? null : new Blob(chunks);
|
|
1375
|
+
} catch (_unused) {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1132
1380
|
function createGitHubPullRequests(repository, send = fetch) {
|
|
1133
1381
|
if (!validRepository(repository)) throw invalidResponse();
|
|
1134
1382
|
const api = "https://api.github.com";
|
|
@@ -1193,13 +1441,26 @@ function createGitHubPullRequests(repository, send = fetch) {
|
|
|
1193
1441
|
continue;
|
|
1194
1442
|
}
|
|
1195
1443
|
try {
|
|
1196
|
-
var _file$previous, _pr$head$repository;
|
|
1444
|
+
var _file$previous, _pr$head$repository, _file$previous2, _pr$head$repository2;
|
|
1197
1445
|
if (status !== "added" && !base) throw invalidResponse();
|
|
1198
1446
|
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)]);
|
|
1447
|
+
const loadAsset = rawAssetLoader({
|
|
1448
|
+
before: status === "added" ? null : {
|
|
1449
|
+
repository,
|
|
1450
|
+
sha: base,
|
|
1451
|
+
path: (_file$previous2 = file.previous) !== null && _file$previous2 !== void 0 ? _file$previous2 : file.path
|
|
1452
|
+
},
|
|
1453
|
+
after: status === "removed" ? null : {
|
|
1454
|
+
repository: (_pr$head$repository2 = pr.head.repository) !== null && _pr$head$repository2 !== void 0 ? _pr$head$repository2 : repository,
|
|
1455
|
+
sha: pr.head.sha,
|
|
1456
|
+
path: file.path
|
|
1457
|
+
}
|
|
1458
|
+
}, send, signal);
|
|
1199
1459
|
files.push({
|
|
1200
1460
|
path: file.path,
|
|
1201
1461
|
previousPath: file.previous,
|
|
1202
|
-
patch: createProjectSourceDiff(before, after)
|
|
1462
|
+
patch: createProjectSourceDiff(before, after),
|
|
1463
|
+
loadAsset
|
|
1203
1464
|
});
|
|
1204
1465
|
} catch (error) {
|
|
1205
1466
|
if (signal.aborted) throw error;
|
|
@@ -1372,9 +1633,93 @@ function placeholderDefinition(element, opcode, type, blockly, locale) {
|
|
|
1372
1633
|
}
|
|
1373
1634
|
return definition;
|
|
1374
1635
|
}
|
|
1636
|
+
var defaultScale = .675;
|
|
1637
|
+
var padding = 16;
|
|
1638
|
+
var minHeight = 160;
|
|
1639
|
+
var maxHeight = 420;
|
|
1640
|
+
/**
|
|
1641
|
+
* Sizes a preview frame to its blocks and reports scrolling and zooming, so both versions can move together.
|
|
1642
|
+
* The frame keeps a gutter on the right for the zoom buttons, and for the minimap and thumbnail when present.
|
|
1643
|
+
*/
|
|
1644
|
+
function createView(workspace, blockly, surface, frame, startScale, navigation) {
|
|
1645
|
+
var _bounds$x, _bounds$y, _workspace$translate, _surface$ownerDocumen;
|
|
1646
|
+
const bounds = workspace.getBlocksBoundingBox();
|
|
1647
|
+
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;
|
|
1648
|
+
const gutter = navigation ? 150 : 56;
|
|
1649
|
+
const width = frame.clientWidth || 400;
|
|
1650
|
+
const scale = Math.max(.25, Math.min(startScale, (width - padding - gutter) / Math.max(1, bounds.width), 388 / Math.max(1, bounds.height)));
|
|
1651
|
+
const fit = {
|
|
1652
|
+
scale,
|
|
1653
|
+
height: Math.min(maxHeight, Math.max(minHeight, Math.ceil(bounds.height * scale + 32)))
|
|
1654
|
+
};
|
|
1655
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1656
|
+
const translate = (_workspace$translate = workspace.translate) === null || _workspace$translate === void 0 ? void 0 : _workspace$translate.bind(workspace);
|
|
1657
|
+
let quiet = false;
|
|
1658
|
+
if (translate) workspace.translate = (x, y) => {
|
|
1659
|
+
translate(x, y);
|
|
1660
|
+
if (!quiet) for (const listener of listeners) listener();
|
|
1661
|
+
};
|
|
1662
|
+
const setViewport = ({ scale, x, y }) => {
|
|
1663
|
+
if (!translate) return;
|
|
1664
|
+
quiet = true;
|
|
1665
|
+
try {
|
|
1666
|
+
var _workspace$getMetrics, _metrics$absoluteLeft, _metrics$absoluteTop, _workspace$scrollbar;
|
|
1667
|
+
workspace.scale = scale;
|
|
1668
|
+
workspace.scrollX = x;
|
|
1669
|
+
workspace.scrollY = y;
|
|
1670
|
+
const metrics = (_workspace$getMetrics = workspace.getMetrics) === null || _workspace$getMetrics === void 0 ? void 0 : _workspace$getMetrics.call(workspace);
|
|
1671
|
+
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));
|
|
1672
|
+
(_workspace$scrollbar = workspace.scrollbar) === null || _workspace$scrollbar === void 0 || _workspace$scrollbar.resize();
|
|
1673
|
+
} finally {
|
|
1674
|
+
quiet = false;
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
const layout = ({ scale, height }) => {
|
|
1678
|
+
frame.style.height = height + "px";
|
|
1679
|
+
blockly.svgResize(workspace);
|
|
1680
|
+
setViewport({
|
|
1681
|
+
scale,
|
|
1682
|
+
x: padding - left * scale,
|
|
1683
|
+
y: padding - top * scale
|
|
1684
|
+
});
|
|
1685
|
+
};
|
|
1686
|
+
layout(fit);
|
|
1687
|
+
const resize = new ((_surface$ownerDocumen = surface.ownerDocument.defaultView) !== null && _surface$ownerDocumen !== void 0 ? _surface$ownerDocumen : globalThis).ResizeObserver(() => blockly.svgResize(workspace));
|
|
1688
|
+
resize.observe(surface);
|
|
1689
|
+
const wheel = (event) => {
|
|
1690
|
+
if (!event.ctrlKey) event.stopPropagation();
|
|
1691
|
+
};
|
|
1692
|
+
surface.addEventListener("wheel", wheel, { capture: true });
|
|
1693
|
+
return {
|
|
1694
|
+
api: {
|
|
1695
|
+
fit,
|
|
1696
|
+
layout,
|
|
1697
|
+
setViewport,
|
|
1698
|
+
viewport: () => {
|
|
1699
|
+
var _workspace$scale, _workspace$scrollX, _workspace$scrollY;
|
|
1700
|
+
return {
|
|
1701
|
+
scale: (_workspace$scale = workspace.scale) !== null && _workspace$scale !== void 0 ? _workspace$scale : fit.scale,
|
|
1702
|
+
x: (_workspace$scrollX = workspace.scrollX) !== null && _workspace$scrollX !== void 0 ? _workspace$scrollX : 0,
|
|
1703
|
+
y: (_workspace$scrollY = workspace.scrollY) !== null && _workspace$scrollY !== void 0 ? _workspace$scrollY : 0
|
|
1704
|
+
};
|
|
1705
|
+
},
|
|
1706
|
+
onChange(listener) {
|
|
1707
|
+
listeners.add(listener);
|
|
1708
|
+
return () => listeners.delete(listener);
|
|
1709
|
+
}
|
|
1710
|
+
},
|
|
1711
|
+
dispose() {
|
|
1712
|
+
frame.style.removeProperty("height");
|
|
1713
|
+
listeners.clear();
|
|
1714
|
+
resize.disconnect();
|
|
1715
|
+
surface.removeEventListener("wheel", wheel, { capture: true });
|
|
1716
|
+
if (translate) workspace.translate = translate;
|
|
1717
|
+
}
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1375
1720
|
/** Use the host's Scratch VM serializer and Scratch Blocks definitions in an isolated workspace. */
|
|
1376
1721
|
function createScratchBlocksDiffRenderer(options) {
|
|
1377
|
-
return async ({ container, target, id, withContext, parts = [], marks, signal }) => {
|
|
1722
|
+
return async ({ container, target, id, withContext, parts = [], marks, signal, watermark }) => {
|
|
1378
1723
|
var _Object$values$find;
|
|
1379
1724
|
const blockly = await options.getBlockly();
|
|
1380
1725
|
if (signal.aborted) return;
|
|
@@ -1400,11 +1745,16 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1400
1745
|
surface.className = "twgit-block-diff-surface";
|
|
1401
1746
|
container.append(surface);
|
|
1402
1747
|
let workspace;
|
|
1748
|
+
let view;
|
|
1749
|
+
let navigation;
|
|
1750
|
+
const cleanups = [];
|
|
1403
1751
|
const placeholders = [];
|
|
1404
1752
|
const dispose = () => {
|
|
1405
1753
|
const active = blockly.getMainWorkspace();
|
|
1754
|
+
for (const cleanup of cleanups.splice(0)) cleanup();
|
|
1406
1755
|
blockly.Events.disable();
|
|
1407
1756
|
try {
|
|
1757
|
+
navigation === null || navigation === void 0 || navigation.dispose();
|
|
1408
1758
|
workspace === null || workspace === void 0 || workspace.dispose();
|
|
1409
1759
|
} finally {
|
|
1410
1760
|
if (active === workspace) blockly.mainWorkspace = main;
|
|
@@ -1415,7 +1765,7 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1415
1765
|
};
|
|
1416
1766
|
blockly.Events.disable();
|
|
1417
1767
|
try {
|
|
1418
|
-
var _main$options;
|
|
1768
|
+
var _main$options$zoomOpt, _main$options, _main$options2, _options$attachNaviga;
|
|
1419
1769
|
for (const element of Array.from(dom.querySelectorAll("block,shadow"))) {
|
|
1420
1770
|
var _element$getAttribute2, _options$locale;
|
|
1421
1771
|
const opcode = (_element$getAttribute2 = element.getAttribute("type")) !== null && _element$getAttribute2 !== void 0 ? _element$getAttribute2 : "";
|
|
@@ -1429,10 +1779,20 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1429
1779
|
blockly.defineBlocksWithJsonArray([placeholderDefinition(element, opcode, alias, blockly, (_options$locale = options.locale) !== null && _options$locale !== void 0 ? _options$locale : container.ownerDocument.documentElement.lang)]);
|
|
1430
1780
|
element.setAttribute("type", alias);
|
|
1431
1781
|
}
|
|
1782
|
+
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
1783
|
workspace = blockly.inject(surface, {
|
|
1433
1784
|
readOnly: true,
|
|
1434
1785
|
sounds: false,
|
|
1435
|
-
media: main === null || main === void 0 || (_main$
|
|
1786
|
+
media: main === null || main === void 0 || (_main$options2 = main.options) === null || _main$options2 === void 0 ? void 0 : _main$options2.pathToMedia,
|
|
1787
|
+
scrollbars: true,
|
|
1788
|
+
zoom: {
|
|
1789
|
+
controls: true,
|
|
1790
|
+
wheel: true,
|
|
1791
|
+
startScale,
|
|
1792
|
+
maxScale: 4,
|
|
1793
|
+
minScale: .25,
|
|
1794
|
+
scaleSpeed: 1.2
|
|
1795
|
+
}
|
|
1436
1796
|
});
|
|
1437
1797
|
workspace.markFocused = () => {};
|
|
1438
1798
|
blockly.Xml.domToWorkspace(dom, workspace);
|
|
@@ -1447,10 +1807,13 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1447
1807
|
const changed = workspace.getBlockById(id === root ? serializedRoot : id);
|
|
1448
1808
|
if (changed) markBlockLocation(changed, parts);
|
|
1449
1809
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1810
|
+
view = createView(workspace, blockly, surface, container, startScale, !!options.attachNavigation);
|
|
1811
|
+
cleanups.push(view.dispose);
|
|
1812
|
+
navigation = (_options$attachNaviga = options.attachNavigation) === null || _options$attachNaviga === void 0 ? void 0 : _options$attachNaviga.call(options, workspace, blockly, watermark);
|
|
1813
|
+
if (navigation && watermark) {
|
|
1814
|
+
watermark.dataset.navigation = "";
|
|
1815
|
+
cleanups.push(() => delete watermark.dataset.navigation);
|
|
1816
|
+
}
|
|
1454
1817
|
} catch (error) {
|
|
1455
1818
|
dispose();
|
|
1456
1819
|
throw error;
|
|
@@ -1458,7 +1821,10 @@ function createScratchBlocksDiffRenderer(options) {
|
|
|
1458
1821
|
blockly.mainWorkspace = main;
|
|
1459
1822
|
blockly.Events.enable();
|
|
1460
1823
|
}
|
|
1461
|
-
return
|
|
1824
|
+
return {
|
|
1825
|
+
dispose,
|
|
1826
|
+
view: view.api
|
|
1827
|
+
};
|
|
1462
1828
|
};
|
|
1463
1829
|
}
|
|
1464
|
-
export { gitHubRepositoryFromOrigin as a,
|
|
1830
|
+
export { gitHubRepositoryFromOrigin as a, BlockPreview as c, currentCostume as d, compareProjectBlocks as f, createGitHubPullRequests as i, createPreviewSync as l, readProjectSourceDiff as m, createScratchBlocksDiffLocator as n, ProjectSourceComparison as o, createProjectSourceDiff as p, PullRequestError as r, ProjectSourceReview as s, createScratchBlocksDiffRenderer as t, usePreviewSync as u };
|