@kubohiroya/turbowarp-plus-editor-git 0.18.0 → 0.19.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 +10 -2
- package/README.md +10 -2
- package/dist/branch-button.d.ts +15 -0
- package/dist/change-marks.d.ts +8 -0
- package/dist/connection-settings.d.ts +3 -1
- package/dist/github-pull-requests.d.ts +2 -2
- package/dist/github-review-api.d.ts +112 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +377 -268
- package/dist/messages.d.ts +101 -0
- package/dist/{project-asset-paths-BzwVf9HE.js → project-asset-paths-DNaoWs3E.js} +1 -1
- package/dist/{project-comparison-BXOQcZ73.js → project-comparison-B_3QZ6M-.js} +1414 -33
- package/dist/project-source-diff.d.ts +5 -0
- package/dist/project-source-diff.js +4 -4
- package/dist/project-source-review.d.ts +6 -1
- package/dist/pull-request-panel.d.ts +2 -0
- package/dist/remote-operations.d.ts +1 -1
- package/dist/{repository-ByKRBftJ.js → repository-DXRZEtYk.js} +2 -3
- package/dist/repository.js +1 -1
- package/dist/review-composer.d.ts +23 -0
- package/dist/review-submit.d.ts +144 -0
- package/dist/{smart-http-BmHEkE7K.js → smart-http-eARzlXHO.js} +101 -2
- package/dist/smart-http.d.ts +2 -0
- package/dist/smart-http.js +1 -1
- package/dist/style.css +23 -0
- package/package.json +1 -1
- package/src/branch-button.tsx +79 -6
- package/src/change-marks.ts +22 -0
- package/src/connection-settings.tsx +9 -2
- package/src/github-review-api.ts +298 -0
- package/src/index.tsx +153 -15
- package/src/messages.ts +76 -2
- package/src/project-source-diff.css +10 -0
- package/src/project-source-diff.tsx +6 -0
- package/src/project-source-review.tsx +33 -5
- package/src/pull-request-panel.tsx +27 -6
- package/src/remote-operations.ts +1 -1
- package/src/review-composer.tsx +166 -0
- package/src/review-submit.ts +519 -0
- package/src/smart-http.ts +2 -2
- package/src/style.css +13 -0
- package/dist/objectSpread2-Dkd68nJH.js +0 -101
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as compareProjectBlocks, c as isRecord, d as targetKey, i as isAssetName, l as parseProjectSource, n as assetsDirectoryParts, o as createProjectSourceDiff, r as folderSettingsLimit, s as equal, t as assetLimit, u as readProjectSourceDiff } from "./project-asset-paths-
|
|
1
|
+
import { a as remoteUrl, l as _objectSpread2, r as checkConnection, u as init_objectSpread2 } from "./smart-http-eARzlXHO.js";
|
|
2
|
+
import { a as compareProjectBlocks, c as isRecord$2, d as targetKey, i as isAssetName, l as parseProjectSource, n as assetsDirectoryParts, o as createProjectSourceDiff, r as folderSettingsLimit, s as equal, t as assetLimit, u as readProjectSourceDiff } from "./project-asset-paths-DNaoWs3E.js";
|
|
3
3
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { render, unmountComponentAtNode } from "react-dom";
|
|
5
5
|
// src/close-button.tsx
|
|
@@ -41,7 +41,7 @@ function currentCostume(target) {
|
|
|
41
41
|
const { costumes, currentCostume: index } = target;
|
|
42
42
|
if (!Array.isArray(costumes)) return null;
|
|
43
43
|
const costume = costumes[typeof index === "number" && Number.isInteger(index) && index >= 0 ? index : 0];
|
|
44
|
-
if (!isRecord(costume)) return null;
|
|
44
|
+
if (!isRecord$2(costume)) return null;
|
|
45
45
|
const md5ext = typeof costume.md5ext === "string" ? costume.md5ext : typeof costume.assetId === "string" && typeof costume.dataFormat === "string" ? costume.assetId + "." + costume.dataFormat : "";
|
|
46
46
|
const match = /^[0-9A-Za-z_-]{1,128}\.([0-9A-Za-z]{1,8})$/.exec(md5ext);
|
|
47
47
|
const type = match ? imageTypes[match[1].toLowerCase()] : void 0;
|
|
@@ -243,7 +243,7 @@ function scriptRoots(target) {
|
|
|
243
243
|
while (!roots.has(current) && !chain.has(current)) {
|
|
244
244
|
chain.add(current);
|
|
245
245
|
const block = target.blocks[current];
|
|
246
|
-
if (!isRecord(block) || typeof block.parent !== "string" || !Object.hasOwn(target.blocks, block.parent)) break;
|
|
246
|
+
if (!isRecord$2(block) || typeof block.parent !== "string" || !Object.hasOwn(target.blocks, block.parent)) break;
|
|
247
247
|
current = block.parent;
|
|
248
248
|
}
|
|
249
249
|
const root = (_roots$get = roots.get(current)) !== null && _roots$get !== void 0 ? _roots$get : current;
|
|
@@ -351,12 +351,12 @@ function scriptKind(group, changes) {
|
|
|
351
351
|
if (changes.every(isLayoutOnly)) return "reordered";
|
|
352
352
|
return "updated";
|
|
353
353
|
}
|
|
354
|
-
var opcodeOf = (block) => isRecord(block) && typeof block.opcode === "string" ? block.opcode : void 0;
|
|
355
|
-
var isShadow = (block) => !isRecord(block) || block.shadow === true;
|
|
354
|
+
var opcodeOf = (block) => isRecord$2(block) && typeof block.opcode === "string" ? block.opcode : void 0;
|
|
355
|
+
var isShadow = (block) => !isRecord$2(block) || block.shadow === true;
|
|
356
356
|
var isHat = (opcode) => opcode !== void 0 && (opcode === "control_start_as_clone" || opcode === "procedures_definition" || /^[a-z0-9]+_when/i.test(opcode));
|
|
357
357
|
var substackInputs = ["SUBSTACK", "SUBSTACK2"];
|
|
358
358
|
function inputBlock(block, name) {
|
|
359
|
-
if (!isRecord(block) || !isRecord(block.inputs)) return void 0;
|
|
359
|
+
if (!isRecord$2(block) || !isRecord$2(block.inputs)) return void 0;
|
|
360
360
|
const input = block.inputs[name];
|
|
361
361
|
return Array.isArray(input) && typeof input[1] === "string" ? input[1] : void 0;
|
|
362
362
|
}
|
|
@@ -401,7 +401,7 @@ function summarize(group, changes, before, after) {
|
|
|
401
401
|
visited.add(id);
|
|
402
402
|
if (before && Object.hasOwn(before.blocks, id)) return true;
|
|
403
403
|
const next = after.blocks[id];
|
|
404
|
-
id = isRecord(next) && typeof next.next === "string" ? next.next : void 0;
|
|
404
|
+
id = isRecord$2(next) && typeof next.next === "string" ? next.next : void 0;
|
|
405
405
|
}
|
|
406
406
|
return false;
|
|
407
407
|
})) {
|
|
@@ -542,7 +542,7 @@ function statusOf(before, after) {
|
|
|
542
542
|
}
|
|
543
543
|
function entriesOf(target, kind) {
|
|
544
544
|
const list = target === null || target === void 0 ? void 0 : target[kind];
|
|
545
|
-
return Array.isArray(list) ? list.filter(isRecord) : [];
|
|
545
|
+
return Array.isArray(list) ? list.filter(isRecord$2) : [];
|
|
546
546
|
}
|
|
547
547
|
function nameOf(entry) {
|
|
548
548
|
return typeof entry.name === "string" ? entry.name : "";
|
|
@@ -787,6 +787,1356 @@ var legend = (locale) => locale.startsWith("ja") ? [
|
|
|
787
787
|
["updated", "Purple outline: block with changed contents"],
|
|
788
788
|
["part", "Orange frame: changed value"]
|
|
789
789
|
];
|
|
790
|
+
// src/github-review-api.ts
|
|
791
|
+
init_objectSpread2();
|
|
792
|
+
var reviewEvents = [
|
|
793
|
+
"COMMENT",
|
|
794
|
+
"APPROVE",
|
|
795
|
+
"REQUEST_CHANGES"
|
|
796
|
+
];
|
|
797
|
+
var proxyCodes = {
|
|
798
|
+
400: ["BAD_REQUEST"],
|
|
799
|
+
401: [
|
|
800
|
+
"SESSION_REQUIRED",
|
|
801
|
+
"SESSION_EXPIRED",
|
|
802
|
+
"REAUTH_REQUIRED"
|
|
803
|
+
],
|
|
804
|
+
403: ["FORBIDDEN"],
|
|
805
|
+
404: ["GITHUB_API_UNAVAILABLE", "PULL_REQUEST_NOT_FOUND"],
|
|
806
|
+
409: [
|
|
807
|
+
"REVIEW_STEP_CONFLICT",
|
|
808
|
+
"REVIEW_DUPLICATE",
|
|
809
|
+
"PENDING_REVIEW_EXISTS",
|
|
810
|
+
"REDIRECT_REJECTED"
|
|
811
|
+
],
|
|
812
|
+
411: ["LENGTH_REQUIRED"],
|
|
813
|
+
413: ["PAYLOAD_TOO_LARGE"],
|
|
814
|
+
422: ["REVIEW_REJECTED", "REVIEW_THREAD_REJECTED"],
|
|
815
|
+
429: ["RATE_LIMITED"],
|
|
816
|
+
500: ["GITHUB_API_MISCONFIGURED"],
|
|
817
|
+
502: ["UPSTREAM_FAILURE"],
|
|
818
|
+
504: ["UPSTREAM_TIMEOUT"]
|
|
819
|
+
};
|
|
820
|
+
var unknownCodes = [
|
|
821
|
+
"REVIEW_DUPLICATE",
|
|
822
|
+
"UPSTREAM_FAILURE",
|
|
823
|
+
"UPSTREAM_TIMEOUT",
|
|
824
|
+
"NETWORK_ERROR",
|
|
825
|
+
"INVALID_RESPONSE"
|
|
826
|
+
];
|
|
827
|
+
var GitHubReviewError = class extends Error {
|
|
828
|
+
constructor(code, options = {}) {
|
|
829
|
+
super(code);
|
|
830
|
+
this.code = code;
|
|
831
|
+
this.options = options;
|
|
832
|
+
this.name = "GitHubReviewError";
|
|
833
|
+
}
|
|
834
|
+
get unknown() {
|
|
835
|
+
return this.options.unknown === true;
|
|
836
|
+
}
|
|
837
|
+
get retryAfter() {
|
|
838
|
+
return this.options.retryAfter;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
var isSha$2 = (value) => typeof value === "string" && /^[0-9a-f]{40}$/u.test(value);
|
|
842
|
+
var isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
843
|
+
var isCount = (value, max) => Number.isSafeInteger(value) && Number(value) >= 0 && Number(value) <= max;
|
|
844
|
+
var optionalString = (value) => value === null || typeof value === "string";
|
|
845
|
+
var submitIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
846
|
+
var requestIdPattern = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/iu;
|
|
847
|
+
/** A thread path the proxy accepts: a relative path whose last element is exactly project.source.json. */
|
|
848
|
+
function isReviewThreadPath(path) {
|
|
849
|
+
const parts = path.split("/");
|
|
850
|
+
return parts[parts.length - 1] === "project.source.json" && new TextEncoder().encode(path).byteLength <= 1024 && parts.every((part) => part && part !== "." && part !== "..") && !/[\\\u0000-\u001f\u007f]/u.test(path);
|
|
851
|
+
}
|
|
852
|
+
function newReviewSubmitId() {
|
|
853
|
+
const id = crypto.randomUUID();
|
|
854
|
+
if (!submitIdPattern.test(id)) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
855
|
+
return id;
|
|
856
|
+
}
|
|
857
|
+
function readContext(value, withSubmit) {
|
|
858
|
+
if (!isRecord$1(value) || !isSha$2(value.headRefOid) || typeof value.viewer !== "string" || !Array.isArray(value.reviews)) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
859
|
+
let pendingReview = null;
|
|
860
|
+
if (value.pendingReview !== null) {
|
|
861
|
+
const pending = value.pendingReview;
|
|
862
|
+
if (!isRecord$1(pending) || typeof pending.ours !== "boolean" || !(pending.commitOid === null || isSha$2(pending.commitOid)) || typeof pending.createdAt !== "string") throw new GitHubReviewError("INVALID_RESPONSE");
|
|
863
|
+
pendingReview = {
|
|
864
|
+
ours: pending.ours,
|
|
865
|
+
commitOid: pending.commitOid,
|
|
866
|
+
createdAt: pending.createdAt
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
const reviews = value.reviews.map((review) => {
|
|
870
|
+
if (!isRecord$1(review) || typeof review.state !== "string" || !(review.commitOid === null || isSha$2(review.commitOid)) || !optionalString(review.submittedAt) || typeof review.ours !== "boolean") throw new GitHubReviewError("INVALID_RESPONSE");
|
|
871
|
+
return {
|
|
872
|
+
state: review.state,
|
|
873
|
+
commitOid: review.commitOid,
|
|
874
|
+
submittedAt: review.submittedAt,
|
|
875
|
+
ours: review.ours
|
|
876
|
+
};
|
|
877
|
+
});
|
|
878
|
+
const context = {
|
|
879
|
+
headRefOid: value.headRefOid,
|
|
880
|
+
viewer: value.viewer,
|
|
881
|
+
pendingReview,
|
|
882
|
+
reviews
|
|
883
|
+
};
|
|
884
|
+
if (withSubmit) {
|
|
885
|
+
const submit = value.submit;
|
|
886
|
+
if (!isRecord$1(submit) || typeof submit.state !== "string" || !isCount(submit.threads, 50)) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
887
|
+
context.submit = {
|
|
888
|
+
state: submit.state,
|
|
889
|
+
threads: submit.threads
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
return context;
|
|
893
|
+
}
|
|
894
|
+
async function readBounded(response, limit) {
|
|
895
|
+
if (!response.body) return "";
|
|
896
|
+
const reader = response.body.getReader();
|
|
897
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
898
|
+
let text = "", size = 0;
|
|
899
|
+
try {
|
|
900
|
+
for (;;) {
|
|
901
|
+
const { done, value } = await reader.read();
|
|
902
|
+
if (done) return text + decoder.decode();
|
|
903
|
+
size += value.byteLength;
|
|
904
|
+
if (size > limit) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
905
|
+
text += decoder.decode(value, { stream: true });
|
|
906
|
+
}
|
|
907
|
+
} finally {
|
|
908
|
+
reader.cancel().catch(() => {});
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function createGitHubReviewApi({ remote, getSession, fetch: send = (...args) => fetch(...args) }) {
|
|
912
|
+
let path;
|
|
913
|
+
try {
|
|
914
|
+
path = "/v1/github" + remoteUrl(remote).slice(18).replace(/\.git$/u, "");
|
|
915
|
+
} catch (_unused) {
|
|
916
|
+
throw new GitHubReviewError("INVALID_REMOTE");
|
|
917
|
+
}
|
|
918
|
+
/** What the proxy answered about each route group: `false` after `404 GITHUB_API_UNAVAILABLE`. */
|
|
919
|
+
const available = {};
|
|
920
|
+
let account = null;
|
|
921
|
+
async function session(review) {
|
|
922
|
+
var _value$account;
|
|
923
|
+
let value;
|
|
924
|
+
try {
|
|
925
|
+
value = await getSession(remote, false, review);
|
|
926
|
+
} catch (_unused2) {
|
|
927
|
+
throw new GitHubReviewError("SESSION_UNAVAILABLE");
|
|
928
|
+
}
|
|
929
|
+
if (!value) throw new GitHubReviewError("SESSION_REQUIRED");
|
|
930
|
+
if (!Number.isFinite(value.expiresAt) || value.expiresAt <= Date.now()) throw new GitHubReviewError("SESSION_EXPIRED");
|
|
931
|
+
try {
|
|
932
|
+
checkConnection(value.connection);
|
|
933
|
+
} catch (_unused3) {
|
|
934
|
+
throw new GitHubReviewError("INVALID_CONNECTION");
|
|
935
|
+
}
|
|
936
|
+
if (typeof value.bearer !== "string" || !/^[A-Za-z0-9._~+\/-]+=*$/u.test(value.bearer)) throw new GitHubReviewError("INVALID_SESSION");
|
|
937
|
+
account = typeof ((_value$account = value.account) === null || _value$account === void 0 ? void 0 : _value$account.login) === "string" ? value.account.login : null;
|
|
938
|
+
return value;
|
|
939
|
+
}
|
|
940
|
+
async function call(route, { method = "GET", body, submitId, review = false, mutation = false, limit = 262144, group, signal }) {
|
|
941
|
+
var _response$headers$get;
|
|
942
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) throw new GitHubReviewError("ABORTED");
|
|
943
|
+
const current = await session(review);
|
|
944
|
+
const headers = {
|
|
945
|
+
Authorization: "Bearer " + current.bearer,
|
|
946
|
+
Accept: "application/json"
|
|
947
|
+
};
|
|
948
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
949
|
+
if (submitId !== void 0) {
|
|
950
|
+
if (!submitIdPattern.test(submitId)) throw new GitHubReviewError("BAD_REQUEST");
|
|
951
|
+
headers["Review-Submit-Id"] = submitId;
|
|
952
|
+
}
|
|
953
|
+
let response;
|
|
954
|
+
try {
|
|
955
|
+
response = await send(current.connection.proxyOrigin + path + route, {
|
|
956
|
+
method,
|
|
957
|
+
headers,
|
|
958
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
959
|
+
signal,
|
|
960
|
+
credentials: "omit",
|
|
961
|
+
redirect: "error",
|
|
962
|
+
cache: "no-store",
|
|
963
|
+
referrerPolicy: "no-referrer"
|
|
964
|
+
});
|
|
965
|
+
} catch (_unused4) {
|
|
966
|
+
if (!mutation && (signal === null || signal === void 0 ? void 0 : signal.aborted)) throw new GitHubReviewError("ABORTED");
|
|
967
|
+
throw new GitHubReviewError("NETWORK_ERROR", { unknown: mutation });
|
|
968
|
+
}
|
|
969
|
+
const json = ((_response$headers$get = response.headers.get("content-type")) === null || _response$headers$get === void 0 ? void 0 : _response$headers$get.split(";")[0].trim()) === "application/json";
|
|
970
|
+
if (response.status !== 200) {
|
|
971
|
+
var _allowed$;
|
|
972
|
+
const allowed = proxyCodes[response.status];
|
|
973
|
+
let code = (_allowed$ = allowed === null || allowed === void 0 ? void 0 : allowed[0]) !== null && _allowed$ !== void 0 ? _allowed$ : "INVALID_RESPONSE";
|
|
974
|
+
let requestId, retryAfter;
|
|
975
|
+
try {
|
|
976
|
+
const parsed = json ? JSON.parse(await readBounded(response, 4096)) : null;
|
|
977
|
+
if (isRecord$1(parsed)) {
|
|
978
|
+
if (allowed === null || allowed === void 0 ? void 0 : allowed.includes(parsed.code)) code = parsed.code;
|
|
979
|
+
if (typeof parsed.requestId === "string" && requestIdPattern.test(parsed.requestId)) requestId = parsed.requestId;
|
|
980
|
+
if (Number.isSafeInteger(parsed.retryAfter) && Number(parsed.retryAfter) >= 0) retryAfter = Number(parsed.retryAfter);
|
|
981
|
+
}
|
|
982
|
+
} catch (_unused5) {}
|
|
983
|
+
if (code === "GITHUB_API_UNAVAILABLE" && group !== "read") available[group] = false;
|
|
984
|
+
if (code === "GITHUB_API_UNAVAILABLE" && group === "read") available.threads = false;
|
|
985
|
+
const unknown = mutation && (unknownCodes.includes(code) || !allowed);
|
|
986
|
+
throw new GitHubReviewError(unknown && !allowed ? "UPSTREAM_FAILURE" : code, {
|
|
987
|
+
status: response.status,
|
|
988
|
+
requestId,
|
|
989
|
+
retryAfter,
|
|
990
|
+
unknown
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
if (group !== "read") available[group] = true;
|
|
994
|
+
try {
|
|
995
|
+
if (!json) throw new Error();
|
|
996
|
+
return JSON.parse(await readBounded(response, limit));
|
|
997
|
+
} catch (_unused6) {
|
|
998
|
+
throw new GitHubReviewError("INVALID_RESPONSE", { unknown: mutation });
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
const checkNumber = (number) => {
|
|
1002
|
+
if (!Number.isSafeInteger(number) || number < 1 || number > 2147483647) throw new GitHubReviewError("BAD_REQUEST");
|
|
1003
|
+
};
|
|
1004
|
+
const graphql = (operation, number, variables, submitId, signal, mutation = true) => {
|
|
1005
|
+
checkNumber(number);
|
|
1006
|
+
return call("/graphql/" + operation, {
|
|
1007
|
+
method: "POST",
|
|
1008
|
+
body: _objectSpread2({ number }, variables),
|
|
1009
|
+
submitId,
|
|
1010
|
+
review: mutation,
|
|
1011
|
+
mutation,
|
|
1012
|
+
group: mutation ? "threads" : "read",
|
|
1013
|
+
signal
|
|
1014
|
+
});
|
|
1015
|
+
};
|
|
1016
|
+
const shape = (check, value, mutation = true) => {
|
|
1017
|
+
if (!check) throw new GitHubReviewError("INVALID_RESPONSE", { unknown: mutation });
|
|
1018
|
+
return value;
|
|
1019
|
+
};
|
|
1020
|
+
return {
|
|
1021
|
+
available,
|
|
1022
|
+
/** The GitHub login of the last session this client used. */
|
|
1023
|
+
account: () => account,
|
|
1024
|
+
/** Reads the viewer's pending and recent reviews; with `submitId`, resolves that submission's unknown outcome. */
|
|
1025
|
+
async context(number, submitId, signal) {
|
|
1026
|
+
const context = readContext(await graphql("pullRequestReviewContext", number, {}, submitId, signal, false), submitId !== void 0);
|
|
1027
|
+
available.threads = true;
|
|
1028
|
+
return context;
|
|
1029
|
+
},
|
|
1030
|
+
/** Step 1: a pending review on `commitOid`. */
|
|
1031
|
+
async start(number, submitId, commitOid, signal) {
|
|
1032
|
+
if (!isSha$2(commitOid)) throw new GitHubReviewError("BAD_REQUEST");
|
|
1033
|
+
const value = await graphql("addPullRequestReview", number, { commitOid }, submitId, signal);
|
|
1034
|
+
shape(isRecord$1(value) && value.state === "PENDING" && value.commitOid === commitOid, value);
|
|
1035
|
+
},
|
|
1036
|
+
/** Step 2: one file-level thread on a project.source.json; resolves the number of threads the proxy has accepted. */
|
|
1037
|
+
async thread(number, submitId, threadIndex, threadPath, body, signal) {
|
|
1038
|
+
if (!isCount(threadIndex, 49) || !isReviewThreadPath(threadPath) || !body || body.length > 65536) throw new GitHubReviewError("BAD_REQUEST");
|
|
1039
|
+
const value = await graphql("addPullRequestReviewThread", number, {
|
|
1040
|
+
threadIndex,
|
|
1041
|
+
path: threadPath,
|
|
1042
|
+
body
|
|
1043
|
+
}, submitId, signal);
|
|
1044
|
+
return shape(isRecord$1(value) && value.threads === threadIndex + 1, threadIndex + 1);
|
|
1045
|
+
},
|
|
1046
|
+
/** Step 3: publishes the pending review. */
|
|
1047
|
+
async submit(number, submitId, threadCount, event, body, signal) {
|
|
1048
|
+
if (!isCount(threadCount, 50) || !reviewEvents.includes(event) || body.length > 65536) throw new GitHubReviewError("BAD_REQUEST");
|
|
1049
|
+
const value = await graphql("submitPullRequestReview", number, {
|
|
1050
|
+
threadCount,
|
|
1051
|
+
event,
|
|
1052
|
+
body
|
|
1053
|
+
}, submitId, signal);
|
|
1054
|
+
const review = isRecord$1(value) ? value : {};
|
|
1055
|
+
return shape(typeof review.state === "string" && review.state !== "PENDING" && isSha$2(review.commitOid) && typeof review.submittedAt === "string" && typeof review.url === "string", review);
|
|
1056
|
+
},
|
|
1057
|
+
/** Removes the pending review of the submission. */
|
|
1058
|
+
async discard(number, submitId, signal) {
|
|
1059
|
+
const value = await graphql("deletePullRequestReview", number, {}, submitId, signal);
|
|
1060
|
+
shape(isRecord$1(value) && value.deleted === true, value);
|
|
1061
|
+
},
|
|
1062
|
+
/** The review-body route: one page of the pull request's reviews, oldest first. */
|
|
1063
|
+
async reviews(number, page, signal) {
|
|
1064
|
+
checkNumber(number);
|
|
1065
|
+
if (!Number.isSafeInteger(page) || page < 1 || page > 10) throw new GitHubReviewError("BAD_REQUEST");
|
|
1066
|
+
const value = await call(`/pulls/${number}/reviews?page=${page}`, {
|
|
1067
|
+
group: "body",
|
|
1068
|
+
limit: 4194304,
|
|
1069
|
+
signal
|
|
1070
|
+
});
|
|
1071
|
+
if (!Array.isArray(value)) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
1072
|
+
return value.map((review) => {
|
|
1073
|
+
if (!isRecord$1(review) || !Number.isSafeInteger(review.id) || typeof review.state !== "string" || !(review.commit_id === null || isSha$2(review.commit_id)) || !optionalString(review.body)) throw new GitHubReviewError("INVALID_RESPONSE");
|
|
1074
|
+
const login = isRecord$1(review.user) && typeof review.user.login === "string" ? review.user.login : null;
|
|
1075
|
+
return {
|
|
1076
|
+
id: Number(review.id),
|
|
1077
|
+
login,
|
|
1078
|
+
commitId: review.commit_id,
|
|
1079
|
+
state: review.state,
|
|
1080
|
+
body: typeof review.body === "string" ? review.body : ""
|
|
1081
|
+
};
|
|
1082
|
+
});
|
|
1083
|
+
},
|
|
1084
|
+
/** The review-body route: creates and publishes a review with a body only. */
|
|
1085
|
+
async create(number, submitId, commitOid, event, body, signal) {
|
|
1086
|
+
checkNumber(number);
|
|
1087
|
+
if (!isSha$2(commitOid) || !reviewEvents.includes(event) || body.length > 65536 || !body && event !== "APPROVE") throw new GitHubReviewError("BAD_REQUEST");
|
|
1088
|
+
const value = await call(`/pulls/${number}/reviews`, {
|
|
1089
|
+
method: "POST",
|
|
1090
|
+
body: {
|
|
1091
|
+
commit_id: commitOid,
|
|
1092
|
+
event,
|
|
1093
|
+
body
|
|
1094
|
+
},
|
|
1095
|
+
submitId,
|
|
1096
|
+
review: true,
|
|
1097
|
+
mutation: true,
|
|
1098
|
+
group: "body",
|
|
1099
|
+
limit: 1048576,
|
|
1100
|
+
signal
|
|
1101
|
+
});
|
|
1102
|
+
return shape(isRecord$1(value) && typeof value.state === "string" && typeof value.html_url === "string", { url: value.html_url });
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
// src/messages.ts
|
|
1107
|
+
var messages = {
|
|
1108
|
+
en: {
|
|
1109
|
+
pullRequests: "Pull requests",
|
|
1110
|
+
localChanges: "Local changes",
|
|
1111
|
+
title: "Local Git",
|
|
1112
|
+
close: "Close",
|
|
1113
|
+
choose: "Choose Repository…",
|
|
1114
|
+
refresh: "Refresh",
|
|
1115
|
+
empty: "Choose an existing Git repository to inspect its files and history.",
|
|
1116
|
+
repository: "Repository",
|
|
1117
|
+
project: "Project folder",
|
|
1118
|
+
none: "No project selected",
|
|
1119
|
+
root: "Repository root",
|
|
1120
|
+
open: "Open This Project",
|
|
1121
|
+
reload: "Reload Project",
|
|
1122
|
+
up: "Parent folder",
|
|
1123
|
+
browse: "Choose a project inside this repository",
|
|
1124
|
+
replace: "Replace the open project and discard its unsaved changes?",
|
|
1125
|
+
dirty: "The Editor has unsaved changes.",
|
|
1126
|
+
saved: "The Editor has no unsaved changes.",
|
|
1127
|
+
staged: "Changes in the next record",
|
|
1128
|
+
unstaged: "Saved, not yet recorded",
|
|
1129
|
+
untracked: "New files never recorded",
|
|
1130
|
+
commit: "Changes in this record",
|
|
1131
|
+
history: "Records (commits)",
|
|
1132
|
+
worktree: "Saved files",
|
|
1133
|
+
clean: "No saved changes in the selected folder.",
|
|
1134
|
+
branches: "Local branches",
|
|
1135
|
+
unborn: "No records yet",
|
|
1136
|
+
detached: "Detached HEAD",
|
|
1137
|
+
select: "Select a file to view its diff.",
|
|
1138
|
+
binary: "Binary asset",
|
|
1139
|
+
large: "Text preview exceeds its display limit.",
|
|
1140
|
+
bytes: "bytes",
|
|
1141
|
+
mode: "Git file mode",
|
|
1142
|
+
permissions: "Working-tree executable permission changes are unavailable in the browser.",
|
|
1143
|
+
unchanged: "The text is unchanged.",
|
|
1144
|
+
loading: "Reading repository…",
|
|
1145
|
+
invalid: "Choose a repository with a .git directory, and a project inside that repository.",
|
|
1146
|
+
changed: "Files or Git state changed while reading. Refresh to read the current state.",
|
|
1147
|
+
outdated: "The repository changed outside the Editor. Refresh to read the current state.",
|
|
1148
|
+
unsupported: "This repository contains an entry the browser cannot represent.",
|
|
1149
|
+
commitMessage: "Write a message for this record.",
|
|
1150
|
+
switchUnsafe: "Stop the project, and save or record (or undo) every change, before switching branches.",
|
|
1151
|
+
branchExists: "A branch with this name already exists.",
|
|
1152
|
+
branchConflict: "This name clashes with an existing branch: Git cannot keep both a branch and another branch under its name followed by /, such as pr and pr/7. Rename or delete the other branch with Git, or choose another name.",
|
|
1153
|
+
pullRequestBranch: "The branch pr/<number> already exists with other records, so this version was not opened. The branch is left unchanged. Switch to it in Local changes, or rename or delete it with Git, then open the pull request here again.",
|
|
1154
|
+
openedPullRequest: "Opened pull request #{number} on the local branch {branch}. To go back, switch to {previous} in Local changes.",
|
|
1155
|
+
openedPullRequestDetached: "Opened pull request #{number} on the local branch {branch}. To go back, switch to another branch in Local changes.",
|
|
1156
|
+
locked: "Another Git tool is working on this repository (a lock file, or a merge, cherry-pick, revert or rebase in progress). Try again after it finishes.",
|
|
1157
|
+
branchName: "Git cannot use this name for a branch.",
|
|
1158
|
+
pullUnsafe: "Stop the project, and save or undo unsaved edits and unrecorded changes, before getting changes from GitHub (pull).",
|
|
1159
|
+
diverged: "This branch and GitHub's have both gained records, so they cannot be combined here (pull only fast-forwards). Your branch and files are unchanged.",
|
|
1160
|
+
session: "Reconnect GitHub for this repository and requested permission.",
|
|
1161
|
+
pushUnknown: "It is unknown whether GitHub received the records. Check GitHub (fetch) before sending again.",
|
|
1162
|
+
pushRefresh: "GitHub received the records. Check GitHub (fetch) to update the status.",
|
|
1163
|
+
rollbackFailed: "Some local changes could not be restored. Check the files and the Git details.",
|
|
1164
|
+
remoteFailed: "Could not complete the GitHub operation. Check the connection and try again.",
|
|
1165
|
+
remoteDone: "The GitHub operation finished.",
|
|
1166
|
+
stage: "Add to Next Record",
|
|
1167
|
+
unstage: "Remove from Next Record",
|
|
1168
|
+
stageAll: "Add All Changes to Next Record",
|
|
1169
|
+
commitLabel: "Message for this record",
|
|
1170
|
+
commitButton: "Record (Commit)",
|
|
1171
|
+
committed: "Recorded the changes (commit).",
|
|
1172
|
+
commitAuthor: "Recorded as",
|
|
1173
|
+
saveFirst: "Save the project before adding its changes to the next record.",
|
|
1174
|
+
author: "Set user.name and user.email for this repository in Git before recording. The Editor does not change Git settings.",
|
|
1175
|
+
nothingStaged: "Add changes to the next record first.",
|
|
1176
|
+
switchTo: "Switch",
|
|
1177
|
+
currentBranch: "current",
|
|
1178
|
+
createBranch: "Create Branch",
|
|
1179
|
+
newBranch: "New branch name",
|
|
1180
|
+
switchAfterCreate: "Switch to it",
|
|
1181
|
+
createFromRemote: "Create and Switch",
|
|
1182
|
+
remoteBranches: "Branches on GitHub (origin)",
|
|
1183
|
+
switched: "Switched branches. Use Reload Project to open the files of this branch.",
|
|
1184
|
+
branchCreated: "Created the branch.",
|
|
1185
|
+
projectMissing: "The open project folder is not on this branch. Switch anyway?",
|
|
1186
|
+
pulledProject: "Got the changes from GitHub. Use Reload Project to open the updated files.",
|
|
1187
|
+
confirmPush: "Send the records on this branch to GitHub (push)?",
|
|
1188
|
+
details: "Git details",
|
|
1189
|
+
branch: "Branch",
|
|
1190
|
+
head: "HEAD",
|
|
1191
|
+
groups: "Git groups",
|
|
1192
|
+
added: "Added",
|
|
1193
|
+
modified: "Changed",
|
|
1194
|
+
deleted: "Deleted",
|
|
1195
|
+
gitStaged: "Staged",
|
|
1196
|
+
gitUnstaged: "Unstaged",
|
|
1197
|
+
gitUntracked: "Untracked",
|
|
1198
|
+
upstreamSame: "Same as GitHub",
|
|
1199
|
+
upstreamAhead: "Ahead of GitHub",
|
|
1200
|
+
upstreamBehind: "Behind GitHub",
|
|
1201
|
+
upstreamDiverged: "Diverged from GitHub",
|
|
1202
|
+
upstreamUnknown: "Not checked with GitHub yet",
|
|
1203
|
+
records: "records",
|
|
1204
|
+
checkedAt: "as of the last check",
|
|
1205
|
+
failed: "Could not read the repository. Check folder access and refresh.",
|
|
1206
|
+
historyMore: "Show more",
|
|
1207
|
+
historyLoading: "Reading records…",
|
|
1208
|
+
historyFailed: "Could not read the records.",
|
|
1209
|
+
historyActions: "Actions",
|
|
1210
|
+
historyCreateHere: "Create branch here…",
|
|
1211
|
+
historySwitch: "Switch to {name}",
|
|
1212
|
+
historyCreateFromRemote: "Create and switch to {name}",
|
|
1213
|
+
historyCopy: "Copy commit ID",
|
|
1214
|
+
historyCopied: "Copied the commit ID.",
|
|
1215
|
+
historyCopyFailed: "Could not copy the commit ID.",
|
|
1216
|
+
historyRefs: "Branches",
|
|
1217
|
+
cancel: "Cancel",
|
|
1218
|
+
branchMenu: "Branches",
|
|
1219
|
+
openPanel: "Open Local Git",
|
|
1220
|
+
newBranchItem: "New Branch…",
|
|
1221
|
+
newBranchTitle: "New Branch",
|
|
1222
|
+
switchedShort: "Switched branches.",
|
|
1223
|
+
historyViewEmpty: "Choose a Git repository to show its history here."
|
|
1224
|
+
},
|
|
1225
|
+
ja: {
|
|
1226
|
+
pullRequests: "プルリクエスト",
|
|
1227
|
+
localChanges: "ローカルの変更",
|
|
1228
|
+
title: "ローカル Git",
|
|
1229
|
+
close: "閉じる",
|
|
1230
|
+
choose: "リポジトリを選択…",
|
|
1231
|
+
refresh: "再取得",
|
|
1232
|
+
empty: "既存の Git リポジトリを選び、ファイルの状態と履歴を確認できます。",
|
|
1233
|
+
repository: "リポジトリ",
|
|
1234
|
+
project: "作品フォルダ",
|
|
1235
|
+
none: "作品は未選択",
|
|
1236
|
+
root: "リポジトリルート",
|
|
1237
|
+
open: "この作品を開く",
|
|
1238
|
+
reload: "作品を再読込",
|
|
1239
|
+
up: "上のフォルダ",
|
|
1240
|
+
browse: "リポジトリ内の作品を選択",
|
|
1241
|
+
replace: "保存していない編集を破棄して、別の作品を開きますか?",
|
|
1242
|
+
dirty: "Editor に保存していない編集があります。",
|
|
1243
|
+
saved: "Editor に保存していない編集はありません。",
|
|
1244
|
+
staged: "次の記録に入る変更",
|
|
1245
|
+
unstaged: "保存したが、まだ記録していない変更",
|
|
1246
|
+
untracked: "まだ記録したことのない新しいファイル",
|
|
1247
|
+
commit: "この記録の変更",
|
|
1248
|
+
history: "記録(コミット)の履歴",
|
|
1249
|
+
worktree: "保存したファイル",
|
|
1250
|
+
clean: "対象フォルダに、保存した変更はありません。",
|
|
1251
|
+
branches: "ローカルブランチ",
|
|
1252
|
+
unborn: "まだ記録がありません",
|
|
1253
|
+
detached: "ブランチ未選択の HEAD",
|
|
1254
|
+
select: "ファイルを選ぶと差分を表示します。",
|
|
1255
|
+
binary: "バイナリアセット",
|
|
1256
|
+
large: "テキスト差分の表示上限を超えています。",
|
|
1257
|
+
bytes: "バイト",
|
|
1258
|
+
mode: "Git のファイルモード",
|
|
1259
|
+
permissions: "作業ツリーの実行権限の変更はブラウザーから確認できません。",
|
|
1260
|
+
unchanged: "テキストに変更はありません。",
|
|
1261
|
+
loading: "リポジトリを読込中…",
|
|
1262
|
+
invalid: ".git ディレクトリを持つリポジトリと、その配下の作品を選んでください。",
|
|
1263
|
+
changed: "読込中にファイルまたは Git の状態が変わりました。再取得してください。",
|
|
1264
|
+
outdated: "Editor の外でリポジトリが変更されました。再取得すると最新の状態を読み込みます。",
|
|
1265
|
+
unsupported: "ブラウザーで扱えない種類の Git エントリーがあります。",
|
|
1266
|
+
commitMessage: "この記録の説明を書いてください。",
|
|
1267
|
+
switchUnsafe: "ブランチを切り替える前に、実行を止め、すべての変更を保存して記録(または取り消し)してください。",
|
|
1268
|
+
branchExists: "同じ名前のブランチがすでにあります。",
|
|
1269
|
+
branchConflict: "この名前は既存のブランチとぶつかります。Git では、pr と pr/7 のように、あるブランチと、その名前に / を続けたブランチを両方は持てません。Git でもう一方の名前を変えるか消すか、別の名前にしてください。",
|
|
1270
|
+
pullRequestBranch: "ブランチ pr/<番号> が、別の記録を指してすでにあるため、この版を開きませんでした。ブランチはそのままです。「ローカルの変更」で切り替えるか、Git で名前を変えるか消してから、もう一度ここで開いてください。",
|
|
1271
|
+
openedPullRequest: "プルリクエスト #{number} を、ローカルブランチ {branch} で開きました。戻るときは「ローカルの変更」で {previous} に切り替えてください。",
|
|
1272
|
+
openedPullRequestDetached: "プルリクエスト #{number} を、ローカルブランチ {branch} で開きました。戻るときは「ローカルの変更」で別のブランチに切り替えてください。",
|
|
1273
|
+
locked: "ほかの Git の操作が、このリポジトリを使っています(ロックファイル、またはマージ・cherry-pick・revert・rebase の途中)。終わってから、もう一度試してください。",
|
|
1274
|
+
branchName: "この名前は Git のブランチ名に使えません。",
|
|
1275
|
+
pullUnsafe: "GitHub から取り込む前に、実行を止め、保存していない編集とまだ記録していない変更をなくしてください。",
|
|
1276
|
+
diverged: "このブランチと GitHub の両方に新しい記録があるため、ここでは取り込めません(pull は fast-forward のみ)。ブランチとファイルはそのままです。",
|
|
1277
|
+
session: "このリポジトリと操作に必要な権限で GitHub に接続してください。",
|
|
1278
|
+
pushUnknown: "GitHub が記録を受け取ったか分かりません。送り直す前に「GitHub と確かめる」を使ってください。",
|
|
1279
|
+
pushRefresh: "GitHub が記録を受け取りました。「GitHub と確かめる」で状態を更新してください。",
|
|
1280
|
+
rollbackFailed: "一部のローカルの変更を元に戻せませんでした。ファイルと Git の詳細を確認してください。",
|
|
1281
|
+
remoteFailed: "GitHub との操作を完了できませんでした。接続を確認して、もう一度試してください。",
|
|
1282
|
+
remoteDone: "GitHub との操作が終わりました。",
|
|
1283
|
+
stage: "次の記録に入れる",
|
|
1284
|
+
unstage: "次の記録から外す",
|
|
1285
|
+
stageAll: "すべての変更を次の記録に入れる",
|
|
1286
|
+
commitLabel: "この記録の説明",
|
|
1287
|
+
commitButton: "記録する(コミット)",
|
|
1288
|
+
committed: "変更を記録しました(コミット)。",
|
|
1289
|
+
commitAuthor: "記録する人",
|
|
1290
|
+
saveFirst: "次の記録に入れる前に、作品を保存してください。",
|
|
1291
|
+
author: "記録する前に、Git でこのリポジトリの user.name と user.email を設定してください。Editor は Git の設定を変えません。",
|
|
1292
|
+
nothingStaged: "先に、変更を次の記録に入れてください。",
|
|
1293
|
+
switchTo: "切り替える",
|
|
1294
|
+
currentBranch: "現在",
|
|
1295
|
+
createBranch: "ブランチを作る",
|
|
1296
|
+
newBranch: "新しいブランチの名前",
|
|
1297
|
+
switchAfterCreate: "作ったら切り替える",
|
|
1298
|
+
createFromRemote: "作って切り替える",
|
|
1299
|
+
remoteBranches: "GitHub(origin)のブランチ",
|
|
1300
|
+
switched: "ブランチを切り替えました。「作品を再読込」で、このブランチのファイルを開けます。",
|
|
1301
|
+
branchCreated: "ブランチを作りました。",
|
|
1302
|
+
projectMissing: "開いている作品フォルダは、このブランチにありません。切り替えますか?",
|
|
1303
|
+
pulledProject: "GitHub から取り込みました。「作品を再読込」で更新したファイルを開けます。",
|
|
1304
|
+
confirmPush: "このブランチの記録を GitHub に送りますか(push)?",
|
|
1305
|
+
details: "Git の詳細",
|
|
1306
|
+
branch: "ブランチ",
|
|
1307
|
+
head: "HEAD",
|
|
1308
|
+
groups: "Git の区分",
|
|
1309
|
+
added: "追加",
|
|
1310
|
+
modified: "変更",
|
|
1311
|
+
deleted: "削除",
|
|
1312
|
+
gitStaged: "ステージ済み (staged)",
|
|
1313
|
+
gitUnstaged: "未ステージ (unstaged)",
|
|
1314
|
+
gitUntracked: "未追跡 (untracked)",
|
|
1315
|
+
upstreamSame: "GitHub と同じ",
|
|
1316
|
+
upstreamAhead: "GitHub より進んでいる",
|
|
1317
|
+
upstreamBehind: "GitHub より遅れている",
|
|
1318
|
+
upstreamDiverged: "GitHub と分かれている",
|
|
1319
|
+
upstreamUnknown: "まだ GitHub と確かめていない",
|
|
1320
|
+
records: "件",
|
|
1321
|
+
checkedAt: "最後に確かめた時点",
|
|
1322
|
+
failed: "リポジトリを読み込めませんでした。フォルダへのアクセスを確認し、再取得してください。",
|
|
1323
|
+
historyMore: "さらに表示",
|
|
1324
|
+
historyLoading: "記録を読込中…",
|
|
1325
|
+
historyFailed: "記録を読み込めませんでした。",
|
|
1326
|
+
historyActions: "操作",
|
|
1327
|
+
historyCreateHere: "ここからブランチを作る…",
|
|
1328
|
+
historySwitch: "{name} に切り替える",
|
|
1329
|
+
historyCreateFromRemote: "{name} を作って切り替える",
|
|
1330
|
+
historyCopy: "コミットの ID をコピー",
|
|
1331
|
+
historyCopied: "コミットの ID をコピーしました。",
|
|
1332
|
+
historyCopyFailed: "コミットの ID をコピーできませんでした。",
|
|
1333
|
+
historyRefs: "ブランチ",
|
|
1334
|
+
cancel: "キャンセル",
|
|
1335
|
+
branchMenu: "ブランチ",
|
|
1336
|
+
openPanel: "ローカル Git を開く",
|
|
1337
|
+
newBranchItem: "新しいブランチ…",
|
|
1338
|
+
newBranchTitle: "新しいブランチ",
|
|
1339
|
+
switchedShort: "ブランチを切り替えました。",
|
|
1340
|
+
historyViewEmpty: "Git リポジトリを選ぶと、ここに履歴を表示します。"
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
var labelsFor = (locale) => locale.startsWith("ja") ? messages.ja : messages.en;
|
|
1344
|
+
/** The words of sending a pull request review through the GitHub connection. */
|
|
1345
|
+
var reviewMessages = {
|
|
1346
|
+
en: {
|
|
1347
|
+
composer: "Send a review",
|
|
1348
|
+
overall: "Overall comment",
|
|
1349
|
+
event: "Result",
|
|
1350
|
+
COMMENT: "Comment",
|
|
1351
|
+
APPROVE: "Approve",
|
|
1352
|
+
REQUEST_CHANGES: "Request changes",
|
|
1353
|
+
scriptComment: "Comment on this code",
|
|
1354
|
+
commented: "Commented",
|
|
1355
|
+
summaryThreads: "Code comments sent as threads on their project.source.json: {threads}. Code comments in the review body: {body}.",
|
|
1356
|
+
summaryBody: "This connection sends only the review body: {body} code comments go in it.",
|
|
1357
|
+
summaryNone: "No code comments yet. Write them under each code change.",
|
|
1358
|
+
send: "Send Review",
|
|
1359
|
+
sending: "Sending the review…",
|
|
1360
|
+
checking: "Checking GitHub…",
|
|
1361
|
+
sendAgain: "Send Again",
|
|
1362
|
+
check: "Check Whether It Was Sent",
|
|
1363
|
+
discard: "Discard the Pending Review",
|
|
1364
|
+
forget: "Stop Tracking This Submission",
|
|
1365
|
+
useVersion: "Review This Version",
|
|
1366
|
+
sent: "Sent the review.",
|
|
1367
|
+
openReview: "Open the review on GitHub",
|
|
1368
|
+
openPullRequest: "Open the pull request on GitHub",
|
|
1369
|
+
dismiss: "Dismiss",
|
|
1370
|
+
discarded: "Discarded the pending review. Your words are kept.",
|
|
1371
|
+
notStored: "Browser storage is unavailable, so this draft is kept only while the panel is open.",
|
|
1372
|
+
headChanged: "The pull request changed after you started this review. The review is sent for the version shown.",
|
|
1373
|
+
unsupported: "This GitHub connection cannot send reviews.",
|
|
1374
|
+
needsGrant: "To send a review, disconnect and connect again with “Allow sending reviews”.",
|
|
1375
|
+
allowReview: "Allow sending reviews",
|
|
1376
|
+
PENDING_REVIEW_EXISTS: "You already have a pending review on this pull request on GitHub. Submit or discard it on GitHub, then send again.",
|
|
1377
|
+
PENDING_REVIEW_LEFT: "A pending review remains on GitHub. Only you can see it. Discard it here, or on GitHub with “Finish your review”.",
|
|
1378
|
+
REVIEW_OUTCOME_UNKNOWN: "It is unknown whether GitHub received the review. Check before sending again.",
|
|
1379
|
+
pending: "The review waits on GitHub with its code comments but is not published. Change it and send again, or discard it.",
|
|
1380
|
+
REVIEW_REJECTED: "GitHub refused the review, for example an approval of your own pull request.",
|
|
1381
|
+
REVIEW_THREAD_REJECTED: "GitHub refused a comment on a project file, so nothing was published. Comments on that file now go in the review body. Send again.",
|
|
1382
|
+
RATE_LIMITED: "The GitHub rate limit was reached. Try again in {seconds} seconds.",
|
|
1383
|
+
EMPTY_REVIEW: "Write an overall comment (needed to request changes) or a comment on the code.",
|
|
1384
|
+
TOO_LONG: "The review is longer than GitHub accepts (65,536 characters).",
|
|
1385
|
+
closed: "GitHub has no such review: it was not published, or it was discarded. You can send it again.",
|
|
1386
|
+
lost: "The connection no longer knows this submission. Check on GitHub whether the review was published ({count} of your reviews on this version found).",
|
|
1387
|
+
session: "Connect to GitHub again to send the review.",
|
|
1388
|
+
FORBIDDEN: "The GitHub connection is not allowed to send reviews to this repository.",
|
|
1389
|
+
GITHUB_API_UNAVAILABLE: "The connection cannot add code comments as threads. They now go in the review body: check the summary and send again.",
|
|
1390
|
+
failed: "Could not send the review. Nothing was published. Try again."
|
|
1391
|
+
},
|
|
1392
|
+
ja: {
|
|
1393
|
+
composer: "レビューを送る",
|
|
1394
|
+
overall: "全体のコメント",
|
|
1395
|
+
event: "結果",
|
|
1396
|
+
COMMENT: "コメント",
|
|
1397
|
+
APPROVE: "承認",
|
|
1398
|
+
REQUEST_CHANGES: "修正を依頼",
|
|
1399
|
+
scriptComment: "このコードへのコメント",
|
|
1400
|
+
commented: "コメントあり",
|
|
1401
|
+
summaryThreads: "コードへのコメントのうち {threads} 件をその project.source.json へのスレッドとして、{body} 件をレビュー本文に入れて送ります。",
|
|
1402
|
+
summaryBody: "この接続ではレビュー本文だけを送れます。コードへのコメント {body} 件を本文に入れます。",
|
|
1403
|
+
summaryNone: "コードへのコメントはまだありません。各コードの変更の下に書けます。",
|
|
1404
|
+
send: "レビューを送信",
|
|
1405
|
+
sending: "レビューを送信中…",
|
|
1406
|
+
checking: "GitHub で確かめています…",
|
|
1407
|
+
sendAgain: "もう一度送る",
|
|
1408
|
+
check: "送信されたか確かめる",
|
|
1409
|
+
discard: "保留中のレビューを破棄",
|
|
1410
|
+
forget: "この送信の記録を消す",
|
|
1411
|
+
useVersion: "この版をレビューする",
|
|
1412
|
+
sent: "レビューを送りました。",
|
|
1413
|
+
openReview: "GitHub でレビューを開く",
|
|
1414
|
+
openPullRequest: "GitHub で PR を開く",
|
|
1415
|
+
dismiss: "閉じる",
|
|
1416
|
+
discarded: "保留中のレビューを破棄しました。書いた内容は残っています。",
|
|
1417
|
+
notStored: "ブラウザーのストレージを使えないため、この下書きはパネルを開いている間だけ残ります。",
|
|
1418
|
+
headChanged: "レビューを書き始めた後に PR が更新されました。レビューは表示中の版に対して送ります。",
|
|
1419
|
+
unsupported: "この GitHub 接続ではレビューを送れません。",
|
|
1420
|
+
needsGrant: "レビューを送るには、切断して「レビューの送信を許可する」を付けて接続し直してください。",
|
|
1421
|
+
allowReview: "レビューの送信を許可する",
|
|
1422
|
+
PENDING_REVIEW_EXISTS: "この PR に、GitHub で書きかけの保留中のレビューがあります。GitHub で送るか破棄してから、もう一度送ってください。",
|
|
1423
|
+
PENDING_REVIEW_LEFT: "GitHub に保留中のレビューが残っています。あなたにしか見えません。ここで破棄するか、GitHub の「Finish your review」から破棄してください。",
|
|
1424
|
+
REVIEW_OUTCOME_UNKNOWN: "GitHub がレビューを受け取ったかわかりません。もう一度送る前に確かめてください。",
|
|
1425
|
+
pending: "レビューはコードへのコメント付きで GitHub に保留中ですが、公開されていません。直してもう一度送るか、破棄してください。",
|
|
1426
|
+
REVIEW_REJECTED: "GitHub がレビューを拒みました(自分の PR の承認など)。",
|
|
1427
|
+
REVIEW_THREAD_REJECTED: "GitHub が作品ファイルへのコメントを拒んだため、何も公開していません。そのファイルへのコメントはレビュー本文に入れます。もう一度送ってください。",
|
|
1428
|
+
RATE_LIMITED: "GitHub の利用上限に達しました。{seconds} 秒後に再試行してください。",
|
|
1429
|
+
EMPTY_REVIEW: "全体のコメント(修正の依頼では必須)か、コードへのコメントを書いてください。",
|
|
1430
|
+
TOO_LONG: "レビューが GitHub の上限(65,536 文字)を超えています。",
|
|
1431
|
+
closed: "GitHub にそのレビューはありません。公開されなかったか、破棄されています。もう一度送れます。",
|
|
1432
|
+
lost: "接続がこの送信の記録を持っていません。レビューが公開されたかを GitHub で確かめてください(この版へのあなたのレビュー {count} 件)。",
|
|
1433
|
+
session: "レビューを送るには、GitHub にもう一度接続してください。",
|
|
1434
|
+
FORBIDDEN: "GitHub 接続に、このリポジトリへレビューを送る権限がありません。",
|
|
1435
|
+
GITHUB_API_UNAVAILABLE: "この接続ではコードへのコメントをスレッドにできません。コメントはレビュー本文に入れます。件数を確かめて、もう一度送ってください。",
|
|
1436
|
+
failed: "レビューを送れませんでした。何も公開していません。再試行してください。"
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
var reviewLabelsFor = (locale) => locale.startsWith("ja") ? reviewMessages.ja : reviewMessages.en;
|
|
1440
|
+
// src/review-submit.ts
|
|
1441
|
+
init_objectSpread2();
|
|
1442
|
+
var emptyReviewDraft = () => ({
|
|
1443
|
+
event: "COMMENT",
|
|
1444
|
+
body: "",
|
|
1445
|
+
comments: {},
|
|
1446
|
+
inBody: []
|
|
1447
|
+
});
|
|
1448
|
+
var draftKey = (name) => `turbowarp-plus-git:review-draft:${name}`;
|
|
1449
|
+
var memoryDrafts = /* @__PURE__ */ new Map();
|
|
1450
|
+
var draftListeners = /* @__PURE__ */ new Map();
|
|
1451
|
+
var persistent = true;
|
|
1452
|
+
var running = /* @__PURE__ */ new Set();
|
|
1453
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1454
|
+
var isSha$1 = (value) => typeof value === "string" && /^[0-9a-f]{40}$/u.test(value);
|
|
1455
|
+
var states = [
|
|
1456
|
+
"starting",
|
|
1457
|
+
"threading",
|
|
1458
|
+
"submitting",
|
|
1459
|
+
"start-unknown",
|
|
1460
|
+
"submit-unknown",
|
|
1461
|
+
"pending",
|
|
1462
|
+
"left"
|
|
1463
|
+
];
|
|
1464
|
+
/** Keeps only the fields of a draft, so nothing else read from storage reaches a request. */
|
|
1465
|
+
function readDraftValue(value, settle) {
|
|
1466
|
+
if (!isRecord(value)) return null;
|
|
1467
|
+
const draft = emptyReviewDraft();
|
|
1468
|
+
if (reviewEvents.includes(value.event)) draft.event = value.event;
|
|
1469
|
+
if (typeof value.body === "string") draft.body = value.body;
|
|
1470
|
+
if (isRecord(value.comments)) {
|
|
1471
|
+
for (const [key, comment] of Object.entries(value.comments)) if (isRecord(comment) && typeof comment.file === "string" && typeof comment.heading === "string" && typeof comment.body === "string") draft.comments[key] = {
|
|
1472
|
+
file: comment.file,
|
|
1473
|
+
heading: comment.heading,
|
|
1474
|
+
body: comment.body
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
if (Array.isArray(value.inBody)) draft.inBody = value.inBody.filter((file) => typeof file === "string");
|
|
1478
|
+
if (isSha$1(value.head)) draft.head = value.head;
|
|
1479
|
+
const submit = value.submit;
|
|
1480
|
+
if (isRecord(submit) && typeof submit.id === "string" && isSha$1(submit.commitOid) && (submit.route === "threads" || submit.route === "body") && states.includes(submit.state) && Array.isArray(submit.threadKeys)) {
|
|
1481
|
+
const state = submit.state;
|
|
1482
|
+
draft.submit = _objectSpread2({
|
|
1483
|
+
id: submit.id,
|
|
1484
|
+
commitOid: submit.commitOid,
|
|
1485
|
+
route: submit.route,
|
|
1486
|
+
state: !settle ? state : state === "starting" ? "start-unknown" : state === "threading" ? "left" : state === "submitting" ? "submit-unknown" : state,
|
|
1487
|
+
threadKeys: submit.threadKeys.filter((key) => typeof key === "string")
|
|
1488
|
+
}, typeof submit.sent === "string" && { sent: submit.sent });
|
|
1489
|
+
}
|
|
1490
|
+
return draft;
|
|
1491
|
+
}
|
|
1492
|
+
/** The saved draft of a review, such as `owner/repo#42`, or an empty one. */
|
|
1493
|
+
function readReviewDraft(name) {
|
|
1494
|
+
const memory = memoryDrafts.get(name);
|
|
1495
|
+
if (memory) return memory;
|
|
1496
|
+
try {
|
|
1497
|
+
var _globalThis$localStor;
|
|
1498
|
+
const stored = (_globalThis$localStor = globalThis.localStorage) === null || _globalThis$localStor === void 0 ? void 0 : _globalThis$localStor.getItem(draftKey(name));
|
|
1499
|
+
return stored && readDraftValue(JSON.parse(stored), !running.has(name)) || emptyReviewDraft();
|
|
1500
|
+
} catch (_unused) {
|
|
1501
|
+
return emptyReviewDraft();
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
var isEmpty = (draft) => !draft.body && !draft.submit && !draft.inBody.length && Object.values(draft.comments).every((comment) => !comment.body);
|
|
1505
|
+
/** Saves a draft, or removes it with `null`. Without browser storage, the draft is kept while the page is open. */
|
|
1506
|
+
function writeReviewDraft(name, draft) {
|
|
1507
|
+
var _draftListeners$get;
|
|
1508
|
+
const value = draft && !isEmpty(draft) ? draft : null;
|
|
1509
|
+
try {
|
|
1510
|
+
if (!globalThis.localStorage) throw new Error();
|
|
1511
|
+
if (value) globalThis.localStorage.setItem(draftKey(name), JSON.stringify(value));
|
|
1512
|
+
else globalThis.localStorage.removeItem(draftKey(name));
|
|
1513
|
+
memoryDrafts.delete(name);
|
|
1514
|
+
persistent = true;
|
|
1515
|
+
} catch (_unused2) {
|
|
1516
|
+
persistent = false;
|
|
1517
|
+
if (value) memoryDrafts.set(name, value);
|
|
1518
|
+
else memoryDrafts.delete(name);
|
|
1519
|
+
}
|
|
1520
|
+
(_draftListeners$get = draftListeners.get(name)) === null || _draftListeners$get === void 0 || _draftListeners$get.forEach((listener) => listener());
|
|
1521
|
+
}
|
|
1522
|
+
/** Whether the last write reached browser storage. */
|
|
1523
|
+
var reviewDraftPersistent = () => persistent;
|
|
1524
|
+
function subscribeReviewDraft(name, listener) {
|
|
1525
|
+
var _draftListeners$get2;
|
|
1526
|
+
const listeners = (_draftListeners$get2 = draftListeners.get(name)) !== null && _draftListeners$get2 !== void 0 ? _draftListeners$get2 : /* @__PURE__ */ new Set();
|
|
1527
|
+
draftListeners.set(name, listeners.add(listener));
|
|
1528
|
+
return () => {
|
|
1529
|
+
listeners.delete(listener);
|
|
1530
|
+
if (!listeners.size) draftListeners.delete(name);
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
var threadBody = (comment) => `**${comment.heading}**\n\n${comment.body.trim()}`;
|
|
1534
|
+
/** Puts the overall comment first, then each script comment that is not a thread under its file and heading. */
|
|
1535
|
+
function reviewBody(overall, comments) {
|
|
1536
|
+
const parts = overall.trim() ? [overall.trim()] : [];
|
|
1537
|
+
if (comments.length) {
|
|
1538
|
+
if (parts.length) parts.push("---");
|
|
1539
|
+
for (const { comment } of comments) parts.push(`**${comment.file} · ${comment.heading}**\n\n${comment.body.trim()}`);
|
|
1540
|
+
}
|
|
1541
|
+
return parts.join("\n\n");
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Decides where each script comment goes. A comment becomes a thread when threads are used, its project.source.json is
|
|
1545
|
+
* among the pull request's changed files, GitHub has not refused a thread on that file, and fewer than 50 threads come
|
|
1546
|
+
* before it. Comments in `exclude` are already on the pending review.
|
|
1547
|
+
*/
|
|
1548
|
+
function planReview(draft, { threads, changedFiles, exclude = [] }) {
|
|
1549
|
+
const changed = new Set(changedFiles);
|
|
1550
|
+
const plan = {
|
|
1551
|
+
threads: [],
|
|
1552
|
+
inBody: [],
|
|
1553
|
+
body: ""
|
|
1554
|
+
};
|
|
1555
|
+
const comments = Object.entries(draft.comments).filter(([key, comment]) => comment.body.trim() && !exclude.includes(key)).sort(([a, x], [b, y]) => x.file < y.file ? -1 : x.file > y.file ? 1 : a < b ? -1 : a > b ? 1 : 0);
|
|
1556
|
+
for (const [key, comment] of comments) if (threads && plan.threads.length < 50 && changed.has(comment.file) && isReviewThreadPath(comment.file) && !draft.inBody.includes(comment.file)) plan.threads.push({
|
|
1557
|
+
key,
|
|
1558
|
+
path: comment.file,
|
|
1559
|
+
body: threadBody(comment)
|
|
1560
|
+
});
|
|
1561
|
+
else plan.inBody.push({
|
|
1562
|
+
key,
|
|
1563
|
+
comment
|
|
1564
|
+
});
|
|
1565
|
+
plan.body = reviewBody(draft.body, plan.inBody);
|
|
1566
|
+
return plan;
|
|
1567
|
+
}
|
|
1568
|
+
var bodyFingerprint = (body) => {
|
|
1569
|
+
let hash = 2166136261;
|
|
1570
|
+
for (let index = 0; index < body.length; index++) hash = Math.imul(hash ^ body.charCodeAt(index), 16777619);
|
|
1571
|
+
return body.length.toString(36) + "." + (hash >>> 0).toString(36);
|
|
1572
|
+
};
|
|
1573
|
+
/** What GitHub would refuse before anything is sent. */
|
|
1574
|
+
function reviewProblem(event, plan, route) {
|
|
1575
|
+
const threads = route === "threads" ? plan.threads.length : 0;
|
|
1576
|
+
if (plan.body.length > 65536 || plan.threads.some((thread) => thread.body.length > 65536)) return "TOO_LONG";
|
|
1577
|
+
if (event === "REQUEST_CHANGES" && !plan.body) return "EMPTY_REVIEW";
|
|
1578
|
+
if (event === "COMMENT" && !plan.body && !threads) return "EMPTY_REVIEW";
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
/** Runs one action at a time per review, so the steps of a submission are never sent side by side. */
|
|
1582
|
+
async function exclusive(target, action) {
|
|
1583
|
+
if (running.has(target.name)) return {
|
|
1584
|
+
outcome: "not-sent",
|
|
1585
|
+
code: "BUSY"
|
|
1586
|
+
};
|
|
1587
|
+
running.add(target.name);
|
|
1588
|
+
try {
|
|
1589
|
+
return await action();
|
|
1590
|
+
} finally {
|
|
1591
|
+
running.delete(target.name);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
var failure = (error) => error instanceof GitHubReviewError ? _objectSpread2({ code: error.code }, error.retryAfter !== void 0 && { retryAfter: error.retryAfter }) : { code: "NETWORK_ERROR" };
|
|
1595
|
+
var isUnknown = (error) => error instanceof GitHubReviewError ? error.unknown : true;
|
|
1596
|
+
var codeOf = (error) => error instanceof GitHubReviewError ? error.code : void 0;
|
|
1597
|
+
function update(name, change) {
|
|
1598
|
+
writeReviewDraft(name, change(readReviewDraft(name)));
|
|
1599
|
+
}
|
|
1600
|
+
var setSubmit = (name, submit) => update(name, (draft) => _objectSpread2(_objectSpread2({}, draft), {}, { submit }));
|
|
1601
|
+
var finish = (name) => writeReviewDraft(name, null);
|
|
1602
|
+
/** The route the next submission takes, or `null` when the proxy offers neither. */
|
|
1603
|
+
function reviewRoute(api) {
|
|
1604
|
+
return api.available.threads !== false ? "threads" : api.available.body !== false ? "body" : null;
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Asks the proxy which review routes it offers, with one read: the review context, then, when the GraphQL routes are
|
|
1608
|
+
* off, the list of reviews. A failure other than an unavailable route leaves the route as it was.
|
|
1609
|
+
*/
|
|
1610
|
+
async function probeReviewRoutes(api, number, signal) {
|
|
1611
|
+
let context = null;
|
|
1612
|
+
try {
|
|
1613
|
+
context = await api.context(number, void 0, signal);
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
if (codeOf(error) !== "GITHUB_API_UNAVAILABLE") return {
|
|
1616
|
+
context: null,
|
|
1617
|
+
error
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
if (api.available.threads === false && api.available.body === void 0) try {
|
|
1621
|
+
await api.reviews(number, 1, signal);
|
|
1622
|
+
} catch (error) {
|
|
1623
|
+
if (codeOf(error) !== "GITHUB_API_UNAVAILABLE") return {
|
|
1624
|
+
context,
|
|
1625
|
+
error
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
return { context };
|
|
1629
|
+
}
|
|
1630
|
+
/** Discards the pending review after a failed step. The draft keeps the words either way. */
|
|
1631
|
+
async function discardAfter(target, submit, cause) {
|
|
1632
|
+
const { api, name, number, signal } = target;
|
|
1633
|
+
try {
|
|
1634
|
+
await api.discard(number, submit.id, signal);
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "left" }));
|
|
1637
|
+
return _objectSpread2({
|
|
1638
|
+
outcome: "left",
|
|
1639
|
+
code: "PENDING_REVIEW_LEFT"
|
|
1640
|
+
}, failure(error).retryAfter !== void 0 && { retryAfter: failure(error).retryAfter });
|
|
1641
|
+
}
|
|
1642
|
+
setSubmit(name, void 0);
|
|
1643
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(cause));
|
|
1644
|
+
}
|
|
1645
|
+
async function lastStep(target, submit, draft) {
|
|
1646
|
+
const { api, name, number, signal } = target;
|
|
1647
|
+
const plan = planReview(draft, {
|
|
1648
|
+
threads: false,
|
|
1649
|
+
changedFiles: [],
|
|
1650
|
+
exclude: submit.threadKeys
|
|
1651
|
+
});
|
|
1652
|
+
const problem = reviewProblem(draft.event, _objectSpread2(_objectSpread2({}, plan), {}, { threads: submit.threadKeys.map((key) => ({
|
|
1653
|
+
key,
|
|
1654
|
+
path: "",
|
|
1655
|
+
body: ""
|
|
1656
|
+
})) }), "threads");
|
|
1657
|
+
if (problem) {
|
|
1658
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" }));
|
|
1659
|
+
return {
|
|
1660
|
+
outcome: "pending",
|
|
1661
|
+
code: problem
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "submitting" }));
|
|
1665
|
+
try {
|
|
1666
|
+
const published = await api.submit(number, submit.id, submit.threadKeys.length, draft.event, plan.body, signal);
|
|
1667
|
+
finish(name);
|
|
1668
|
+
return {
|
|
1669
|
+
outcome: "submitted",
|
|
1670
|
+
url: published.url
|
|
1671
|
+
};
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
if (isUnknown(error)) {
|
|
1674
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "submit-unknown" }));
|
|
1675
|
+
return {
|
|
1676
|
+
outcome: "unknown",
|
|
1677
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
if (codeOf(error) === "REVIEW_STEP_CONFLICT") {
|
|
1681
|
+
setSubmit(name, void 0);
|
|
1682
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(error));
|
|
1683
|
+
}
|
|
1684
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" }));
|
|
1685
|
+
return _objectSpread2({ outcome: "pending" }, failure(error));
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
async function sendThreads(target, draft) {
|
|
1689
|
+
const { api, name, number, head, changedFiles, signal } = target;
|
|
1690
|
+
const plan = planReview(draft, {
|
|
1691
|
+
threads: true,
|
|
1692
|
+
changedFiles
|
|
1693
|
+
});
|
|
1694
|
+
const problem = reviewProblem(draft.event, plan, "threads");
|
|
1695
|
+
if (problem) return {
|
|
1696
|
+
outcome: "not-sent",
|
|
1697
|
+
code: problem
|
|
1698
|
+
};
|
|
1699
|
+
let submit = {
|
|
1700
|
+
id: newReviewSubmitId(),
|
|
1701
|
+
commitOid: head,
|
|
1702
|
+
route: "threads",
|
|
1703
|
+
state: "starting",
|
|
1704
|
+
threadKeys: []
|
|
1705
|
+
};
|
|
1706
|
+
setSubmit(name, submit);
|
|
1707
|
+
try {
|
|
1708
|
+
await api.start(number, submit.id, head, signal);
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
if (!isUnknown(error)) {
|
|
1711
|
+
setSubmit(name, void 0);
|
|
1712
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(error));
|
|
1713
|
+
}
|
|
1714
|
+
submit = _objectSpread2(_objectSpread2({}, submit), {}, { state: "start-unknown" });
|
|
1715
|
+
setSubmit(name, submit);
|
|
1716
|
+
return resolveStart(target, submit, error);
|
|
1717
|
+
}
|
|
1718
|
+
submit = _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" });
|
|
1719
|
+
setSubmit(name, submit);
|
|
1720
|
+
for (const [index, thread] of plan.threads.entries()) {
|
|
1721
|
+
submit = _objectSpread2(_objectSpread2({}, submit), {}, { state: "threading" });
|
|
1722
|
+
setSubmit(name, submit);
|
|
1723
|
+
try {
|
|
1724
|
+
await api.thread(number, submit.id, index, thread.path, thread.body, signal);
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
if (codeOf(error) === "REVIEW_THREAD_REJECTED") update(name, (current) => _objectSpread2(_objectSpread2({}, current), {}, { inBody: [.../* @__PURE__ */ new Set([...current.inBody, thread.path])] }));
|
|
1727
|
+
if (codeOf(error) === "REVIEW_STEP_CONFLICT") {
|
|
1728
|
+
setSubmit(name, void 0);
|
|
1729
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(error));
|
|
1730
|
+
}
|
|
1731
|
+
return discardAfter(target, submit, error);
|
|
1732
|
+
}
|
|
1733
|
+
submit = _objectSpread2(_objectSpread2({}, submit), {}, {
|
|
1734
|
+
state: "pending",
|
|
1735
|
+
threadKeys: [...submit.threadKeys, thread.key]
|
|
1736
|
+
});
|
|
1737
|
+
setSubmit(name, submit);
|
|
1738
|
+
}
|
|
1739
|
+
submit = _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" });
|
|
1740
|
+
return lastStep(target, submit, readReviewDraft(name));
|
|
1741
|
+
}
|
|
1742
|
+
/** After an unknown first step: a pending review that the step created is discarded. */
|
|
1743
|
+
async function resolveStart(target, submit, cause) {
|
|
1744
|
+
const { api, name, number, signal } = target;
|
|
1745
|
+
let state;
|
|
1746
|
+
try {
|
|
1747
|
+
var _await$api$context$su;
|
|
1748
|
+
state = (_await$api$context$su = (await api.context(number, submit.id, signal)).submit) === null || _await$api$context$su === void 0 ? void 0 : _await$api$context$su.state;
|
|
1749
|
+
} catch (error) {
|
|
1750
|
+
if (codeOf(error) === "REVIEW_STEP_CONFLICT") return lost(target, submit);
|
|
1751
|
+
return {
|
|
1752
|
+
outcome: "unknown",
|
|
1753
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
if (state === "closed") {
|
|
1757
|
+
setSubmit(name, void 0);
|
|
1758
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(cause));
|
|
1759
|
+
}
|
|
1760
|
+
if (state === "pending" || state === "broken") return discardAfter(target, _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" }), cause);
|
|
1761
|
+
return {
|
|
1762
|
+
outcome: "unknown",
|
|
1763
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
async function lost(target, submit) {
|
|
1767
|
+
let reviews = [];
|
|
1768
|
+
try {
|
|
1769
|
+
reviews = (await target.api.context(target.number, void 0, target.signal)).reviews.filter((review) => review.commitOid === submit.commitOid).map(({ state, submittedAt }) => ({
|
|
1770
|
+
state,
|
|
1771
|
+
submittedAt
|
|
1772
|
+
}));
|
|
1773
|
+
} catch (_unused3) {}
|
|
1774
|
+
return {
|
|
1775
|
+
outcome: "lost",
|
|
1776
|
+
code: "REVIEW_STEP_CONFLICT",
|
|
1777
|
+
reviews
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
async function sendBody(target, draft) {
|
|
1781
|
+
const { api, name, number, head, signal } = target;
|
|
1782
|
+
const plan = planReview(draft, {
|
|
1783
|
+
threads: false,
|
|
1784
|
+
changedFiles: []
|
|
1785
|
+
});
|
|
1786
|
+
const problem = reviewProblem(draft.event, plan, "body");
|
|
1787
|
+
if (problem) return {
|
|
1788
|
+
outcome: "not-sent",
|
|
1789
|
+
code: problem
|
|
1790
|
+
};
|
|
1791
|
+
const submit = {
|
|
1792
|
+
id: newReviewSubmitId(),
|
|
1793
|
+
commitOid: head,
|
|
1794
|
+
route: "body",
|
|
1795
|
+
state: "submitting",
|
|
1796
|
+
threadKeys: [],
|
|
1797
|
+
sent: bodyFingerprint(plan.body)
|
|
1798
|
+
};
|
|
1799
|
+
setSubmit(name, submit);
|
|
1800
|
+
try {
|
|
1801
|
+
const created = await api.create(number, submit.id, head, draft.event, plan.body, signal);
|
|
1802
|
+
finish(name);
|
|
1803
|
+
return {
|
|
1804
|
+
outcome: "submitted",
|
|
1805
|
+
url: created.url
|
|
1806
|
+
};
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
if (isUnknown(error)) {
|
|
1809
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "submit-unknown" }));
|
|
1810
|
+
return {
|
|
1811
|
+
outcome: "unknown",
|
|
1812
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
setSubmit(name, void 0);
|
|
1816
|
+
return _objectSpread2({ outcome: "not-sent" }, failure(error));
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Sends the draft, or, after the last step was refused (`pending`), sends that step again with the current event and
|
|
1821
|
+
* overall comment. A submission whose outcome is unknown, or whose pending review was left, is not sent again.
|
|
1822
|
+
*/
|
|
1823
|
+
function sendReview(target) {
|
|
1824
|
+
return exclusive(target, async () => {
|
|
1825
|
+
const draft = readReviewDraft(target.name);
|
|
1826
|
+
const submit = draft.submit;
|
|
1827
|
+
if ((submit === null || submit === void 0 ? void 0 : submit.state) === "pending" && submit.route === "threads") return lastStep(target, submit, draft);
|
|
1828
|
+
if ((submit === null || submit === void 0 ? void 0 : submit.state) === "left") return {
|
|
1829
|
+
outcome: "left",
|
|
1830
|
+
code: "PENDING_REVIEW_LEFT"
|
|
1831
|
+
};
|
|
1832
|
+
if (submit) return {
|
|
1833
|
+
outcome: "unknown",
|
|
1834
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1835
|
+
};
|
|
1836
|
+
const route = reviewRoute(target.api);
|
|
1837
|
+
if (!route) return {
|
|
1838
|
+
outcome: "not-sent",
|
|
1839
|
+
code: "UNSUPPORTED"
|
|
1840
|
+
};
|
|
1841
|
+
const result = await (route === "threads" ? sendThreads(target, draft) : sendBody(target, draft));
|
|
1842
|
+
if (result.code === "GITHUB_API_UNAVAILABLE" && !reviewRoute(target.api)) return _objectSpread2(_objectSpread2({}, result), {}, { code: "UNSUPPORTED" });
|
|
1843
|
+
return result;
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
/** Finds out what happened to a submission whose outcome is unknown. Reading is safe to repeat. */
|
|
1847
|
+
function checkReview(target) {
|
|
1848
|
+
return exclusive(target, async () => {
|
|
1849
|
+
const { api, name, number, signal } = target;
|
|
1850
|
+
const submit = readReviewDraft(name).submit;
|
|
1851
|
+
if (!submit) return { outcome: "not-sent" };
|
|
1852
|
+
if (submit.route === "body") {
|
|
1853
|
+
const login = api.account();
|
|
1854
|
+
try {
|
|
1855
|
+
for (let page = 1; page <= 10; page++) {
|
|
1856
|
+
const reviews = await api.reviews(number, page, signal);
|
|
1857
|
+
if (reviews.some((review) => review.login === login && review.commitId === submit.commitOid && bodyFingerprint(review.body) === submit.sent)) {
|
|
1858
|
+
finish(name);
|
|
1859
|
+
return { outcome: "submitted" };
|
|
1860
|
+
}
|
|
1861
|
+
if (reviews.length < 30) break;
|
|
1862
|
+
}
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
return _objectSpread2({ outcome: "unknown" }, failure(error));
|
|
1865
|
+
}
|
|
1866
|
+
setSubmit(name, void 0);
|
|
1867
|
+
return { outcome: "closed" };
|
|
1868
|
+
}
|
|
1869
|
+
if (submit.state === "start-unknown") return resolveStart(target, submit, new GitHubReviewError("UPSTREAM_FAILURE"));
|
|
1870
|
+
let state;
|
|
1871
|
+
try {
|
|
1872
|
+
var _await$api$context$su2;
|
|
1873
|
+
state = (_await$api$context$su2 = (await api.context(number, submit.id, signal)).submit) === null || _await$api$context$su2 === void 0 ? void 0 : _await$api$context$su2.state;
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
if (codeOf(error) === "REVIEW_STEP_CONFLICT") return lost(target, submit);
|
|
1876
|
+
return _objectSpread2({ outcome: "unknown" }, failure(error));
|
|
1877
|
+
}
|
|
1878
|
+
if (state === "submitted") {
|
|
1879
|
+
finish(name);
|
|
1880
|
+
return { outcome: "submitted" };
|
|
1881
|
+
}
|
|
1882
|
+
if (state === "closed") {
|
|
1883
|
+
setSubmit(name, void 0);
|
|
1884
|
+
return { outcome: "closed" };
|
|
1885
|
+
}
|
|
1886
|
+
if (state === "pending") {
|
|
1887
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "pending" }));
|
|
1888
|
+
return { outcome: "pending" };
|
|
1889
|
+
}
|
|
1890
|
+
if (state === "broken") {
|
|
1891
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "left" }));
|
|
1892
|
+
return {
|
|
1893
|
+
outcome: "left",
|
|
1894
|
+
code: "PENDING_REVIEW_LEFT"
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
return {
|
|
1898
|
+
outcome: "unknown",
|
|
1899
|
+
code: "REVIEW_OUTCOME_UNKNOWN"
|
|
1900
|
+
};
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
/** Discards the submission's pending review on GitHub, when one was left or the last step was refused. */
|
|
1904
|
+
function discardReview(target) {
|
|
1905
|
+
return exclusive(target, async () => {
|
|
1906
|
+
const { api, name, number, signal } = target;
|
|
1907
|
+
const submit = readReviewDraft(name).submit;
|
|
1908
|
+
if (!submit || submit.route !== "threads" || submit.state !== "pending" && submit.state !== "left") return {
|
|
1909
|
+
outcome: "not-sent",
|
|
1910
|
+
code: "NOT_ALLOWED"
|
|
1911
|
+
};
|
|
1912
|
+
try {
|
|
1913
|
+
await api.discard(number, submit.id, signal);
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
if (codeOf(error) === "REVIEW_DUPLICATE" || codeOf(error) === "REVIEW_STEP_CONFLICT") try {
|
|
1916
|
+
var _await$api$context$su3;
|
|
1917
|
+
if (((_await$api$context$su3 = (await api.context(number, submit.id, signal)).submit) === null || _await$api$context$su3 === void 0 ? void 0 : _await$api$context$su3.state) === "closed") {
|
|
1918
|
+
setSubmit(name, void 0);
|
|
1919
|
+
return { outcome: "not-sent" };
|
|
1920
|
+
}
|
|
1921
|
+
} catch (check) {
|
|
1922
|
+
if (codeOf(check) === "REVIEW_STEP_CONFLICT") return lost(target, submit);
|
|
1923
|
+
}
|
|
1924
|
+
setSubmit(name, _objectSpread2(_objectSpread2({}, submit), {}, { state: "left" }));
|
|
1925
|
+
return _objectSpread2({
|
|
1926
|
+
outcome: "left",
|
|
1927
|
+
code: "PENDING_REVIEW_LEFT"
|
|
1928
|
+
}, failure(error).retryAfter !== void 0 && { retryAfter: failure(error).retryAfter });
|
|
1929
|
+
}
|
|
1930
|
+
setSubmit(name, void 0);
|
|
1931
|
+
return { outcome: "not-sent" };
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
/** Forgets a submission the proxy no longer knows, keeping the words. Anything left on GitHub is handled there. */
|
|
1935
|
+
function forgetReviewSubmission(name) {
|
|
1936
|
+
setSubmit(name, void 0);
|
|
1937
|
+
}
|
|
1938
|
+
// src/review-composer.tsx
|
|
1939
|
+
init_objectSpread2();
|
|
1940
|
+
/** The saved draft of a review, updated when any view of the same review writes it. */
|
|
1941
|
+
function useReviewDraft(name) {
|
|
1942
|
+
const [, refresh] = useState(0);
|
|
1943
|
+
useEffect(() => name ? subscribeReviewDraft(name, () => refresh((count) => count + 1)) : void 0, [name]);
|
|
1944
|
+
return name ? readReviewDraft(name) : null;
|
|
1945
|
+
}
|
|
1946
|
+
/** Changes the saved draft from what is stored now, so other views' words are kept. */
|
|
1947
|
+
function changeReviewDraft(name, change) {
|
|
1948
|
+
writeReviewDraft(name, change(readReviewDraft(name)));
|
|
1949
|
+
}
|
|
1950
|
+
var sessionCodes = [
|
|
1951
|
+
"SESSION_REQUIRED",
|
|
1952
|
+
"SESSION_EXPIRED",
|
|
1953
|
+
"REAUTH_REQUIRED",
|
|
1954
|
+
"SESSION_UNAVAILABLE",
|
|
1955
|
+
"INVALID_SESSION"
|
|
1956
|
+
];
|
|
1957
|
+
function resultMessage(result, labels) {
|
|
1958
|
+
var _result$retryAfter, _result$reviews$lengt, _result$reviews;
|
|
1959
|
+
const code = result.code;
|
|
1960
|
+
if (code === "RATE_LIMITED") return labels.RATE_LIMITED.replace("{seconds}", String((_result$retryAfter = result.retryAfter) !== null && _result$retryAfter !== void 0 ? _result$retryAfter : 60));
|
|
1961
|
+
if (result.outcome === "submitted") return labels.sent;
|
|
1962
|
+
if (result.outcome === "closed") return labels.closed;
|
|
1963
|
+
if (result.outcome === "lost") return labels.lost.replace("{count}", String((_result$reviews$lengt = (_result$reviews = result.reviews) === null || _result$reviews === void 0 ? void 0 : _result$reviews.length) !== null && _result$reviews$lengt !== void 0 ? _result$reviews$lengt : 0));
|
|
1964
|
+
if (result.outcome === "left") return labels.PENDING_REVIEW_LEFT;
|
|
1965
|
+
if (result.outcome === "unknown") return labels.REVIEW_OUTCOME_UNKNOWN;
|
|
1966
|
+
if (code && sessionCodes.includes(code)) return labels.session;
|
|
1967
|
+
if (code === "PENDING_REVIEW_EXISTS" || code === "REVIEW_REJECTED" || code === "REVIEW_THREAD_REJECTED" || code === "EMPTY_REVIEW" || code === "TOO_LONG" || code === "FORBIDDEN" || code === "GITHUB_API_UNAVAILABLE") {
|
|
1968
|
+
const message = labels[code];
|
|
1969
|
+
return result.outcome === "pending" ? labels.pending + " " + message : message;
|
|
1970
|
+
}
|
|
1971
|
+
if (code === "UNSUPPORTED") return labels.unsupported;
|
|
1972
|
+
if (result.outcome === "pending") return labels.pending;
|
|
1973
|
+
if (result.outcome === "not-sent" && !code) return labels.discarded;
|
|
1974
|
+
return labels.failed;
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* The overall comment, the result, how the code comments will be sent, and the send button of a pull request review.
|
|
1978
|
+
* The code comments are written under each code change of the review that shares `name`.
|
|
1979
|
+
*/
|
|
1980
|
+
function PullRequestReviewComposer({ api, name, number, head, changedFiles, url, locale }) {
|
|
1981
|
+
const labels = reviewLabelsFor(locale);
|
|
1982
|
+
const draft = useReviewDraft(name);
|
|
1983
|
+
const [route, setRoute] = useState(() => reviewRoute(api));
|
|
1984
|
+
const [busy, setBusy] = useState(null);
|
|
1985
|
+
const [result, setResult] = useState(null);
|
|
1986
|
+
const [blocked, setBlocked] = useState(false);
|
|
1987
|
+
const active = useRef(true);
|
|
1988
|
+
useEffect(() => {
|
|
1989
|
+
active.current = true;
|
|
1990
|
+
return () => {
|
|
1991
|
+
active.current = false;
|
|
1992
|
+
};
|
|
1993
|
+
}, []);
|
|
1994
|
+
const target = {
|
|
1995
|
+
api,
|
|
1996
|
+
name,
|
|
1997
|
+
number,
|
|
1998
|
+
head,
|
|
1999
|
+
changedFiles
|
|
2000
|
+
};
|
|
2001
|
+
const run = async (kind, action) => {
|
|
2002
|
+
if (busy || blocked) return;
|
|
2003
|
+
setBusy(kind);
|
|
2004
|
+
setResult(null);
|
|
2005
|
+
let next;
|
|
2006
|
+
try {
|
|
2007
|
+
next = await action();
|
|
2008
|
+
} catch (_unused) {
|
|
2009
|
+
next = {
|
|
2010
|
+
outcome: "not-sent",
|
|
2011
|
+
code: "NETWORK_ERROR"
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
if (!active.current) return;
|
|
2015
|
+
setBusy(null);
|
|
2016
|
+
setRoute(reviewRoute(api));
|
|
2017
|
+
if (next.code === "BUSY") return;
|
|
2018
|
+
setResult(next);
|
|
2019
|
+
if (next.retryAfter) {
|
|
2020
|
+
setBlocked(true);
|
|
2021
|
+
setTimeout(() => {
|
|
2022
|
+
if (active.current) setBlocked(false);
|
|
2023
|
+
}, next.retryAfter * 1e3);
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
useEffect(() => {
|
|
2027
|
+
const controller = new AbortController();
|
|
2028
|
+
(async () => {
|
|
2029
|
+
var _readReviewDraft$subm;
|
|
2030
|
+
await probeReviewRoutes(api, number, controller.signal);
|
|
2031
|
+
if (controller.signal.aborted) return;
|
|
2032
|
+
setRoute(reviewRoute(api));
|
|
2033
|
+
const state = (_readReviewDraft$subm = readReviewDraft(name).submit) === null || _readReviewDraft$subm === void 0 ? void 0 : _readReviewDraft$subm.state;
|
|
2034
|
+
if (state === "start-unknown" || state === "submit-unknown") await run("check", () => checkReview(target));
|
|
2035
|
+
})();
|
|
2036
|
+
return () => controller.abort();
|
|
2037
|
+
}, [
|
|
2038
|
+
api,
|
|
2039
|
+
name,
|
|
2040
|
+
number
|
|
2041
|
+
]);
|
|
2042
|
+
const hasWords = !!draft.body || Object.values(draft.comments).some((comment) => comment.body);
|
|
2043
|
+
useEffect(() => {
|
|
2044
|
+
if (hasWords && !draft.head) changeReviewDraft(name, (current) => _objectSpread2(_objectSpread2({}, current), {}, { head }));
|
|
2045
|
+
}, [
|
|
2046
|
+
hasWords,
|
|
2047
|
+
draft.head,
|
|
2048
|
+
head,
|
|
2049
|
+
name
|
|
2050
|
+
]);
|
|
2051
|
+
const submit = draft.submit;
|
|
2052
|
+
const plan = planReview(draft, {
|
|
2053
|
+
threads: route === "threads" && (submit === null || submit === void 0 ? void 0 : submit.state) !== "pending",
|
|
2054
|
+
changedFiles,
|
|
2055
|
+
exclude: submit === null || submit === void 0 ? void 0 : submit.threadKeys
|
|
2056
|
+
});
|
|
2057
|
+
const threads = (submit === null || submit === void 0 ? void 0 : submit.state) === "pending" ? submit.threadKeys.length : route === "threads" ? plan.threads.length : 0;
|
|
2058
|
+
const comments = ((submit === null || submit === void 0 ? void 0 : submit.state) === "pending" ? submit.threadKeys.length : 0) + plan.threads.length + plan.inBody.length;
|
|
2059
|
+
const problem = route && reviewProblem(draft.event, _objectSpread2(_objectSpread2({}, plan), {}, { threads: Array.from({ length: threads }, () => ({
|
|
2060
|
+
key: "",
|
|
2061
|
+
path: "",
|
|
2062
|
+
body: ""
|
|
2063
|
+
})) }), route);
|
|
2064
|
+
const disabled = !!busy || blocked;
|
|
2065
|
+
const message = result && resultMessage(result, labels);
|
|
2066
|
+
const failed = result && result.outcome !== "submitted" && !(result.outcome === "not-sent" && !result.code);
|
|
2067
|
+
const pullLink = /* @__PURE__ */ React.createElement("a", {
|
|
2068
|
+
href: url,
|
|
2069
|
+
target: "_blank",
|
|
2070
|
+
rel: "noreferrer"
|
|
2071
|
+
}, labels.openPullRequest);
|
|
2072
|
+
return /* @__PURE__ */ React.createElement("section", {
|
|
2073
|
+
className: "twgit-review-composer",
|
|
2074
|
+
"aria-label": labels.composer,
|
|
2075
|
+
"data-git-review-composer": ""
|
|
2076
|
+
}, /* @__PURE__ */ React.createElement("h3", null, labels.composer), !reviewDraftPersistent() && /* @__PURE__ */ React.createElement("p", {
|
|
2077
|
+
role: "status",
|
|
2078
|
+
className: "twgit-review-note"
|
|
2079
|
+
}, labels.notStored), draft.head && draft.head !== head && /* @__PURE__ */ React.createElement("p", {
|
|
2080
|
+
role: "status",
|
|
2081
|
+
className: "twgit-review-note"
|
|
2082
|
+
}, labels.headChanged, " ", /* @__PURE__ */ React.createElement("button", {
|
|
2083
|
+
type: "button",
|
|
2084
|
+
onClick: () => changeReviewDraft(name, (current) => _objectSpread2(_objectSpread2({}, current), {}, { head }))
|
|
2085
|
+
}, labels.useVersion)), /* @__PURE__ */ React.createElement("label", { className: "twgit-review-field" }, labels.overall, /* @__PURE__ */ React.createElement("textarea", {
|
|
2086
|
+
value: draft.body,
|
|
2087
|
+
rows: 4,
|
|
2088
|
+
onChange: (event) => {
|
|
2089
|
+
const body = event.currentTarget.value;
|
|
2090
|
+
changeReviewDraft(name, (current) => _objectSpread2(_objectSpread2({}, current), {}, { body }));
|
|
2091
|
+
}
|
|
2092
|
+
})), /* @__PURE__ */ React.createElement("fieldset", { className: "twgit-review-events" }, /* @__PURE__ */ React.createElement("legend", null, labels.event), reviewEvents.map((event) => /* @__PURE__ */ React.createElement("label", { key: event }, /* @__PURE__ */ React.createElement("input", {
|
|
2093
|
+
type: "radio",
|
|
2094
|
+
name: `twgit-review-event-${name}`,
|
|
2095
|
+
value: event,
|
|
2096
|
+
checked: draft.event === event,
|
|
2097
|
+
onChange: () => changeReviewDraft(name, (current) => _objectSpread2(_objectSpread2({}, current), {}, { event }))
|
|
2098
|
+
}), " ", labels[event]))), route === null ? /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.unsupported) : /* @__PURE__ */ React.createElement(React.Fragment, null, (!submit || submit.state === "pending") && /* @__PURE__ */ React.createElement("p", {
|
|
2099
|
+
className: "twgit-review-summary",
|
|
2100
|
+
"data-git-review-summary": ""
|
|
2101
|
+
}, !comments ? labels.summaryNone : route === "threads" ? labels.summaryThreads.replace("{threads}", String(threads)).replace("{body}", String(plan.inBody.length)) : labels.summaryBody.replace("{body}", String(plan.inBody.length))), /* @__PURE__ */ React.createElement("div", { className: "twgit-review-actions" }, (!submit || submit.state === "pending") && /* @__PURE__ */ React.createElement("button", {
|
|
2102
|
+
type: "button",
|
|
2103
|
+
"data-git-review-send": "",
|
|
2104
|
+
disabled: disabled || !!problem,
|
|
2105
|
+
title: problem ? resultMessage({
|
|
2106
|
+
outcome: "not-sent",
|
|
2107
|
+
code: problem
|
|
2108
|
+
}, labels) : void 0,
|
|
2109
|
+
onClick: () => void run("send", () => sendReview(target))
|
|
2110
|
+
}, submit ? labels.sendAgain : labels.send), ((submit === null || submit === void 0 ? void 0 : submit.state) === "start-unknown" || (submit === null || submit === void 0 ? void 0 : submit.state) === "submit-unknown") && /* @__PURE__ */ React.createElement("button", {
|
|
2111
|
+
type: "button",
|
|
2112
|
+
disabled,
|
|
2113
|
+
onClick: () => void run("check", () => checkReview(target))
|
|
2114
|
+
}, labels.check), ((submit === null || submit === void 0 ? void 0 : submit.state) === "pending" || (submit === null || submit === void 0 ? void 0 : submit.state) === "left") && /* @__PURE__ */ React.createElement("button", {
|
|
2115
|
+
type: "button",
|
|
2116
|
+
disabled,
|
|
2117
|
+
onClick: () => void run("send", () => discardReview(target))
|
|
2118
|
+
}, labels.discard), ((submit === null || submit === void 0 ? void 0 : submit.state) === "left" || (result === null || result === void 0 ? void 0 : result.outcome) === "lost") && /* @__PURE__ */ React.createElement(React.Fragment, null, pullLink, /* @__PURE__ */ React.createElement("button", {
|
|
2119
|
+
type: "button",
|
|
2120
|
+
disabled,
|
|
2121
|
+
onClick: () => {
|
|
2122
|
+
forgetReviewSubmission(name);
|
|
2123
|
+
setResult(null);
|
|
2124
|
+
}
|
|
2125
|
+
}, labels.forget))), busy && /* @__PURE__ */ React.createElement("p", { role: "status" }, busy === "send" ? labels.sending : labels.checking), !busy && !result && submit && /* @__PURE__ */ React.createElement("p", {
|
|
2126
|
+
role: "status",
|
|
2127
|
+
className: "twgit-review-state"
|
|
2128
|
+
}, submit.state === "pending" ? labels.pending : submit.state === "left" ? labels.PENDING_REVIEW_LEFT : labels.REVIEW_OUTCOME_UNKNOWN), message && result && /* @__PURE__ */ React.createElement("div", {
|
|
2129
|
+
className: failed ? "twgit-review-result twgit-error" : "twgit-review-result twgit-context",
|
|
2130
|
+
role: failed ? "alert" : "status"
|
|
2131
|
+
}, /* @__PURE__ */ React.createElement("p", null, message, result.code === "PENDING_REVIEW_EXISTS" && /* @__PURE__ */ React.createElement(React.Fragment, null, " ", pullLink), result.outcome === "submitted" && result.url && /* @__PURE__ */ React.createElement(React.Fragment, null, " ", /* @__PURE__ */ React.createElement("a", {
|
|
2132
|
+
href: result.url,
|
|
2133
|
+
target: "_blank",
|
|
2134
|
+
rel: "noreferrer"
|
|
2135
|
+
}, labels.openReview))), /* @__PURE__ */ React.createElement(CloseButton, {
|
|
2136
|
+
label: labels.dismiss,
|
|
2137
|
+
onClick: () => setResult(null)
|
|
2138
|
+
}))));
|
|
2139
|
+
}
|
|
790
2140
|
// src/project-source-review.tsx
|
|
791
2141
|
init_objectSpread2();
|
|
792
2142
|
var ja = {
|
|
@@ -891,9 +2241,9 @@ var properties = /* @__PURE__ */ new Map([
|
|
|
891
2241
|
]);
|
|
892
2242
|
function scriptTitle(side, labels) {
|
|
893
2243
|
const block = side.target.blocks[side.id];
|
|
894
|
-
if (!isRecord(block)) return labels.script;
|
|
2244
|
+
if (!isRecord$2(block)) return labels.script;
|
|
895
2245
|
if (block.opcode === "event_whenbroadcastreceived") {
|
|
896
|
-
const field = isRecord(block.fields) ? block.fields.BROADCAST_OPTION : void 0;
|
|
2246
|
+
const field = isRecord$2(block.fields) ? block.fields.BROADCAST_OPTION : void 0;
|
|
897
2247
|
const message = Array.isArray(field) && typeof field[0] === "string" ? field[0] : "";
|
|
898
2248
|
if (message) return labels === ja ? `「${message}」${labels.broadcast}` : `“${message}” ${labels.broadcast}`;
|
|
899
2249
|
}
|
|
@@ -947,7 +2297,7 @@ function scriptReviewTitle(script, labels) {
|
|
|
947
2297
|
const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
|
|
948
2298
|
return beforeTitle && afterTitle && beforeTitle !== afterTitle ? beforeTitle + " → " + afterTitle : afterTitle !== null && afterTitle !== void 0 ? afterTitle : beforeTitle;
|
|
949
2299
|
}
|
|
950
|
-
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset, location, expanded = false }) {
|
|
2300
|
+
function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset, location, expanded = false, comment, target }) {
|
|
951
2301
|
const [open, setOpen] = useState(expanded);
|
|
952
2302
|
const { sync, toggle } = usePreviewSync(labels === ja ? "ja" : "en");
|
|
953
2303
|
const status = script.kind;
|
|
@@ -958,7 +2308,7 @@ function ScriptComparison({ script, labels, reviewed = false, setReviewed, rende
|
|
|
958
2308
|
className: "twgit-review-script",
|
|
959
2309
|
open: expanded || void 0,
|
|
960
2310
|
onToggle: (event) => setOpen(event.currentTarget.open)
|
|
961
|
-
}, /* @__PURE__ */ React.createElement("summary", null, /* @__PURE__ */ React.createElement("span", { className: "twgit-review-status" }, labels[status]), /* @__PURE__ */ React.createElement("strong", null, title), status === "reorganized" && /* @__PURE__ */ React.createElement("small", null, script.before.length, " → ", script.after.length), summary && /* @__PURE__ */ React.createElement("small", { className: "twgit-review-summary" }, summary), reviewed && /* @__PURE__ */ React.createElement("small", null, labels.reviewed)), open && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-sides" }, ["before", "after"].map((version) => /* @__PURE__ */ React.createElement("div", { key: version }, script[version].length ? script[version].map((side, index, sides) => /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
2311
|
+
}, /* @__PURE__ */ React.createElement("summary", null, /* @__PURE__ */ React.createElement("span", { className: "twgit-review-status" }, labels[status]), /* @__PURE__ */ React.createElement("strong", null, title), status === "reorganized" && /* @__PURE__ */ React.createElement("small", null, script.before.length, " → ", script.after.length), summary && /* @__PURE__ */ React.createElement("small", { className: "twgit-review-summary" }, summary), reviewed && /* @__PURE__ */ React.createElement("small", null, labels.reviewed), (comment === null || comment === void 0 ? void 0 : comment.value) && /* @__PURE__ */ React.createElement("small", { "data-git-review-commented": "" }, reviewLabelsFor(locale).commented)), open && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "twgit-block-diff-sides" }, ["before", "after"].map((version) => /* @__PURE__ */ React.createElement("div", { key: version }, script[version].length ? script[version].map((side, index, sides) => /* @__PURE__ */ React.createElement(BlockPreview, {
|
|
962
2312
|
key: side.id,
|
|
963
2313
|
side,
|
|
964
2314
|
title: sides.length > 1 ? `${labels[version]} ${index + 1}` : labels[version],
|
|
@@ -980,7 +2330,14 @@ function ScriptComparison({ script, labels, reviewed = false, setReviewed, rende
|
|
|
980
2330
|
type: "checkbox",
|
|
981
2331
|
checked: reviewed,
|
|
982
2332
|
onChange: (event) => setReviewed(event.currentTarget.checked)
|
|
983
|
-
}), " ", labels.reviewed))
|
|
2333
|
+
}), " ", labels.reviewed), comment && /* @__PURE__ */ React.createElement("label", { className: "twgit-review-field twgit-review-comment" }, reviewLabelsFor(locale).scriptComment, /* @__PURE__ */ React.createElement("textarea", {
|
|
2334
|
+
rows: 3,
|
|
2335
|
+
value: comment.value,
|
|
2336
|
+
onChange: (event) => {
|
|
2337
|
+
var _target$name;
|
|
2338
|
+
return comment.set(event.currentTarget.value, `${(target === null || target === void 0 ? void 0 : target.isStage) ? labels.stage : (_target$name = target === null || target === void 0 ? void 0 : target.name) !== null && _target$name !== void 0 ? _target$name : ""} · ${title} · ${script.id}`);
|
|
2339
|
+
}
|
|
2340
|
+
}))));
|
|
984
2341
|
}
|
|
985
2342
|
function propertyNames(keys, labels) {
|
|
986
2343
|
const locale = labels === ja ? "ja" : "en";
|
|
@@ -1030,6 +2387,7 @@ function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, locateB
|
|
|
1030
2387
|
loadAsset
|
|
1031
2388
|
}), target.isStage ? labels.stage : target.name, target.scripts.length > 0 && /* @__PURE__ */ React.createElement("small", null, labels.scriptCount(target.scripts.length))), target.status !== "updated" && /* @__PURE__ */ React.createElement("p", { className: "twgit-review-target-status" }, (target.status === "added" ? labels.targetAdded : labels.targetRemoved)(target.name, target.isStage)), target.status === "updated" && target.properties.length > 0 && /* @__PURE__ */ React.createElement("p", null, labels.properties, " · ", propertyNames(target.properties, labels)), target.scripts.map((script) => {
|
|
1032
2389
|
const key = reviewKey(review === null || review === void 0 ? void 0 : review.file, target, script);
|
|
2390
|
+
const comments = review === null || review === void 0 ? void 0 : review.comments;
|
|
1033
2391
|
return /* @__PURE__ */ React.createElement(ScriptComparison, {
|
|
1034
2392
|
key,
|
|
1035
2393
|
script,
|
|
@@ -1038,7 +2396,12 @@ function ComparisonTargets({ comparison, labels, renderBlock, loadAsset, locateB
|
|
|
1038
2396
|
loadAsset,
|
|
1039
2397
|
location,
|
|
1040
2398
|
reviewed: review === null || review === void 0 ? void 0 : review.keys.has(key),
|
|
1041
|
-
setReviewed: review && ((value) => review.set(key, value))
|
|
2399
|
+
setReviewed: review && ((value) => review.set(key, value)),
|
|
2400
|
+
target,
|
|
2401
|
+
comment: comments && {
|
|
2402
|
+
value: comments.get(key),
|
|
2403
|
+
set: (value, heading) => comments.set(key, value, heading)
|
|
2404
|
+
}
|
|
1042
2405
|
});
|
|
1043
2406
|
}))));
|
|
1044
2407
|
}
|
|
@@ -1065,8 +2428,8 @@ function assetOwner(path, status, entries) {
|
|
|
1065
2428
|
for (const kind of ["costumes", "sounds"]) {
|
|
1066
2429
|
const list = owner[kind];
|
|
1067
2430
|
if (!Array.isArray(list)) continue;
|
|
1068
|
-
const entry = list.find((item) => isRecord(item) && assetFileName(item) === name);
|
|
1069
|
-
if (isRecord(entry)) return {
|
|
2431
|
+
const entry = list.find((item) => isRecord$2(item) && assetFileName(item) === name);
|
|
2432
|
+
if (isRecord$2(entry)) return {
|
|
1070
2433
|
target,
|
|
1071
2434
|
version,
|
|
1072
2435
|
md5ext: name,
|
|
@@ -1193,8 +2556,25 @@ var writeMarks = (name, marks, remember = true) => {
|
|
|
1193
2556
|
} catch (_unused4) {}
|
|
1194
2557
|
(_markListeners$get2 = markListeners.get(name)) === null || _markListeners$get2 === void 0 || _markListeners$get2.forEach((listener) => listener());
|
|
1195
2558
|
};
|
|
1196
|
-
function ProjectSourceReview({ title, description, verification, files, renderBlock, locale = "en", rememberAs }) {
|
|
2559
|
+
function ProjectSourceReview({ title, description, verification, files, renderBlock, locale = "en", rememberAs, commentAs }) {
|
|
1197
2560
|
const labels = locale.startsWith("ja") ? ja : en;
|
|
2561
|
+
const draft = useReviewDraft(commentAs);
|
|
2562
|
+
const commentsFor = (file) => commentAs && draft ? {
|
|
2563
|
+
get: (key) => {
|
|
2564
|
+
var _draft$comments$key$b, _draft$comments$key;
|
|
2565
|
+
return (_draft$comments$key$b = (_draft$comments$key = draft.comments[key]) === null || _draft$comments$key === void 0 ? void 0 : _draft$comments$key.body) !== null && _draft$comments$key$b !== void 0 ? _draft$comments$key$b : "";
|
|
2566
|
+
},
|
|
2567
|
+
set: (key, body, heading) => changeReviewDraft(commentAs, (current) => {
|
|
2568
|
+
const comments = _objectSpread2({}, current.comments);
|
|
2569
|
+
if (body) comments[key] = {
|
|
2570
|
+
file,
|
|
2571
|
+
heading,
|
|
2572
|
+
body
|
|
2573
|
+
};
|
|
2574
|
+
else delete comments[key];
|
|
2575
|
+
return _objectSpread2(_objectSpread2({}, current), {}, { comments });
|
|
2576
|
+
})
|
|
2577
|
+
} : void 0;
|
|
1198
2578
|
const entries = useMemo(() => files.map((file) => {
|
|
1199
2579
|
if (!("patch" in file)) return { file };
|
|
1200
2580
|
try {
|
|
@@ -1267,7 +2647,8 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
|
|
|
1267
2647
|
review: {
|
|
1268
2648
|
file: file.path,
|
|
1269
2649
|
keys: reviewed,
|
|
1270
|
-
set: setReviewed
|
|
2650
|
+
set: setReviewed,
|
|
2651
|
+
comments: commentsFor(file.path)
|
|
1271
2652
|
}
|
|
1272
2653
|
}))));
|
|
1273
2654
|
}
|
|
@@ -1374,15 +2755,15 @@ async function readText(response, limit) {
|
|
|
1374
2755
|
return chunks.join("") + decoder.decode();
|
|
1375
2756
|
}
|
|
1376
2757
|
function readSummary(value) {
|
|
1377
|
-
if (!isRecord(value) || !Number.isSafeInteger(value.number) || Number(value.number) < 1 || typeof value.title !== "string") throw invalidResponse();
|
|
2758
|
+
if (!isRecord$2(value) || !Number.isSafeInteger(value.number) || Number(value.number) < 1 || typeof value.title !== "string") throw invalidResponse();
|
|
1378
2759
|
return {
|
|
1379
2760
|
number: Number(value.number),
|
|
1380
2761
|
title: value.title
|
|
1381
2762
|
};
|
|
1382
2763
|
}
|
|
1383
2764
|
function readRevision(value) {
|
|
1384
|
-
if (!isRecord(value) || !isSha(value.sha) || typeof value.ref !== "string") throw invalidResponse();
|
|
1385
|
-
const repository = isRecord(value.repo) ? value.repo.full_name : null;
|
|
2765
|
+
if (!isRecord$2(value) || !isSha(value.sha) || typeof value.ref !== "string") throw invalidResponse();
|
|
2766
|
+
const repository = isRecord$2(value.repo) ? value.repo.full_name : null;
|
|
1386
2767
|
if (repository !== null && !validRepository(repository)) throw invalidResponse();
|
|
1387
2768
|
return {
|
|
1388
2769
|
sha: value.sha,
|
|
@@ -1392,8 +2773,8 @@ function readRevision(value) {
|
|
|
1392
2773
|
}
|
|
1393
2774
|
function readPullRequest(value) {
|
|
1394
2775
|
const summary = readSummary(value);
|
|
1395
|
-
if (!isRecord(value) || value.state !== "open" && value.state !== "closed" || !Number.isSafeInteger(value.changed_files) || Number(value.changed_files) < 0) throw invalidResponse();
|
|
1396
|
-
const author = isRecord(value.user) && typeof value.user.login === "string" ? value.user.login : void 0;
|
|
2776
|
+
if (!isRecord$2(value) || value.state !== "open" && value.state !== "closed" || !Number.isSafeInteger(value.changed_files) || Number(value.changed_files) < 0) throw invalidResponse();
|
|
2777
|
+
const author = isRecord$2(value.user) && typeof value.user.login === "string" ? value.user.login : void 0;
|
|
1397
2778
|
const description = typeof value.body_text === "string" ? value.body_text : typeof value.body === "string" ? value.body : "";
|
|
1398
2779
|
const state = value.merged_at ? "merged" : value.draft ? "draft" : value.state;
|
|
1399
2780
|
return _objectSpread2(_objectSpread2({}, summary), {}, {
|
|
@@ -1406,7 +2787,7 @@ function readPullRequest(value) {
|
|
|
1406
2787
|
});
|
|
1407
2788
|
}
|
|
1408
2789
|
function readFile(value) {
|
|
1409
|
-
if (!isRecord(value) || typeof value.filename !== "string" || typeof value.status !== "string") throw invalidResponse();
|
|
2790
|
+
if (!isRecord$2(value) || typeof value.filename !== "string" || typeof value.status !== "string") throw invalidResponse();
|
|
1410
2791
|
return {
|
|
1411
2792
|
path: value.filename,
|
|
1412
2793
|
previous: typeof value.previous_filename === "string" ? value.previous_filename : void 0,
|
|
@@ -1528,8 +2909,8 @@ function createGitHubPullRequests(repository, send = fetch) {
|
|
|
1528
2909
|
let base;
|
|
1529
2910
|
if (records.some((file) => file.path.split("/").pop() === "project.source.json" && file.status !== "added" && file.status !== "copied")) try {
|
|
1530
2911
|
const comparison = await json(repo + "/compare/" + pr.base.sha + "..." + pr.head.sha + "?per_page=1", signal);
|
|
1531
|
-
const merge = isRecord(comparison) ? comparison.merge_base_commit : null;
|
|
1532
|
-
if (isRecord(merge) && isSha(merge.sha)) base = merge.sha;
|
|
2912
|
+
const merge = isRecord$2(comparison) ? comparison.merge_base_commit : null;
|
|
2913
|
+
if (isRecord$2(merge) && isSha(merge.sha)) base = merge.sha;
|
|
1533
2914
|
} catch (error) {
|
|
1534
2915
|
if (signal.aborted) throw error;
|
|
1535
2916
|
}
|
|
@@ -1718,7 +3099,7 @@ function createScratchBlocksDiffLocator(resolveWorkspace) {
|
|
|
1718
3099
|
if (!workspace || location.signal.aborted) return false;
|
|
1719
3100
|
const block = workspace.getBlockById(location.id);
|
|
1720
3101
|
const expected = location.target.blocks[location.id];
|
|
1721
|
-
if (!block || isRecord(expected) && typeof expected.opcode === "string" && block.type !== expected.opcode) return false;
|
|
3102
|
+
if (!block || isRecord$2(expected) && typeof expected.opcode === "string" && block.type !== expected.opcode) return false;
|
|
1722
3103
|
const clear = markBlockLocation(block, location.parts);
|
|
1723
3104
|
if (!clear) return false;
|
|
1724
3105
|
try {
|
|
@@ -1741,14 +3122,14 @@ function scriptRoot({ target, id }) {
|
|
|
1741
3122
|
const visited = /* @__PURE__ */ new Set();
|
|
1742
3123
|
for (;;) {
|
|
1743
3124
|
const block = target.blocks[id];
|
|
1744
|
-
if (!isRecord(block) || typeof block.parent !== "string" || !Object.hasOwn(target.blocks, block.parent)) return id;
|
|
3125
|
+
if (!isRecord$2(block) || typeof block.parent !== "string" || !Object.hasOwn(target.blocks, block.parent)) return id;
|
|
1745
3126
|
if (visited.has(id)) throw new Error("Cyclic block parents.");
|
|
1746
3127
|
visited.add(id);
|
|
1747
3128
|
id = block.parent;
|
|
1748
3129
|
}
|
|
1749
3130
|
}
|
|
1750
3131
|
function safeMutation(value, root, depth = 0) {
|
|
1751
|
-
if (!isRecord(value) || depth > 32 || value.tagName !== (root ? "mutation" : "arg") || !Array.isArray(value.children)) return false;
|
|
3132
|
+
if (!isRecord$2(value) || depth > 32 || value.tagName !== (root ? "mutation" : "arg") || !Array.isArray(value.children)) return false;
|
|
1752
3133
|
return Object.entries(value).every(([key, item]) => key === "children" || /^[a-zA-Z][\w-]*$/.test(key) && [
|
|
1753
3134
|
"string",
|
|
1754
3135
|
"number",
|
|
@@ -1773,7 +3154,7 @@ function selectBlocks({ target, id }, root, withContext) {
|
|
|
1773
3154
|
] : [...data];
|
|
1774
3155
|
return;
|
|
1775
3156
|
}
|
|
1776
|
-
if (!isRecord(data) || typeof data.opcode !== "string") throw new Error("Invalid Scratch block.");
|
|
3157
|
+
if (!isRecord$2(data) || typeof data.opcode !== "string") throw new Error("Invalid Scratch block.");
|
|
1777
3158
|
const block = structuredClone(data);
|
|
1778
3159
|
if (block.mutation && !safeMutation(block.mutation, true)) throw new Error("Unsupported block mutation.");
|
|
1779
3160
|
block.x = 20;
|
|
@@ -1784,7 +3165,7 @@ function selectBlocks({ target, id }, root, withContext) {
|
|
|
1784
3165
|
if (!followNext) block.next = null;
|
|
1785
3166
|
selected[blockId] = block;
|
|
1786
3167
|
visiting.add(blockId);
|
|
1787
|
-
if (isRecord(block.inputs)) for (const [name, input] of Object.entries(block.inputs)) {
|
|
3168
|
+
if (isRecord$2(block.inputs)) for (const [name, input] of Object.entries(block.inputs)) {
|
|
1788
3169
|
if (!withContext && /^SUBSTACK\d*$/.test(name)) {
|
|
1789
3170
|
delete block.inputs[name];
|
|
1790
3171
|
continue;
|
|
@@ -2122,4 +3503,4 @@ function openProjectComparison(document, options) {
|
|
|
2122
3503
|
render(/* @__PURE__ */ React.createElement(ProjectComparisonDialog, _objectSpread2(_objectSpread2({}, options), {}, { onChoose: choose })), root);
|
|
2123
3504
|
});
|
|
2124
3505
|
}
|
|
2125
|
-
export { usePreviewSync as _, openProjectSourceTabs as a,
|
|
3506
|
+
export { assetFileName as A, CloseButton as B, subscribeReviewDraft as C, GitHubReviewError as D, reviewLabelsFor as E, createPreviewSync as F, usePreviewSync as I, currentCostume as L, projectPropertyLabel as M, compareProjectForReview as N, createGitHubReviewApi as O, BlockPreview as P, useAssetImage as R, sendReview as S, labelsFor as T, probeReviewRoutes as _, openProjectSourceTabs as a, reviewProblem as b, gitHubRepositoryFromOrigin as c, PullRequestReviewComposer as d, checkReview as f, planReview as g, forgetReviewSubmission as h, createScratchBlocksDiffLocator as i, organizeProjectChanges as j, isReviewThreadPath as k, ProjectSourceComparison as l, emptyReviewDraft as m, openProjectComparison as n, PullRequestError as o, discardReview as p, createScratchBlocksDiffRenderer as r, createGitHubPullRequests as s, ProjectComparisonDialog as t, ProjectSourceReview as u, readReviewDraft as v, writeReviewDraft as w, reviewRoute as x, reviewBody as y, useCostumeImage as z };
|