@kubohiroya/turbowarp-plus-editor-git 0.15.0 → 0.16.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.
@@ -13,6 +13,20 @@ export type LocalOperation = {
13
13
  kind: 'commit';
14
14
  message: string;
15
15
  author?: GitAuthor;
16
+ }
17
+ /**
18
+ * Creates `name` at HEAD, at the commit `at`, or at `origin/<name>` with `fromRemote`, and switches to it with
19
+ * `checkout`.
20
+ */
21
+ | {
22
+ kind: 'createBranch';
23
+ name: string;
24
+ fromRemote?: boolean;
25
+ at?: string;
26
+ checkout: boolean;
27
+ } | {
28
+ kind: 'switchBranch';
29
+ name: string;
16
30
  };
17
31
  /**
18
32
  * Stages, unstages or commits in the granted repository. Like pull, Git's writes are collected first and applied
@@ -42,6 +42,10 @@ export declare const messages: {
42
42
  outdated: string;
43
43
  unsupported: string;
44
44
  commitMessage: string;
45
+ switchUnsafe: string;
46
+ branchExists: string;
47
+ locked: string;
48
+ branchName: string;
45
49
  pullUnsafe: string;
46
50
  diverged: string;
47
51
  session: string;
@@ -60,6 +64,16 @@ export declare const messages: {
60
64
  saveFirst: string;
61
65
  author: string;
62
66
  nothingStaged: string;
67
+ switchTo: string;
68
+ currentBranch: string;
69
+ createBranch: string;
70
+ newBranch: string;
71
+ switchAfterCreate: string;
72
+ createFromRemote: string;
73
+ remoteBranches: string;
74
+ switched: string;
75
+ branchCreated: string;
76
+ projectMissing: string;
63
77
  pulledProject: string;
64
78
  confirmPush: string;
65
79
  details: string;
@@ -80,6 +94,18 @@ export declare const messages: {
80
94
  records: string;
81
95
  checkedAt: string;
82
96
  failed: string;
97
+ historyMore: string;
98
+ historyLoading: string;
99
+ historyFailed: string;
100
+ historyActions: string;
101
+ historyCreateHere: string;
102
+ historySwitch: string;
103
+ historyCreateFromRemote: string;
104
+ historyCopy: string;
105
+ historyCopied: string;
106
+ historyCopyFailed: string;
107
+ historyRefs: string;
108
+ cancel: string;
83
109
  };
84
110
  ja: {
85
111
  pullRequests: string;
@@ -124,6 +150,10 @@ export declare const messages: {
124
150
  outdated: string;
125
151
  unsupported: string;
126
152
  commitMessage: string;
153
+ switchUnsafe: string;
154
+ branchExists: string;
155
+ locked: string;
156
+ branchName: string;
127
157
  pullUnsafe: string;
128
158
  diverged: string;
129
159
  session: string;
@@ -142,6 +172,16 @@ export declare const messages: {
142
172
  saveFirst: string;
143
173
  author: string;
144
174
  nothingStaged: string;
175
+ switchTo: string;
176
+ currentBranch: string;
177
+ createBranch: string;
178
+ newBranch: string;
179
+ switchAfterCreate: string;
180
+ createFromRemote: string;
181
+ remoteBranches: string;
182
+ switched: string;
183
+ branchCreated: string;
184
+ projectMissing: string;
145
185
  pulledProject: string;
146
186
  confirmPush: string;
147
187
  details: string;
@@ -162,6 +202,18 @@ export declare const messages: {
162
202
  records: string;
163
203
  checkedAt: string;
164
204
  failed: string;
205
+ historyMore: string;
206
+ historyLoading: string;
207
+ historyFailed: string;
208
+ historyActions: string;
209
+ historyCreateHere: string;
210
+ historySwitch: string;
211
+ historyCreateFromRemote: string;
212
+ historyCopy: string;
213
+ historyCopied: string;
214
+ historyCopyFailed: string;
215
+ historyRefs: string;
216
+ cancel: string;
165
217
  };
166
218
  };
167
219
  export type Labels = typeof messages.en;
@@ -1569,17 +1569,22 @@ function useBlockLocation(comparison, locateBlock) {
1569
1569
  status: (side) => (location === null || location === void 0 ? void 0 : location.comparison) === comparison && location.locator === locateBlock && location.key === sideKey(side) ? location.status : void 0
1570
1570
  };
1571
1571
  }
1572
- function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset, location }) {
1573
- const [open, setOpen] = useState(false);
1572
+ /** What a script is called in a review: its hat block, or both hat blocks when the change replaced it. */
1573
+ function scriptReviewTitle(script, labels) {
1574
+ const beforeTitle = script.before[0] && scriptTitle(script.before[0], labels);
1575
+ const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
1576
+ return beforeTitle && afterTitle && beforeTitle !== afterTitle ? beforeTitle + " → " + afterTitle : afterTitle !== null && afterTitle !== void 0 ? afterTitle : beforeTitle;
1577
+ }
1578
+ function ScriptComparison({ script, labels, reviewed = false, setReviewed, renderBlock, loadAsset, location, expanded = false }) {
1579
+ const [open, setOpen] = useState(expanded);
1574
1580
  const { sync, toggle } = usePreviewSync(labels === ja ? "ja" : "en");
1575
1581
  const status = script.kind;
1576
1582
  const locale = labels === ja ? "ja" : "en";
1577
1583
  const summary = script.summary.map((item) => summaryPhrase(item, locale)).join(labels === ja ? "、" : ", ");
1578
- const beforeTitle = script.before[0] && scriptTitle(script.before[0], labels);
1579
- const afterTitle = script.after[0] && scriptTitle(script.after[0], labels);
1580
- const title = beforeTitle && afterTitle && beforeTitle !== afterTitle ? beforeTitle + " → " + afterTitle : afterTitle !== null && afterTitle !== void 0 ? afterTitle : beforeTitle;
1584
+ const title = scriptReviewTitle(script, labels);
1581
1585
  return /* @__PURE__ */ React.createElement("details", {
1582
1586
  className: "twgit-review-script",
1587
+ open: expanded || void 0,
1583
1588
  onToggle: (event) => setOpen(event.currentTarget.open)
1584
1589
  }, /* @__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, {
1585
1590
  key: side.id,
@@ -1781,7 +1786,20 @@ var scriptPrint = (script) => fingerprint([
1781
1786
  script.after.map(scriptBlocks)
1782
1787
  ]);
1783
1788
  var storageKey = (name) => `turbowarp-plus-git:reviewed:${name}`;
1784
- var readMarks = (name) => {
1789
+ var memoryMarks = /* @__PURE__ */ new Map();
1790
+ var markListeners = /* @__PURE__ */ new Map();
1791
+ var subscribeMarks = (name, listener) => {
1792
+ var _markListeners$get;
1793
+ const listeners = (_markListeners$get = markListeners.get(name)) !== null && _markListeners$get !== void 0 ? _markListeners$get : /* @__PURE__ */ new Set();
1794
+ markListeners.set(name, listeners.add(listener));
1795
+ return () => {
1796
+ listeners.delete(listener);
1797
+ if (!listeners.size) markListeners.delete(name);
1798
+ };
1799
+ };
1800
+ var readMarks = (name, remember = true) => {
1801
+ var _memoryMarks$get;
1802
+ if (!remember) return (_memoryMarks$get = memoryMarks.get(name)) !== null && _memoryMarks$get !== void 0 ? _memoryMarks$get : {};
1785
1803
  try {
1786
1804
  var _globalThis$localStor, _globalThis$localStor2;
1787
1805
  const value = JSON.parse((_globalThis$localStor = (_globalThis$localStor2 = globalThis.localStorage) === null || _globalThis$localStor2 === void 0 ? void 0 : _globalThis$localStor2.getItem(storageKey(name))) !== null && _globalThis$localStor !== void 0 ? _globalThis$localStor : "{}");
@@ -1790,12 +1808,18 @@ var readMarks = (name) => {
1790
1808
  return {};
1791
1809
  }
1792
1810
  };
1793
- var writeMarks = (name, marks) => {
1811
+ var writeMarks = (name, marks, remember = true) => {
1812
+ var _markListeners$get2;
1813
+ if (!remember) {
1814
+ if (Object.keys(marks).length) memoryMarks.set(name, marks);
1815
+ else memoryMarks.delete(name);
1816
+ }
1794
1817
  try {
1795
1818
  var _globalThis$localStor3, _globalThis$localStor4;
1796
- if (Object.keys(marks).length) (_globalThis$localStor3 = globalThis.localStorage) === null || _globalThis$localStor3 === void 0 || _globalThis$localStor3.setItem(storageKey(name), JSON.stringify(marks));
1797
- else (_globalThis$localStor4 = globalThis.localStorage) === null || _globalThis$localStor4 === void 0 || _globalThis$localStor4.removeItem(storageKey(name));
1819
+ if (remember && Object.keys(marks).length) (_globalThis$localStor3 = globalThis.localStorage) === null || _globalThis$localStor3 === void 0 || _globalThis$localStor3.setItem(storageKey(name), JSON.stringify(marks));
1820
+ else if (remember) (_globalThis$localStor4 = globalThis.localStorage) === null || _globalThis$localStor4 === void 0 || _globalThis$localStor4.removeItem(storageKey(name));
1798
1821
  } catch (_unused4) {}
1822
+ (_markListeners$get2 = markListeners.get(name)) === null || _markListeners$get2 === void 0 || _markListeners$get2.forEach((listener) => listener());
1799
1823
  };
1800
1824
  function ProjectSourceReview({ title, description, verification, files, renderBlock, locale = "en", rememberAs }) {
1801
1825
  const labels = locale.startsWith("ja") ? ja : en;
@@ -1826,20 +1850,24 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
1826
1850
  return prints;
1827
1851
  }, [entries]);
1828
1852
  const [checked, setChecked] = useState();
1853
+ const [, refresh] = useState(0);
1854
+ useEffect(() => rememberAs ? subscribeMarks(rememberAs, () => {
1855
+ setChecked(void 0);
1856
+ refresh((count) => count + 1);
1857
+ }) : void 0, [rememberAs]);
1829
1858
  const marks = (checked === null || checked === void 0 ? void 0 : checked.entries) === entries ? checked.marks : rememberAs ? readMarks(rememberAs) : {};
1830
1859
  const reviewed = new Set([...scriptPrints].filter(([key, print]) => marks[key] === print).map(([key]) => key));
1831
- const setReviewed = (key, value) => setChecked((previous) => {
1832
- const current = (previous === null || previous === void 0 ? void 0 : previous.entries) === entries ? previous.marks : rememberAs ? readMarks(rememberAs) : {};
1860
+ const setReviewed = (key, value) => {
1833
1861
  const next = {};
1834
- for (const [name, print] of scriptPrints) if (current[name] === print) next[name] = print;
1862
+ for (const [name, print] of scriptPrints) if (marks[name] === print) next[name] = print;
1835
1863
  if (value) next[key] = scriptPrints.get(key);
1836
1864
  else delete next[key];
1837
1865
  if (rememberAs) writeMarks(rememberAs, next);
1838
- return {
1866
+ setChecked({
1839
1867
  entries,
1840
1868
  marks: next
1841
- };
1842
- });
1869
+ });
1870
+ };
1843
1871
  const targets = entries.reduce((total, entry) => {
1844
1872
  var _entry$comparison$tar, _entry$comparison;
1845
1873
  return total + ((_entry$comparison$tar = (_entry$comparison = entry.comparison) === null || _entry$comparison === void 0 ? void 0 : _entry$comparison.targets.length) !== null && _entry$comparison$tar !== void 0 ? _entry$comparison$tar : 0);
@@ -1871,6 +1899,62 @@ function ProjectSourceReview({ title, description, verification, files, renderBl
1871
1899
  }
1872
1900
  }))));
1873
1901
  }
1902
+ /** The words of one tab of a project comparison opened as tabs. */
1903
+ function reviewTabWords(locale) {
1904
+ const labels = locale.startsWith("ja") ? ja : en;
1905
+ return {
1906
+ script: (script) => `${scriptReviewTitle(script, labels)} · ${labels[script.kind]}`,
1907
+ target: (target) => target.isStage ? labels.stage : target.name,
1908
+ targetDetails: labels.properties,
1909
+ project: labels.comparison,
1910
+ noChanges: labels.noChanges
1911
+ };
1912
+ }
1913
+ function useReviewMark(place, script) {
1914
+ const [, refresh] = useState(0);
1915
+ useEffect(() => place && subscribeMarks(place.name, () => refresh((count) => count + 1)), [place === null || place === void 0 ? void 0 : place.name]);
1916
+ if (!place) return {};
1917
+ const key = reviewKey(place.file, place.target, script);
1918
+ const print = scriptPrint(script);
1919
+ return {
1920
+ reviewed: readMarks(place.name, place.remember)[key] === print,
1921
+ setReviewed(value) {
1922
+ const marks = _objectSpread2({}, readMarks(place.name, place.remember));
1923
+ if (value) marks[key] = print;
1924
+ else delete marks[key];
1925
+ writeMarks(place.name, marks, place.remember);
1926
+ }
1927
+ };
1928
+ }
1929
+ /** One script of a comparison, open, for a tab of its own. */
1930
+ function ScriptReviewTab({ comparison, script, renderBlock, locale = "en", loadAsset, locateBlock, mark }) {
1931
+ const labels = locale.startsWith("ja") ? ja : en;
1932
+ const location = useBlockLocation(comparison, locateBlock);
1933
+ const { reviewed, setReviewed } = useReviewMark(mark, script);
1934
+ return /* @__PURE__ */ React.createElement("section", { className: "twgit-block-diff twgit-review twgit-review-tab" }, /* @__PURE__ */ React.createElement(ScriptComparison, {
1935
+ script,
1936
+ labels,
1937
+ renderBlock,
1938
+ loadAsset,
1939
+ location,
1940
+ expanded: true,
1941
+ reviewed,
1942
+ setReviewed
1943
+ }), /* @__PURE__ */ React.createElement(Legend, { labels }));
1944
+ }
1945
+ /**
1946
+ * What changed in a stage or sprite outside its code, or in the project as a whole when no target is given,
1947
+ * for a tab of its own.
1948
+ */
1949
+ function TargetReviewTab({ comparison, target, locale = "en", loadAsset }) {
1950
+ const labels = locale.startsWith("ja") ? ja : en;
1951
+ if (!target) return /* @__PURE__ */ React.createElement("section", { className: "twgit-block-diff twgit-review twgit-review-tab" }, comparison.properties.length > 0 ? /* @__PURE__ */ React.createElement("p", null, labels.properties, " · ", propertyNames(comparison.properties, labels)) : /* @__PURE__ */ React.createElement("p", null, labels.noChanges));
1952
+ return /* @__PURE__ */ React.createElement("section", { className: "twgit-block-diff twgit-review twgit-review-tab" }, /* @__PURE__ */ React.createElement("h5", null, /* @__PURE__ */ React.createElement(TargetThumbnails, {
1953
+ target,
1954
+ labels,
1955
+ loadAsset
1956
+ }), target.isStage ? labels.stage : target.name), 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.length > 0 && /* @__PURE__ */ React.createElement("p", null, labels.scriptCount(target.scripts.length)));
1957
+ }
1874
1958
  // src/github-pull-requests.ts
1875
1959
  init_objectSpread2();
1876
1960
  var PullRequestError = class extends Error {
@@ -2116,6 +2200,119 @@ function createGitHubPullRequests(repository, send = fetch) {
2116
2200
  }
2117
2201
  };
2118
2202
  }
2203
+ // src/project-source-tabs.tsx
2204
+ var providerId = "turbowarp-plus-git";
2205
+ var statusColor = {
2206
+ added: "green",
2207
+ removed: "red",
2208
+ updated: "blue"
2209
+ };
2210
+ /** Renders its content once the tab is first shown, so a comparison with many scripts draws only what is looked at. */
2211
+ function WhenShown({ children }) {
2212
+ const element = useRef(null);
2213
+ const [shown, setShown] = useState(false);
2214
+ useEffect(() => {
2215
+ var _element$current;
2216
+ const view = (_element$current = element.current) === null || _element$current === void 0 ? void 0 : _element$current.ownerDocument.defaultView;
2217
+ if (shown || !element.current) return;
2218
+ if (!(view === null || view === void 0 ? void 0 : view.IntersectionObserver)) {
2219
+ setShown(true);
2220
+ return;
2221
+ }
2222
+ const observer = new view.IntersectionObserver((entries) => {
2223
+ if (entries.some((entry) => entry.isIntersecting)) setShown(true);
2224
+ });
2225
+ observer.observe(element.current);
2226
+ return () => observer.disconnect();
2227
+ }, [shown]);
2228
+ return /* @__PURE__ */ React.createElement("div", {
2229
+ ref: element,
2230
+ className: "twgit-review-tab-body"
2231
+ }, shown && children);
2232
+ }
2233
+ /**
2234
+ * Opens a comparison of project.source.json files as tabs of the host's workspace: a tab group for each changed
2235
+ * stage or sprite, holding a tab for each changed script and one for its changes outside the code.
2236
+ * Returns how many tabs were asked for, or `0` when the host has no workspace to take them.
2237
+ */
2238
+ function openProjectSourceTabs(openPanel, { key, files, renderBlock, locale = "en", rememberAs }) {
2239
+ if (!openPanel) return 0;
2240
+ const words = reviewTabWords(locale);
2241
+ const area = `${providerId}:diff:${key}`;
2242
+ const requests = [];
2243
+ const tab = (documentId, title, content, tabGroup) => ({
2244
+ reference: {
2245
+ providerId,
2246
+ documentId: `diff:${key}:${documentId}`
2247
+ },
2248
+ filename: title,
2249
+ title,
2250
+ transient: true,
2251
+ area,
2252
+ tabGroup,
2253
+ inactive: requests.length > 0,
2254
+ mount(element) {
2255
+ element.classList.add("twgit-panel-tab", "twgit-review-tab-slot");
2256
+ render(/* @__PURE__ */ React.createElement(WhenShown, null, content()), element);
2257
+ return {
2258
+ requestClose: () => {},
2259
+ dispose: () => {
2260
+ unmountComponentAtNode(element);
2261
+ }
2262
+ };
2263
+ }
2264
+ });
2265
+ for (const file of files) {
2266
+ const comparison = compareProjectForReview(file.before, file.after);
2267
+ const prefix = files.length > 1 ? file.path.split("/").slice(0, -1).join("/") : "";
2268
+ const labelOf = (target) => prefix ? `${prefix}: ${words.target(target)}` : words.target(target);
2269
+ if (comparison.properties.length || !comparison.targets.length) {
2270
+ const title = prefix ? `${prefix}: ${words.project}` : words.project;
2271
+ requests.push(tab(`${file.path}:project`, title, () => /* @__PURE__ */ React.createElement(TargetReviewTab, {
2272
+ comparison,
2273
+ locale
2274
+ })));
2275
+ }
2276
+ for (const target of comparison.targets) {
2277
+ const targetKey = JSON.stringify([
2278
+ file.path,
2279
+ target.isStage,
2280
+ target.name
2281
+ ]);
2282
+ const tabGroup = {
2283
+ id: `${area}:${targetKey}`,
2284
+ label: labelOf(target),
2285
+ color: statusColor[target.status]
2286
+ };
2287
+ if (target.status !== "updated" || target.properties.length || !target.scripts.length) requests.push(tab(`${targetKey}:details`, words.targetDetails, () => /* @__PURE__ */ React.createElement(TargetReviewTab, {
2288
+ comparison,
2289
+ target,
2290
+ locale,
2291
+ loadAsset: file.loadAsset
2292
+ }), tabGroup));
2293
+ for (const script of target.scripts) requests.push(tab(`${targetKey}:${script.id}`, words.script(script), () => /* @__PURE__ */ React.createElement(ScriptReviewTab, {
2294
+ comparison,
2295
+ script,
2296
+ renderBlock,
2297
+ locale,
2298
+ loadAsset: file.loadAsset,
2299
+ locateBlock: file.locateBlock,
2300
+ mark: {
2301
+ name: rememberAs !== null && rememberAs !== void 0 ? rememberAs : area,
2302
+ remember: !!rememberAs,
2303
+ file: file.path,
2304
+ target
2305
+ }
2306
+ }), tabGroup));
2307
+ }
2308
+ }
2309
+ let opened = 0;
2310
+ for (const request of requests) {
2311
+ if (!openPanel(request)) break;
2312
+ opened++;
2313
+ }
2314
+ return opened;
2315
+ }
2119
2316
  // src/block-diff-location.ts
2120
2317
  function markBlockLocation(block, parts) {
2121
2318
  const root = block.getSvgRoot();
@@ -2546,4 +2743,4 @@ function openProjectComparison(document, options) {
2546
2743
  render(/* @__PURE__ */ React.createElement(ProjectComparisonDialog, _objectSpread2(_objectSpread2({}, options), {}, { onChoose: choose })), root);
2547
2744
  });
2548
2745
  }
2549
- export { readProjectSourceDiff as C, parseProjectSource as S, currentCostume as _, PullRequestError as a, compareProjectBlocks as b, ProjectSourceComparison as c, organizeProjectChanges as d, projectPropertyLabel as f, usePreviewSync as g, createPreviewSync as h, createScratchBlocksDiffLocator as i, ProjectSourceReview as l, BlockPreview as m, openProjectComparison as n, createGitHubPullRequests as o, compareProjectForReview as p, createScratchBlocksDiffRenderer as r, gitHubRepositoryFromOrigin as s, ProjectComparisonDialog as t, assetFileName as u, useAssetImage as v, createProjectSourceDiff as x, useCostumeImage as y };
2746
+ export { parseProjectSource as C, createProjectSourceDiff as S, usePreviewSync as _, openProjectSourceTabs as a, useCostumeImage as b, gitHubRepositoryFromOrigin as c, assetFileName as d, organizeProjectChanges as f, createPreviewSync as g, BlockPreview as h, createScratchBlocksDiffLocator as i, ProjectSourceComparison as l, compareProjectForReview as m, openProjectComparison as n, PullRequestError as o, projectPropertyLabel as p, createScratchBlocksDiffRenderer as r, createGitHubPullRequests as s, ProjectComparisonDialog as t, ProjectSourceReview as u, currentCostume as v, readProjectSourceDiff as w, compareProjectBlocks as x, useAssetImage as y };
@@ -5,6 +5,8 @@ import './project-source-diff.css';
5
5
  export { createGitHubPullRequests, gitHubRepositoryFromOrigin } from './github-pull-requests.js';
6
6
  export { ProjectSourceComparison, ProjectSourceReview } from './project-source-review.js';
7
7
  export { ProjectComparisonDialog, openProjectComparison } from './project-comparison.js';
8
+ export { openProjectSourceTabs } from './project-source-tabs.js';
9
+ export type { ProjectSourceTabsFile, ProjectSourceTabsOptions } from './project-source-tabs.js';
8
10
  export type { ProjectComparisonAction, ProjectComparisonDialogProps, ProjectComparisonOptions, ProjectComparisonSection } from './project-comparison.js';
9
11
  export type { ProjectSourceComparisonProps, ProjectSourceReviewFile, ProjectSourceReviewProps } from './project-source-review.js';
10
12
  export { createScratchBlocksDiffLocator } from './block-diff-location.js';
@@ -1,4 +1,4 @@
1
- import { C as readProjectSourceDiff, _ as currentCostume, b as compareProjectBlocks, c as ProjectSourceComparison, d as organizeProjectChanges, f as projectPropertyLabel, g as usePreviewSync, h as createPreviewSync, i as createScratchBlocksDiffLocator, l as ProjectSourceReview, m as BlockPreview, n as openProjectComparison, o as createGitHubPullRequests, p as compareProjectForReview, r as createScratchBlocksDiffRenderer, s as gitHubRepositoryFromOrigin, t as ProjectComparisonDialog, u as assetFileName, x as createProjectSourceDiff } from "./project-comparison-LAyP_beL.js";
1
+ import { S as createProjectSourceDiff, _ as usePreviewSync, a as openProjectSourceTabs, c as gitHubRepositoryFromOrigin, d as assetFileName, f as organizeProjectChanges, g as createPreviewSync, h as BlockPreview, i as createScratchBlocksDiffLocator, l as ProjectSourceComparison, m as compareProjectForReview, n as openProjectComparison, p as projectPropertyLabel, r as createScratchBlocksDiffRenderer, s as createGitHubPullRequests, t as ProjectComparisonDialog, u as ProjectSourceReview, v as currentCostume, w as readProjectSourceDiff, x as compareProjectBlocks } from "./project-comparison-MU0qD2wo.js";
2
2
  import { n as init_objectSpread2, t as _objectSpread2 } from "./objectSpread2-Dkd68nJH.js";
3
3
  import React, { useEffect, useMemo, useRef, useState } from "react";
4
4
  // src/project-source-diff.tsx
@@ -169,4 +169,4 @@ function ProjectSourceBlockDiff({ patch, beforeSource, renderBlock, locateBlock,
169
169
  })
170
170
  }, labels.more));
171
171
  }
172
- export { ProjectComparisonDialog, ProjectSourceBlockDiff, ProjectSourceComparison, ProjectSourceReview, assetFileName, compareProjectForReview, createGitHubPullRequests, createPreviewSync, createProjectSourceDiff, createScratchBlocksDiffLocator, createScratchBlocksDiffRenderer, currentCostume, gitHubRepositoryFromOrigin, openProjectComparison, organizeProjectChanges, projectPropertyLabel };
172
+ export { ProjectComparisonDialog, ProjectSourceBlockDiff, ProjectSourceComparison, ProjectSourceReview, assetFileName, compareProjectForReview, createGitHubPullRequests, createPreviewSync, createProjectSourceDiff, createScratchBlocksDiffLocator, createScratchBlocksDiffRenderer, currentCostume, gitHubRepositoryFromOrigin, openProjectComparison, openProjectSourceTabs, organizeProjectChanges, projectPropertyLabel };
@@ -1,7 +1,10 @@
1
1
  import React from 'react';
2
+ import { compareProjectForReview } from './project-source-review-model.js';
3
+ import type { ScriptReview, TargetReview } from './project-source-review-model.js';
2
4
  import type { ProjectBlockRenderer } from './scratch-block-diff-renderer.js';
3
5
  import type { ProjectBlockLocator } from './block-diff-location.js';
4
6
  import type { ProjectAssetLoader } from './project-assets.js';
7
+ type Comparison = ReturnType<typeof compareProjectForReview>;
5
8
  export type ProjectSourceReviewFile = {
6
9
  path: string;
7
10
  summary?: string;
@@ -51,3 +54,43 @@ export interface ProjectSourceComparisonProps {
51
54
  /** Compares two versions of a project.source.json by stage, sprite and code, as the pull request review does. */
52
55
  export declare function ProjectSourceComparison({ before, after, renderBlock, locale, fallback, loadAsset, locateBlock, target }: ProjectSourceComparisonProps): React.JSX.Element;
53
56
  export declare function ProjectSourceReview({ title, description, verification, files, renderBlock, locale, rememberAs }: ProjectSourceReviewProps): React.JSX.Element;
57
+ /** The words of one tab of a project comparison opened as tabs. */
58
+ export declare function reviewTabWords(locale: string): {
59
+ script: (script: ScriptReview) => string;
60
+ target: (target: TargetReview) => string;
61
+ targetDetails: string;
62
+ project: string;
63
+ noChanges: string;
64
+ };
65
+ /**
66
+ * Where a tab keeps its reviewed mark: under `name` with the review of the same name, in the browser when
67
+ * `remember` is set, or else while the page is open.
68
+ */
69
+ export interface ReviewMarkPlace {
70
+ name: string;
71
+ remember: boolean;
72
+ file: string;
73
+ target: TargetReview;
74
+ }
75
+ /** One script of a comparison, open, for a tab of its own. */
76
+ export declare function ScriptReviewTab({ comparison, script, renderBlock, locale, loadAsset, locateBlock, mark }: {
77
+ comparison: Comparison;
78
+ script: ScriptReview;
79
+ renderBlock: ProjectBlockRenderer;
80
+ locale?: string;
81
+ loadAsset?: ProjectAssetLoader;
82
+ locateBlock?: ProjectBlockLocator;
83
+ /** Offers the reviewed mark, shared with the review panel and other tabs that use the same place. */
84
+ mark?: ReviewMarkPlace;
85
+ }): React.JSX.Element;
86
+ /**
87
+ * What changed in a stage or sprite outside its code, or in the project as a whole when no target is given,
88
+ * for a tab of its own.
89
+ */
90
+ export declare function TargetReviewTab({ comparison, target, locale, loadAsset }: {
91
+ comparison: Comparison;
92
+ target?: TargetReview;
93
+ locale?: string;
94
+ loadAsset?: ProjectAssetLoader;
95
+ }): React.JSX.Element;
96
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { ProjectSource } from './project-source-patch.js';
2
+ import type { ProjectBlockRenderer } from './scratch-block-diff-renderer.js';
3
+ import type { ProjectAssetLoader } from './project-assets.js';
4
+ import type { ProjectBlockLocator } from './block-diff-location.js';
5
+ import type { GitPanelRequest } from './index.js';
6
+ /** One project.source.json of a comparison, read into both versions. */
7
+ export interface ProjectSourceTabsFile {
8
+ path: string;
9
+ before: ProjectSource;
10
+ after: ProjectSource;
11
+ loadAsset?: ProjectAssetLoader;
12
+ /** Offers "Highlight in Editor" on the after version, which the caller has matched with the open project. */
13
+ locateBlock?: ProjectBlockLocator;
14
+ }
15
+ export interface ProjectSourceTabsOptions {
16
+ /**
17
+ * Names this comparison, such as `owner/repo#42@<head>` or `<commit>:project.source.json`. Its tabs share one
18
+ * split, and opening the same comparison again brings its tabs forward instead of adding them twice.
19
+ */
20
+ key: string;
21
+ files: readonly ProjectSourceTabsFile[];
22
+ renderBlock: ProjectBlockRenderer;
23
+ locale?: string;
24
+ /**
25
+ * Shares the reviewed marks of the script tabs with the review of this name, such as `owner/repo#42`, and keeps
26
+ * them in the browser. Without it, the marks are kept for this comparison while the page is open.
27
+ */
28
+ rememberAs?: string;
29
+ }
30
+ /**
31
+ * Opens a comparison of project.source.json files as tabs of the host's workspace: a tab group for each changed
32
+ * stage or sprite, holding a tab for each changed script and one for its changes outside the code.
33
+ * Returns how many tabs were asked for, or `0` when the host has no workspace to take them.
34
+ */
35
+ export declare function openProjectSourceTabs(openPanel: ((request: GitPanelRequest) => boolean) | undefined, { key, files, renderBlock, locale, rememberAs }: ProjectSourceTabsOptions): number;
@@ -2,10 +2,15 @@ import React from 'react';
2
2
  import type { ProjectBlockRenderer } from './scratch-block-diff-renderer.js';
3
3
  import type { Repository } from './repository.js';
4
4
  import type { ProjectBlockLocator } from './block-diff-location.js';
5
- export declare function PullRequestReviewPanel({ repository, locale, renderBlock, locateFor }: {
5
+ import type { ProjectSourceTabsFile } from './project-source-tabs.js';
6
+ type OpenTabs = (key: string, files: readonly ProjectSourceTabsFile[], rememberAs?: string) => number;
7
+ export declare function PullRequestReviewPanel({ repository, locale, renderBlock, locateFor, openTabs }: {
6
8
  repository: Repository;
7
9
  locale: string;
8
10
  renderBlock: ProjectBlockRenderer;
11
+ /** Opens a pull request's project changes as tabs of the host's layout. */
12
+ openTabs?: OpenTabs;
9
13
  /** Returns a locator for a project file whose PR head matches the open project, or `undefined`. */
10
14
  locateFor?(path: string, headSha: string): ProjectBlockLocator | undefined;
11
15
  }): React.JSX.Element;
16
+ export {};